[git] GnuPG - branch, master, updated. gnupg-2.1.0beta3-216-g7777e68

by Werner Koch cvs at cvs.gnupg.org
Wed May 22 10:41:44 CEST 2013


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU Privacy Guard".

The branch, master has been updated
       via  7777e68d0482c942f527e91c04adbcfb40bc8bef (commit)
       via  cb6a64bb78296c8e9f72df0c482ff847e89a1541 (commit)
       via  0f0e0559f9b160824f10dc17b389268cdb53aea4 (commit)
       via  f2d8a14e1b12534eba69d595a62c78f92331e11b (commit)
      from  88e24341e57c96e31a25e92e09d67989e64cc1c1 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 7777e68d0482c942f527e91c04adbcfb40bc8bef
Author: Werner Koch <wk at gnupg.org>
Date:   Wed May 22 09:50:12 2013 +0100

    Implement unattended OpenPGP secret key import.
    
    * agent/command.c (cmd_import_key): Add option --unattended.
    * agent/cvt-openpgp.c (convert_transfer_key): New.
    (do_unprotect): Factor some code out to ...
    (prepare_unprotect): new function.
    (convert_from_openpgp): Factor all code out to ...
    (convert_from_openpgp_main): this.  Add arg 'passphrase'.  Implement
    openpgp-native protection modes.
    (convert_from_openpgp_native): New.
    * agent/t-protect.c (convert_from_openpgp_native): New dummy fucntion
    * agent/protect-tool.c (convert_from_openpgp_native): Ditto.
    * agent/protect.c (agent_unprotect): Add arg CTRL.  Adjust all
    callers.  Support openpgp-native protection.
    * g10/call-agent.c (agent_import_key): Add arg 'unattended'.
    * g10/import.c (transfer_secret_keys): Use unattended in batch mode.
    --
    
    With the gpg-agent taking care of the secret keys, the user needs to
    migrate existing keys from secring.gpg to the agent.  This and also
    the standard import of secret keys required the user to unprotect the
    secret keys first, so that gpg-agent was able to re-protected them
    using its own scheme.  With many secret keys this is quite some
    usability hurdle.  In particular if a passphrase is not instantly
    available.
    
    To make this migration smoother, this patch implements an unattended
    key import/migration which delays the conversion to the gpg-agent
    format until the key is actually used.  For example:
    
       gpg2 --batch --import mysecretkey.gpg
    
    works without any user interaction due to the use of --batch.  Now if
    a key is used (e.g. "gpg2 -su USERID_FROM_MYSECRETKEY foo"), gpg-agent
    has to ask for the passphrase anyway, converts the key from the
    openpgp format to the internal format, signs, re-encrypts the key and
    tries to store it in the gpg-agent format to the disk.  The next time,
    the internal format of the key is used.
    
    This patch has only been tested with the old demo keys, more tests
    with other protection formats and no protection are needed.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/README b/README
index 03da25e..c64a14e 100644
--- a/README
+++ b/README
@@ -5,7 +5,8 @@
    THIS IS A DEVELOPMENT VERSION AND NOT INTENDED FOR REGULAR USE.
 
       Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
-     2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+                2006, 2007, 2008, 2009, 2010, 2011, 2012,
+                2013 Free Software Foundation, Inc.
 
 
 INTRODUCTION
@@ -32,21 +33,22 @@ BUILD INSTRUCTIONS
 
 GnuPG 2.1 depends on the following packages:
 
+  npth         (ftp://ftp.gnupg.org/gcrypt/npth/)
   libgpg-error (ftp://ftp.gnupg.org/gcrypt/libgpg-error/)
   libgcrypt    (ftp://ftp.gnupg.org/gcrypt/libgcrypt/)
   libksba      (ftp://ftp.gnupg.org/gcrypt/libksba/)
   libassuan    (ftp://ftp.gnupg.org/gcrypt/libassuan/)
 
-You also need the Pinentry package for most function of GnuPG; however
-it is not a build requirement.  Pinentry is available at
-ftp://ftp.gnupg.org/gcrypt/pinentry/ .
-
 You should get the latest versions of course, the GnuPG configure
 script complains if a version is not sufficient.
 
+You also need the Pinentry package for most functions of GnuPG;
+however it is not a build requirement.  Pinentry is available at
+ftp://ftp.gnupg.org/gcrypt/pinentry/ .
+
 After building and installing the above packages in the order as given
-above, you may now continue with GnuPG installation (you may also just
-try to build GnuPG to see whether your already installed versions are
+above, you may continue with GnuPG installation (you may also just try
+to build GnuPG to see whether your already installed versions are
 sufficient).
 
 As with all packages, you just have to do
@@ -62,7 +64,8 @@ S/MIME and smartcards.  Note that there is no binary gpg but a gpg2 so
 that this package won't conflict with a GnuPG 1.4 installation.  gpg2
 behaves just like gpg.
 
-In case of problem please ask on gnupg-users at gnupg.org for advise.
+In case of problem please ask on gnupg-users at gnupg.org mailing list
+for advise.
 
 Note that the PKITS tests are always skipped unless you copy the PKITS
 test data file into the tests/pkits directory.  There is no need to
@@ -79,21 +82,24 @@ to view the default directories used by GnuPG.
 MIGRATION FROM 1.4 or 2.0 to 2.1
 ================================
 
-The major change in 2.1 is that gpg-agent now takes care of the
-OpenPGP secret keys (those managed by GPG).  The former secring.gpg
-will not be used anymore.  Newly generated keys are generated and
-stored in the agent's key store (~/.gnupg/private-keys-v1.d/).  To
-migrate your existing keys to the agent you should run this command
+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
 
-  gpg2 --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 agent will you ask for the passphrase of each key.  You may use
-the Cancel button of the Pinentry to skip importing this key.  If you
-want to stop the import process and you use one of the latest
-pinentries, you should close the pinentry window instead of hitting
-the cancel button.  Secret keys already imported are skipped by the
-import 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.
 
 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
@@ -182,8 +188,13 @@ authors directly as we are busy working on improvements and bug fixes.
 The English and German mailing lists are watched by the authors and we
 try to answer questions when time allows us to do so.
 
-Commercial grade support for GnuPG is available; please see
-http://www.gnupg.org/service.html .
+Commercial grade support for GnuPG is available; for a listing of
+offers see http://www.gnupg.org/service.html .  The driving force
+behind the development of GnuPG is the company of its principal
+author, Werner Koch.  Maintenance and improvement of GnuPG and related
+software takes up most of their resources.  To allow him to continue
+his work he asks to either purchase a support contract, engage them
+for custom enhancements, or to donate money.  See http://g10code.com .
 
 
   This file is Free Software; as a special exception the authors gives
diff --git a/agent/agent.h b/agent/agent.h
index 2fd0b8b..7445061 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -374,7 +374,8 @@ unsigned char get_standard_s2k_count_rfc4880 (void);
 int agent_protect (const unsigned char *plainkey, const char *passphrase,
                    unsigned char **result, size_t *resultlen,
 		   unsigned long s2k_count);
-int agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
+int agent_unprotect (ctrl_t ctrl,
+                     const unsigned char *protectedkey, const char *passphrase,
                      gnupg_isotime_t protected_at,
                      unsigned char **result, size_t *resultlen);
 int agent_private_key_type (const unsigned char *privatekey);
diff --git a/agent/command.c b/agent/command.c
index e57c69d..0364868 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1,6 +1,7 @@
 /* command.c - gpg-agent command handler
  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010,
  *               2011  Free Software Foundation, Inc.
+ * Copyright (C) 2013 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -1807,18 +1808,21 @@ cmd_keywrap_key (assuan_context_t ctx, char *line)
 
 

 static const char hlp_import_key[] =
-  "IMPORT_KEY [<cache_nonce>]\n"
+  "IMPORT_KEY [--unattended] [<cache_nonce>]\n"
   "\n"
   "Import a secret key into the key store.  The key is expected to be\n"
   "encrypted using the current session's key wrapping key (cf. command\n"
   "KEYWRAP_KEY) using the AESWRAP-128 algorithm.  This function takes\n"
   "no arguments but uses the inquiry \"KEYDATA\" to ask for the actual\n"
-  "key data.  The unwrapped key must be a canonical S-expression.";
+  "key data.  The unwrapped key must be a canonical S-expression.  The\n"
+  "option --unattended tries to import the key as-is without any\n"
+  "re-encryption";
 static gpg_error_t
 cmd_import_key (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
   gpg_error_t err;
+  int opt_unattended;
   unsigned char *wrappedkey = NULL;
   size_t wrappedkeylen;
   gcry_cipher_hd_t cipherhd = NULL;
@@ -1838,6 +1842,9 @@ cmd_import_key (assuan_context_t ctx, char *line)
       goto leave;
     }
 
+  opt_unattended = has_option (line, "--unattended");
+  line = skip_options (line);
+
   p = line;
   for (p=line; *p && *p != ' ' && *p != '\t'; p++)
     ;
@@ -1921,7 +1928,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
       key = NULL;
       err = convert_from_openpgp (ctrl, openpgp_sexp, grip,
                                   ctrl->server_local->keydesc, cache_nonce,
-                                  &key, &passphrase);
+                                  &key, opt_unattended? NULL : &passphrase);
       if (err)
         goto leave;
       realkeylen = gcry_sexp_canon_len (key, 0, NULL, &err);
@@ -1929,6 +1936,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
         goto leave; /* Invalid canonical encoded S-expression.  */
       if (passphrase)
         {
+          assert (!opt_unattended);
           if (!cache_nonce)
             {
               char buf[12];
@@ -1941,6 +1949,12 @@ cmd_import_key (assuan_context_t ctx, char *line)
             assuan_write_status (ctx, "CACHE_NONCE", cache_nonce);
         }
     }
+  else if (opt_unattended)
+    {
+      err = set_error (GPG_ERR_ASS_PARAMETER,
+                       "\"--unattended\" may only be used with OpenPGP keys");
+      goto leave;
+    }
   else
     {
       if (!agent_key_available (grip))
@@ -1957,7 +1971,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
   if (passphrase)
     {
       err = agent_protect (key, passphrase, &finalkey, &finalkeylen,
-	      ctrl->s2k_count);
+                           ctrl->s2k_count);
       if (!err)
         err = agent_write_private_key (grip, finalkey, finalkeylen, 0);
     }
diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c
index ec0fd0a..205b953 100644
--- a/agent/cvt-openpgp.c
+++ b/agent/cvt-openpgp.c
@@ -1,6 +1,7 @@
 /* cvt-openpgp.c - Convert an OpenPGP key to our internal format.
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006, 2009,
  *               2010 Free Software Foundation, Inc.
+ * Copyright (C) 2013 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -160,6 +161,72 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey)
 }
 
 
+/* Convert a secret key given as algorithm id, an array of key
+   parameters, and an S-expression of the original OpenPGP transfer
+   key into our s-expression based format.  This is a variant of
+   convert_secret_key which is used for the openpgp-native protection
+   mode.  Note that PUBKEY_ALGO has an gcrypt algorithm number. */
+static gpg_error_t
+convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
+                      gcry_sexp_t transfer_key)
+{
+  gpg_error_t err;
+  gcry_sexp_t s_skey = NULL;
+
+  *r_key = NULL;
+
+  switch (pubkey_algo)
+    {
+    case GCRY_PK_DSA:
+      err = gcry_sexp_build
+        (&s_skey, NULL,
+         "(protected-private-key(dsa(p%m)(q%m)(g%m)(y%m)"
+         "(protected openpgp-native%S)))",
+         skey[0], skey[1], skey[2], skey[3], transfer_key);
+      break;
+
+    case GCRY_PK_ELG:
+    case GCRY_PK_ELG_E:
+      err = gcry_sexp_build
+        (&s_skey, NULL,
+         "(protected-private-key(elg(p%m)(g%m)(y%m)"
+         "(protected openpgp-native%S)))",
+         skey[0], skey[1], skey[2], transfer_key);
+      break;
+
+
+    case GCRY_PK_RSA:
+    case GCRY_PK_RSA_E:
+    case GCRY_PK_RSA_S:
+      err = gcry_sexp_build
+        (&s_skey, NULL,
+         "(protected-private-key(rsa(n%m)(e%m)",
+         "(protected openpgp-native%S)))",
+         skey[0], skey[1], transfer_key );
+      break;
+
+    case GCRY_PK_ECDSA:
+    case GCRY_PK_ECDH:
+      /* Although our code would work with "ecc" we explicitly use
+         "ecdh" or "ecdsa" to implicitly set the key capabilities.  */
+      err = gcry_sexp_build
+        (&s_skey, NULL,
+         "(protected-private-key(%s(p%m)(a%m)(b%m)(g%m)(n%m)(q%m)"
+         "(protected openpgp-native%S)))",
+         pubkey_algo == GCRY_PK_ECDSA?"ecdsa":"ecdh",
+         skey[0], skey[1], skey[2], skey[3], skey[4], skey[5], transfer_key);
+      break;
+
+    default:
+      err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+      break;
+    }
+
+  if (!err)
+    *r_key = s_skey;
+  return err;
+}
+
 
 /* Hash the passphrase and set the key. */
 static gpg_error_t
@@ -202,30 +269,19 @@ checksum (const unsigned char *p, unsigned int n)
 }
 
 
-/* Note that this function modified SKEY.  SKEYSIZE is the allocated
-   size of the array including the NULL item; this is used for a
-   bounds check.  On success a converted key is stored at R_KEY.  */
+/* Helper for do_unprotect.  PUBKEY_ALOGO is the gcrypt algo number.
+   On success R_NPKEY and R_NSKEY receive the number or parameters for
+   the algorithm PUBKEY_ALGO and R_SKEYLEN the used length of
+   SKEY.  */
 static int
-do_unprotect (const char *passphrase,
-              int pkt_version, int pubkey_algo, int is_protected,
-              gcry_mpi_t *skey, size_t skeysize,
-              int protect_algo, void *protect_iv, size_t protect_ivlen,
-              int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count,
-              u16 desired_csum, gcry_sexp_t *r_key)
+prepare_unprotect (int pubkey_algo, gcry_mpi_t *skey, size_t skeysize,
+                   int s2k_mode,
+                   unsigned int *r_npkey, unsigned int *r_nskey,
+                   unsigned int *r_skeylen)
 {
   gpg_error_t err;
   size_t npkey, nskey, skeylen;
-  gcry_cipher_hd_t cipher_hd = NULL;
-  u16 actual_csum;
-  size_t nbytes;
   int i;
-  gcry_mpi_t tmpmpi;
-
-  *r_key = NULL;
-
- /* Unfortunately, the OpenPGP PK algorithm numbers need to be
-    re-mapped for Libgcrypt.    */
-  pubkey_algo = map_pk_openpgp_to_gcry (pubkey_algo);
 
   /* Count the actual number of MPIs is in the array and set the
      remainder to NULL for easier processing later on.  */
@@ -264,6 +320,54 @@ do_unprotect (const char *passphrase,
   if (nskey+1 >= skeysize)
     return gpg_error (GPG_ERR_BUFFER_TOO_SHORT);
 
+  /* Check that the public key parameters are all available and not
+     encrypted.  */
+  for (i=0; i < npkey; i++)
+    {
+      if (!skey[i] || gcry_mpi_get_flag (skey[i], GCRYMPI_FLAG_OPAQUE))
+        return gpg_error (GPG_ERR_BAD_SECKEY);
+    }
+
+  if (r_npkey)
+    *r_npkey = npkey;
+  if (r_nskey)
+    *r_nskey = nskey;
+  if (r_skeylen)
+    *r_skeylen = skeylen;
+  return 0;
+}
+
+
+/* Note that this function modifies SKEY.  SKEYSIZE is the allocated
+   size of the array including the NULL item; this is used for a
+   bounds check.  On success a converted key is stored at R_KEY.  */
+static int
+do_unprotect (const char *passphrase,
+              int pkt_version, int pubkey_algo, int is_protected,
+              gcry_mpi_t *skey, size_t skeysize,
+              int protect_algo, void *protect_iv, size_t protect_ivlen,
+              int s2k_mode, int s2k_algo, byte *s2k_salt, u32 s2k_count,
+              u16 desired_csum, gcry_sexp_t *r_key)
+{
+  gpg_error_t err;
+  unsigned int npkey, nskey, skeylen;
+  gcry_cipher_hd_t cipher_hd = NULL;
+  u16 actual_csum;
+  size_t nbytes;
+  int i;
+  gcry_mpi_t tmpmpi;
+
+  *r_key = NULL;
+
+  /* Unfortunately, the OpenPGP PK algorithm numbers need to be
+     re-mapped for Libgcrypt.    */
+  pubkey_algo = map_pk_openpgp_to_gcry (pubkey_algo);
+
+  err = prepare_unprotect (pubkey_algo, skey, skeysize, s2k_mode,
+                           &npkey, &nskey, &skeylen);
+  if (err)
+    return err;
+
   /* Check whether SKEY is at all protected.  If it is not protected
      merely verify the checksum.  */
   if (!is_protected)
@@ -512,7 +616,7 @@ do_unprotect (const char *passphrase,
 }
 
 
-/* Callback function to try the unprotection from the passpharse query
+/* Callback function to try the unprotection from the passphrase query
    code.  */
 static int
 try_do_unprotect_cb (struct pin_entry_info_s *pi)
@@ -536,24 +640,19 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi)
 }
 
 
-/* Convert an OpenPGP transfer key into our internal format.  Before
-   asking for a passphrase we check whether the key already exists in
-   our key storage.  S_PGP is the OpenPGP key in transfer format.  If
-   CACHE_NONCE is given the passphrase will be looked up in the cache.
-   On success R_KEY will receive a canonical encoded S-expression with
-   the unprotected key in our internal format; the caller needs to
-   release that memory.  The passphrase used to decrypt the OpenPGP
-   key will be returned at R_PASSPHRASE; the caller must release this
-   passphrase.  The keygrip will be stored at the 20 byte buffer
-   pointed to by GRIP.  On error NULL is stored at all return
-   arguments.  */
-gpg_error_t
-convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
-                      unsigned char *grip, const char *prompt,
-                      const char *cache_nonce,
-                      unsigned char **r_key, char **r_passphrase)
+/* See convert_from_openpgp for the core of the description.  This
+   function adds an optional PASSPHRASE argument and uses this to
+   silently decrypt the key; CACHE_NONCE and R_PASSPHRASE must both be
+   NULL in this mode.  */
+static gpg_error_t
+convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
+                           unsigned char *grip, const char *prompt,
+                           const char *cache_nonce, const char *passphrase,
+                           unsigned char **r_key, char **r_passphrase)
 {
   gpg_error_t err;
+  int unattended;
+  int from_native;
   gcry_sexp_t top_list;
   gcry_sexp_t list = NULL;
   const char *value;
@@ -573,12 +672,13 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
   gcry_mpi_t skey[10];  /* We support up to 9 parameters.  */
   u16 desired_csum;
   int skeyidx = 0;
-  gcry_sexp_t s_skey;
-  struct pin_entry_info_s *pi;
-  struct try_do_unprotect_arg_s pi_arg;
+  gcry_sexp_t s_skey = NULL;
 
   *r_key = NULL;
-  *r_passphrase = NULL;
+  if (r_passphrase)
+    *r_passphrase = NULL;
+  unattended = !r_passphrase;
+  from_native = (!cache_nonce && passphrase && !r_passphrase);
 
   top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0);
   if (!top_list)
@@ -607,6 +707,7 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
     is_protected = 0;
   else
     goto bad_seckey;
+
   if (is_protected)
     {
       string = gcry_sexp_nth_string (list, 2);
@@ -755,64 +856,89 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
   if (err)
     goto leave;
 
-  if (!agent_key_available (grip))
+  if (!from_native && !agent_key_available (grip))
     {
       err = gpg_error (GPG_ERR_EEXIST);
       goto leave;
     }
 
-  pi = xtrycalloc_secure (1, sizeof (*pi) + 100);
-  if (!pi)
-    return gpg_error_from_syserror ();
-  pi->max_length = 100;
-  pi->min_digits = 0;  /* We want a real passphrase.  */
-  pi->max_digits = 16;
-  pi->max_tries = 3;
-  pi->check_cb = try_do_unprotect_cb;
-  pi->check_cb_arg = &pi_arg;
-  pi_arg.is_v4 = is_v4;
-  pi_arg.is_protected = is_protected;
-  pi_arg.pubkey_algo = pubkey_algo;
-  pi_arg.protect_algo = protect_algo;
-  pi_arg.iv = iv;
-  pi_arg.ivlen = ivlen;
-  pi_arg.s2k_mode = s2k_mode;
-  pi_arg.s2k_algo = s2k_algo;
-  pi_arg.s2k_salt = s2k_salt;
-  pi_arg.s2k_count = s2k_count;
-  pi_arg.desired_csum = desired_csum;
-  pi_arg.skey = skey;
-  pi_arg.skeysize = DIM (skey);
-  pi_arg.skeyidx = skeyidx;
-  pi_arg.r_key = &s_skey;
-
-  err = gpg_error (GPG_ERR_BAD_PASSPHRASE);
-  if (cache_nonce)
+  if (unattended && !from_native)
     {
-      char *cache_value;
+      int pubkey_g_algo = map_pk_openpgp_to_gcry (pubkey_algo);
 
-      cache_value = agent_get_cache (cache_nonce, CACHE_MODE_NONCE);
-      if (cache_value)
-        {
-          if (strlen (cache_value) < pi->max_length)
-            strcpy (pi->pin, cache_value);
-          xfree (cache_value);
-        }
-      if (*pi->pin)
-        err = try_do_unprotect_cb (pi);
+      err = prepare_unprotect (pubkey_g_algo, skey, DIM(skey), s2k_mode,
+                               NULL, NULL, NULL);
+      if (err)
+        goto leave;
+
+      err = convert_transfer_key (&s_skey, pubkey_g_algo, skey, s_pgp);
+      if (err)
+        goto leave;
     }
-  if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE)
-    err = agent_askpin (ctrl, prompt, NULL, NULL, pi);
-  skeyidx = pi_arg.skeyidx;
-  if (!err)
+  else
     {
-      *r_passphrase = xtrystrdup (pi->pin);
-      if (!*r_passphrase)
-        err = gpg_error_from_syserror ();
+      struct pin_entry_info_s *pi;
+      struct try_do_unprotect_arg_s pi_arg;
+
+      pi = xtrycalloc_secure (1, sizeof (*pi) + 100);
+      if (!pi)
+        return gpg_error_from_syserror ();
+      pi->max_length = 100;
+      pi->min_digits = 0;  /* We want a real passphrase.  */
+      pi->max_digits = 16;
+      pi->max_tries = 3;
+      pi->check_cb = try_do_unprotect_cb;
+      pi->check_cb_arg = &pi_arg;
+      pi_arg.is_v4 = is_v4;
+      pi_arg.is_protected = is_protected;
+      pi_arg.pubkey_algo = pubkey_algo;
+      pi_arg.protect_algo = protect_algo;
+      pi_arg.iv = iv;
+      pi_arg.ivlen = ivlen;
+      pi_arg.s2k_mode = s2k_mode;
+      pi_arg.s2k_algo = s2k_algo;
+      pi_arg.s2k_salt = s2k_salt;
+      pi_arg.s2k_count = s2k_count;
+      pi_arg.desired_csum = desired_csum;
+      pi_arg.skey = skey;
+      pi_arg.skeysize = DIM (skey);
+      pi_arg.skeyidx = skeyidx;
+      pi_arg.r_key = &s_skey;
+
+      err = gpg_error (GPG_ERR_BAD_PASSPHRASE);
+      if (cache_nonce)
+        {
+          char *cache_value;
+
+          cache_value = agent_get_cache (cache_nonce, CACHE_MODE_NONCE);
+          if (cache_value)
+            {
+              if (strlen (cache_value) < pi->max_length)
+                strcpy (pi->pin, cache_value);
+              xfree (cache_value);
+            }
+          if (*pi->pin)
+            err = try_do_unprotect_cb (pi);
+        }
+      else if (from_native)
+        {
+          if (strlen (passphrase) < pi->max_length)
+            strcpy (pi->pin, passphrase);
+          err = try_do_unprotect_cb (pi);
+        }
+      if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE && !from_native)
+        err = agent_askpin (ctrl, prompt, NULL, NULL, pi);
+      skeyidx = pi_arg.skeyidx;
+      if (!err && r_passphrase)
+        {
+          *r_passphrase = xtrystrdup (pi->pin);
+          if (!*r_passphrase)
+            err = gpg_error_from_syserror ();
+        }
+      xfree (pi);
+      if (err)
+        goto leave;
     }
-  xfree (pi);
-  if (err)
-    goto leave;
 
   /* Save some memory and get rid of the SKEY array now.  */
   for (idx=0; idx < skeyidx; idx++)
@@ -820,16 +946,16 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
   skeyidx = 0;
 
   /* Note that the padding is not required - we use it only because
-     that function allows us to created the result in secure memory.  */
+     that function allows us to create the result in secure memory.  */
   err = make_canon_sexp_pad (s_skey, 1, r_key, NULL);
-  gcry_sexp_release (s_skey);
 
  leave:
+  gcry_sexp_release (s_skey);
   gcry_sexp_release (list);
   gcry_sexp_release (top_list);
   for (idx=0; idx < skeyidx; idx++)
     gcry_mpi_release (skey[idx]);
-  if (err)
+  if (err && r_passphrase)
     {
       xfree (*r_passphrase);
       *r_passphrase = NULL;
@@ -847,6 +973,63 @@ convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
 }
 
 
+/* Convert an OpenPGP transfer key into our internal format.  Before
+   asking for a passphrase we check whether the key already exists in
+   our key storage.  S_PGP is the OpenPGP key in transfer format.  If
+   CACHE_NONCE is given the passphrase will be looked up in the cache.
+   On success R_KEY will receive a canonical encoded S-expression with
+   the unprotected key in our internal format; the caller needs to
+   release that memory.  The passphrase used to decrypt the OpenPGP
+   key will be returned at R_PASSPHRASE; the caller must release this
+   passphrase.  If R_PASSPHRASE is NULL the unattended conversion mode
+   will be used which uses the openpgp-native protection format for
+   the key.  The keygrip will be stored at the 20 byte buffer pointed
+   to by GRIP.  On error NULL is stored at all return arguments.  */
+gpg_error_t
+convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
+                      unsigned char *grip, const char *prompt,
+                      const char *cache_nonce,
+                      unsigned char **r_key, char **r_passphrase)
+{
+  return convert_from_openpgp_main (ctrl, s_pgp, grip, prompt,
+                                    cache_nonce, NULL,
+                                    r_key, r_passphrase);
+}
+
+/* This function is called by agent_unprotect to re-protect an
+   openpgp-native protected private-key into the standard private-key
+   protection format.  */
+gpg_error_t
+convert_from_openpgp_native (ctrl_t ctrl,
+                             gcry_sexp_t s_pgp, const char *passphrase,
+                             unsigned char **r_key)
+{
+  gpg_error_t err;
+  unsigned char grip[20];
+
+  if (!passphrase)
+    return gpg_error (GPG_ERR_INTERNAL);
+
+  err = convert_from_openpgp_main (ctrl, s_pgp, grip, NULL,
+                                   NULL, passphrase,
+                                   r_key, NULL);
+
+  /* On success try to re-write the key.  */
+  if (!err)
+    {
+      unsigned char *protectedkey = NULL;
+      size_t protectedkeylen;
+
+      if (!agent_protect (*r_key, passphrase, &protectedkey, &protectedkeylen,
+                          ctrl->s2k_count))
+        agent_write_private_key (grip, protectedkey, protectedkeylen, 1);
+      xfree (protectedkey);
+    }
+
+  return err;
+}
+
+
 

 static gpg_error_t
 key_from_sexp (gcry_sexp_t sexp, const char *elems, gcry_mpi_t *array)
diff --git a/agent/cvt-openpgp.h b/agent/cvt-openpgp.h
index 3c48d03..d27a776 100644
--- a/agent/cvt-openpgp.h
+++ b/agent/cvt-openpgp.h
@@ -23,6 +23,10 @@ gpg_error_t convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
                                   unsigned char *grip, const char *prompt,
                                   const char *cache_nonce,
                                   unsigned char **r_key, char **r_passphrase);
+gpg_error_t convert_from_openpgp_native (ctrl_t ctrl,
+                                         gcry_sexp_t s_pgp,
+                                         const char *passphrase,
+                                         unsigned char **r_key);
 
 gpg_error_t convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key,
                                 const char *passphrase,
diff --git a/agent/findkey.c b/agent/findkey.c
index ebdcc03..d11f088 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -66,6 +66,9 @@ agent_write_private_key (const unsigned char *grip,
 
   fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
 
+  /* FIXME: Write to a temp file first so that write failures during
+     key updates won't lead to a key loss.  */
+
   if (!force && !access (fname, F_OK))
     {
       log_error ("secret key file '%s' already exists\n", fname);
@@ -119,7 +122,7 @@ try_unprotect_cb (struct pin_entry_info_s *pi)
   assert (!arg->unprotected_key);
 
   arg->change_required = 0;
-  err = agent_unprotect (arg->protected_key, pi->pin, protected_at,
+  err = agent_unprotect (arg->ctrl, arg->protected_key, pi->pin, protected_at,
                          &arg->unprotected_key, &dummy);
   if (err)
     return err;
@@ -325,7 +328,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
       pw = agent_get_cache (cache_nonce, CACHE_MODE_NONCE);
       if (pw)
         {
-          rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
+          rc = agent_unprotect (ctrl, *keybuf, pw, NULL, &result, &resultlen);
           if (!rc)
             {
               if (r_passphrase)
@@ -350,7 +353,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
       pw = agent_get_cache (hexgrip, cache_mode);
       if (pw)
         {
-          rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
+          rc = agent_unprotect (ctrl, *keybuf, pw, NULL, &result, &resultlen);
           if (!rc)
             {
               if (r_passphrase)
diff --git a/agent/keyformat.txt b/agent/keyformat.txt
index 7ba6af2..3f95dae 100644
--- a/agent/keyformat.txt
+++ b/agent/keyformat.txt
@@ -84,56 +84,94 @@ encrypted_octet_string.  The result of the decryption process is a
 list of the secret key parameters.  The protected-at expression is
 optional; the isotimestamp is 15 bytes long (e.g. "19610711T172000").
 
-The only available protection mode for now is
+The currently defined protection modes are:
 
-  openpgp-s2k3-sha1-aes-cbc
+1. openpgp-s2k3-sha1-aes-cbc
 
-which describes an algorithm using using AES in CBC mode for
-encryption, SHA-1 for integrity protection and the String to Key
-algorithm 3 from OpenPGP (rfc2440).
+  This describes an algorithm using using AES in CBC mode for
+  encryption, SHA-1 for integrity protection and the String to Key
+  algorithm 3 from OpenPGP (rfc2440).
 
-Example:
-
-(protected openpgp-s2k3-sha1-aes-cbc
-  ((sha1 16byte_salt no_of_iterations) 16byte_iv)
-  encrypted_octet_string
-)
+  Example:
 
-The encrypted_octet string should yield this S-Exp (in canonical
-representation) after decryption:
+  (protected openpgp-s2k3-sha1-aes-cbc
+    ((sha1 16byte_salt no_of_iterations) 16byte_iv)
+    encrypted_octet_string
+  )
 
-(
- (
-  (d #046129F..[some bytes not shown]..81#)
-  (p #00e861b..[some bytes not shown]..f1#)
-  (q #00f7a7c..[some bytes not shown]..61#)
-  (u #304559a..[some bytes not shown]..9b#)
- )
- (hash sha1 #...[hashvalue]...#)
-)
+  The encrypted_octet string should yield this S-Exp (in canonical
+  representation) after decryption:
 
-For padding reasons, random bytes are appended to this list - they can
-easily be stripped by looking for the end of the list.
-
-The hash is calculated on the concatenation of the public key and
-secret key parameter lists: i.e it is required to hash the
-concatenation of these 6 canonical encoded lists for RSA, including
-the parenthesis, the algorithm keyword and (if used) the protected-at
-list.
-
-(rsa
- (n #00e0ce9..[some bytes not shown]..51#)
- (e #010001#)
- (d #046129F..[some bytes not shown]..81#)
- (p #00e861b..[some bytes not shown]..f1#)
- (q #00f7a7c..[some bytes not shown]..61#)
- (u #304559a..[some bytes not shown]..9b#)
- (protected-at "18950523T000000")
-)
+  (
+   (
+    (d #046129F..[some bytes not shown]..81#)
+    (p #00e861b..[some bytes not shown]..f1#)
+    (q #00f7a7c..[some bytes not shown]..61#)
+    (u #304559a..[some bytes not shown]..9b#)
+   )
+   (hash sha1 #...[hashvalue]...#)
+  )
+
+  For padding reasons, random bytes are appended to this list - they can
+  easily be stripped by looking for the end of the list.
+
+  The hash is calculated on the concatenation of the public key and
+  secret key parameter lists: i.e it is required to hash the
+  concatenation of these 6 canonical encoded lists for RSA, including
+  the parenthesis, the algorithm keyword and (if used) the protected-at
+  list.
+
+  (rsa
+   (n #00e0ce9..[some bytes not shown]..51#)
+   (e #010001#)
+   (d #046129F..[some bytes not shown]..81#)
+   (p #00e861b..[some bytes not shown]..f1#)
+   (q #00f7a7c..[some bytes not shown]..61#)
+   (u #304559a..[some bytes not shown]..9b#)
+   (protected-at "18950523T000000")
+  )
+
+  After decryption the hash must be recalculated and compared against
+  the stored one - If they don't match the integrity of the key is not
+  given.
+
+2. openpgp-native
+
+  This is a wrapper around the OpenPGP Private Key Transport format
+  which resembles the standard OpenPGP format and allows the use of an
+  existing key without re-encrypting to the default protection format.
+
+  Example:
+
+  (protected openpgp-native
+    (openpgp-private-key
+     (version V)
+     (algo PUBKEYALGO)
+     (skey _ P1 _ P2 _ P3 ... e PN)
+     (csum n)
+     (protection PROTTYPE PROTALGO IV S2KMODE S2KHASH S2KSALT S2KCOUNT)))
+
+  Note that the public key paramaters in SKEY are duplicated and
+  should be identical to their copies in the standard parameter
+  elements.  Here is an example of an entire protected private key
+  using this format:
+
+  (protected-private-key
+   (rsa
+    (n #00e0ce9..[some bytes not shown]..51#)
+    (e #010001#)
+    (protected openpgp-native
+     (openpgp-private-key
+      (version 4)
+      (algo rsa)
+      (skey _ #00e0ce9..[some bytes not shown]..51#
+            _ #010001#
+            e #.........................#)
+      (protection sha1 aes #aabbccddeeff00112233445566778899#
+                  3 sha1 #2596f93e85f41e53# 3:190))))
+   (uri http://foo.bar x-foo:whatever_you_want)
+   (comment whatever))
 
-After decryption the hash must be recalculated and compared against
-the stored one - If they don't match the integrity of the key is not
-given.
 
 
 Shadowed Private Key Format
@@ -184,7 +222,7 @@ This format is used to transfer keys between gpg and gpg-agent.
   the secrect key parameters are encrypted if the "protection" list is
   given.  To make this more explicit each parameter is preceded by a
   flag "_" for cleartext or "e" for encrypted text.
-* CSUM is the depreciated 16 bit checksum as defined by OpenPGP.  This
+* CSUM is the deprecated 16 bit checksum as defined by OpenPGP.  This
   is an optional element.
 * If PROTTYPE is "sha1" the new style SHA1 checksum is used if it is "sum"
   the old 16 bit checksum (above) is used and if it is "none" no
diff --git a/agent/protect-tool.c b/agent/protect-tool.c
index d59f5f0..faa0e24 100644
--- a/agent/protect-tool.c
+++ b/agent/protect-tool.c
@@ -372,7 +372,7 @@ read_and_unprotect (const char *fname)
   if (!key)
     return;
 
-  rc = agent_unprotect (key, (pw=get_passphrase (1)),
+  rc = agent_unprotect (NULL, key, (pw=get_passphrase (1)),
                         protected_at, &result, &resultlen);
   release_passphrase (pw);
   xfree (key);
@@ -728,3 +728,15 @@ release_passphrase (char *pw)
       xfree (pw);
     }
 }
+
+
+/* Stub function.  */
+gpg_error_t
+convert_from_openpgp_native (gcry_sexp_t s_pgp, const char *passphrase,
+                             unsigned char **r_key)
+{
+  (void)s_pgp;
+  (void)passphrase;
+  (void)r_key;
+  return gpg_error (GPG_ERR_BUG);
+}
diff --git a/agent/protect.c b/agent/protect.c
index 3e2cbb9..cb2c098 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -1,6 +1,7 @@
 /* protect.c - Un/Protect a secret key
  * Copyright (C) 1998, 1999, 2000, 2001, 2002,
  *               2003, 2007, 2009, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2013 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -604,6 +605,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   return 0;
 }
 
+
 

 /* Do the actual decryption and check the return list for consistency.  */
 static int
@@ -832,9 +834,10 @@ merge_lists (const unsigned char *protectedkey,
 
 /* Unprotect the key encoded in canonical format.  We assume a valid
    S-Exp here.  If a protected-at item is available, its value will
-   be stored at protocted_at unless this is NULL.  */
+   be stored at protected_at unless this is NULL.  */
 int
-agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
+agent_unprotect (ctrl_t ctrl,
+                 const unsigned char *protectedkey, const char *passphrase,
                  gnupg_isotime_t protected_at,
                  unsigned char **result, size_t *resultlen)
 {
@@ -938,7 +941,30 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
   if (!n)
     return gpg_error (GPG_ERR_INV_SEXP);
   if (!smatch (&s, n, "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc"))
-    return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
+    {
+      if (smatch (&s, n, "openpgp-native"))
+        {
+          gcry_sexp_t s_prot_begin;
+
+          rc = gcry_sexp_sscan (&s_prot_begin, NULL,
+                                prot_begin,
+                                gcry_sexp_canon_len (prot_begin, 0,NULL,NULL));
+          if (rc)
+            return rc;
+
+          rc = convert_from_openpgp_native (ctrl,
+                                            s_prot_begin, passphrase, &final);
+          gcry_sexp_release (s_prot_begin);
+          if (!rc)
+            {
+              *result = final;
+              *resultlen = gcry_sexp_canon_len (final, 0, NULL, NULL);
+            }
+          return rc;
+        }
+      else
+        return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
+    }
   if (*s != '(' || s[1] != '(')
     return gpg_error (GPG_ERR_INV_SEXP);
   s += 2;
diff --git a/agent/t-protect.c b/agent/t-protect.c
index 02b614a..9096cb2 100644
--- a/agent/t-protect.c
+++ b/agent/t-protect.c
@@ -337,3 +337,14 @@ main (int argc, char **argv)
 
   return 0;
 }
+
+/* Stub function.  */
+gpg_error_t
+convert_from_openpgp_native (gcry_sexp_t s_pgp, const char *passphrase,
+                             unsigned char **r_key)
+{
+  (void)s_pgp;
+  (void)passphrase;
+  (void)r_key;
+  return gpg_error (GPG_ERR_BUG);
+}
diff --git a/g10/call-agent.c b/g10/call-agent.c
index cb965e9..17290ec 100644
--- a/g10/call-agent.c
+++ b/g10/call-agent.c
@@ -1998,7 +1998,7 @@ inq_import_key_parms (void *opaque, const char *line)
 /* Call the agent to import a key into the agent.  */
 gpg_error_t
 agent_import_key (ctrl_t ctrl, const char *desc, char **cache_nonce_addr,
-                  const void *key, size_t keylen)
+                  const void *key, size_t keylen, int unattended)
 {
   gpg_error_t err;
   struct import_key_parm_s parm;
@@ -2028,7 +2028,8 @@ agent_import_key (ctrl_t ctrl, const char *desc, char **cache_nonce_addr,
   parm.key    = key;
   parm.keylen = keylen;
 
-  snprintf (line, sizeof line, "IMPORT_KEY%s%s",
+  snprintf (line, sizeof line, "IMPORT_KEY%s%s%s",
+            unattended? " --unattended":"",
             cache_nonce_addr && *cache_nonce_addr? " ":"",
             cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"");
   cn_parm.cache_nonce_addr = cache_nonce_addr;
diff --git a/g10/call-agent.h b/g10/call-agent.h
index ab1d41a..cce8304 100644
--- a/g10/call-agent.h
+++ b/g10/call-agent.h
@@ -177,7 +177,7 @@ gpg_error_t agent_keywrap_key (ctrl_t ctrl, int forexport,
 /* Send a key to the agent.  */
 gpg_error_t agent_import_key (ctrl_t ctrl, const char *desc,
                               char **cache_nonce_addr,
-                              const void *key, size_t keylen);
+                              const void *key, size_t keylen, int unattended);
 
 /* Receive a key from the agent.  */
 gpg_error_t agent_export_key (ctrl_t ctrl, const char *keygrip,
diff --git a/g10/import.c b/g10/import.c
index a57b32e..3846c21 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -1432,7 +1432,7 @@ transfer_secret_keys (ctrl_t ctrl, struct stats_s *stats, kbnode_t sec_keyblock)
       {
         char *desc = gpg_format_keydesc (pk, 1, 1);
         err = agent_import_key (ctrl, desc, &cache_nonce,
-                                wrappedkey, wrappedkeylen);
+                                wrappedkey, wrappedkeylen, opt.batch);
         xfree (desc);
       }
       if (!err)

commit cb6a64bb78296c8e9f72df0c482ff847e89a1541
Author: Werner Koch <wk at gnupg.org>
Date:   Tue May 21 14:00:00 2013 +0100

    New debug functions log_printcanon and log_printsexp.
    
    * common/sexputil.c (sexp_to_string, canon_sexp_to_string): New.
    (log_printcanon, log_printsexp): New.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/common/logging.c b/common/logging.c
index cdfd659..73b0dbe 100644
--- a/common/logging.c
+++ b/common/logging.c
@@ -857,6 +857,19 @@ log_printhex (const char *text, const void *buffer, size_t length)
 }
 
 
+/*
+void
+log_printcanon () {}
+is found in sexputils.c
+*/
+
+/*
+void
+log_printsexp () {}
+is found in sexputils.c
+*/
+
+
 void
 log_clock (const char *string)
 {
diff --git a/common/sexputil.c b/common/sexputil.c
index 1c70337..e18756a 100644
--- a/common/sexputil.c
+++ b/common/sexputil.c
@@ -1,5 +1,6 @@
 /* sexputil.c - Utility functions for S-expressions.
  * Copyright (C) 2005, 2007, 2009 Free Software Foundation, Inc.
+ * Copyright (C) 2013 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -46,6 +47,91 @@
 #include "sexp-parse.h"
 
 
+/* Return a malloced string with the S-expression CANON in advanced
+   format.  Returns NULL on error.  */
+static char *
+sexp_to_string (gcry_sexp_t sexp)
+{
+  size_t n;
+  char *result;
+
+  if (!sexp)
+    return NULL;
+  n = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
+  if (!n)
+    return NULL;
+  result = xtrymalloc (n);
+  if (!result)
+    return NULL;
+  n = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, n);
+  if (!n)
+    BUG ();
+
+  return result;
+}
+
+
+/* Return a malloced string with the S-expression CANON in advanced
+   format.  Returns NULL on error.  */
+char *
+canon_sexp_to_string (const unsigned char *canon, size_t canonlen)
+{
+  size_t n;
+  gcry_sexp_t sexp;
+  char *result;
+
+  n = gcry_sexp_canon_len (canon, canonlen, NULL, NULL);
+  if (!n)
+    return NULL;
+  if (gcry_sexp_sscan (&sexp, NULL, canon, n))
+    return NULL;
+  result = sexp_to_string (sexp);
+  gcry_sexp_release (sexp);
+  return result;
+}
+
+
+/* Print the canonical encoded S-expression in SEXP in advanced
+   format.  SEXPLEN may be passed as 0 is SEXP is known to be valid.
+   With TEXT of NULL print just the raw S-expression, with TEXT just
+   an empty string, print a trailing linefeed, otherwise print an
+   entire debug line. */
+void
+log_printcanon (const char *text, const unsigned char *sexp, size_t sexplen)
+{
+  if (text && *text)
+    log_debug ("%s ", text);
+  if (sexp)
+    {
+      char *buf = canon_sexp_to_string (sexp, sexplen);
+      log_printf ("%s", buf? buf : "[invalid S-expression]");
+      xfree (buf);
+    }
+  if (text)
+    log_printf ("\n");
+}
+
+
+/* Print the gcryp S-expression in SEXP in advanced format.  With TEXT
+   of NULL print just the raw S-expression, with TEXT just an empty
+   string, print a trailing linefeed, otherwise print an entire debug
+   line. */
+void
+log_printsexp (const char *text, gcry_sexp_t sexp)
+{
+  if (text && *text)
+    log_debug ("%s ", text);
+  if (sexp)
+    {
+      char *buf = sexp_to_string (sexp);
+      log_printf ("%s", buf? buf : "[invalid S-expression]");
+      xfree (buf);
+    }
+  if (text)
+    log_printf ("\n");
+}
+
+
 /* Helper function to create a canonical encoded S-expression from a
    Libgcrypt S-expression object.  The function returns 0 on success
    and the malloced canonical S-expression is stored at R_BUFFER and
diff --git a/common/util.h b/common/util.h
index 73ba84e..13b702c 100644
--- a/common/util.h
+++ b/common/util.h
@@ -167,6 +167,11 @@ gpg_error_t b64dec_finish (struct b64state *state);
 
 
 /*-- sexputil.c */
+char *canon_sexp_to_string (const unsigned char *canon, size_t canonlen);
+void log_printcanon (const char *text,
+                     const unsigned char *sexp, size_t sexplen);
+void log_printsexp (const char *text, gcry_sexp_t sexp);
+
 gpg_error_t make_canon_sexp (gcry_sexp_t sexp,
                              unsigned char **r_buffer, size_t *r_buflen);
 gpg_error_t make_canon_sexp_pad (gcry_sexp_t sexp, int secure,

commit 0f0e0559f9b160824f10dc17b389268cdb53aea4
Author: Werner Koch <wk at gnupg.org>
Date:   Tue May 21 13:00:16 2013 +0100

    agent: Fix length detection of canonical formatted openpgp keys.
    
    * agent/command.c (cmd_import_key): Pass 0 instead of KEYLEN to
    gcry_sexp_canon_len.
    --
    
    We used to pass KEYLEN to the gcry_sexp_canon_len for no good reason:
    convert_from_openpgp is guaranteed to return a valid canonical
    S-expression and KEYLEN would thus act only as an upper limit.  This
    is not a problem because usually the original input key is longer than
    the returned unprotected key.  A future patch may change this
    assertion and thus we better fix this bug now.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/agent/command.c b/agent/command.c
index 823b233..e57c69d 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1924,7 +1924,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
                                   &key, &passphrase);
       if (err)
         goto leave;
-      realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err);
+      realkeylen = gcry_sexp_canon_len (key, 0, NULL, &err);
       if (!realkeylen)
         goto leave; /* Invalid canonical encoded S-expression.  */
       if (passphrase)

commit f2d8a14e1b12534eba69d595a62c78f92331e11b
Author: Werner Koch <wk at gnupg.org>
Date:   Tue May 21 12:10:00 2013 +0100

    agent: New option --disable-check-own-socket.
    
    * agent/gpg-agent.c (oDisableCheckOwnSocket): New.
    (disable_check_own_socket): New.
    (parse_rereadable_options): Set new option.
    (check_own_socket): Implement new option.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index 4690114..ff129f9 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -112,6 +112,7 @@ enum cmd_and_opt_values
   oKeepDISPLAY,
   oSSHSupport,
   oDisableScdaemon,
+  oDisableCheckOwnSocket,
   oWriteEnvFile
 };
 
@@ -148,6 +149,7 @@ static ARGPARSE_OPTS opts[] = {
   { oScdaemonProgram, "scdaemon-program", 2 ,
                                N_("|PGM|use PGM as the SCdaemon program") },
   { oDisableScdaemon, "disable-scdaemon", 0, N_("do not use the SCdaemon") },
+  { oDisableCheckOwnSocket, "disable-check-own-socket", 0, "@" },
   { oFakedSystemTime, "faked-system-time", 2, "@" }, /* (epoch time) */
 
   { oBatch,      "batch",       0, "@" },
@@ -232,6 +234,9 @@ static int shutdown_pending;
 /* Counter for the currently running own socket checks.  */
 static int check_own_socket_running;
 
+/* Flags to indicate that check_own_socket shall not be called.  */
+static int disable_check_own_socket;
+
 /* It is possible that we are currently running under setuid permissions */
 static int maybe_setuid = 1;
 
@@ -491,6 +496,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
       opt.ignore_cache_for_signing = 0;
       opt.allow_mark_trusted = 0;
       opt.disable_scdaemon = 0;
+      disable_check_own_socket = 0;
       return 1;
     }
 
@@ -521,6 +527,7 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
     case oPinentryTouchFile: opt.pinentry_touch_file = pargs->r.ret_str; break;
     case oScdaemonProgram: opt.scdaemon_program = pargs->r.ret_str; break;
     case oDisableScdaemon: opt.disable_scdaemon = 1; break;
+    case oDisableCheckOwnSocket: disable_check_own_socket = 1; break;
 
     case oDefCacheTTL: opt.def_cache_ttl = pargs->r.ret_ulong; break;
     case oDefCacheTTLSSH: opt.def_cache_ttl_ssh = pargs->r.ret_ulong; break;
@@ -2174,6 +2181,9 @@ check_own_socket (void)
   npth_attr_t tattr;
   int err;
 
+  if (disable_check_own_socket)
+    return;
+
   if (!opt.use_standard_socket)
     return; /* This check makes only sense in standard socket mode.  */
 
diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi
index dcd96fb..f832b8e 100644
--- a/doc/gpg-agent.texi
+++ b/doc/gpg-agent.texi
@@ -449,6 +449,16 @@ Do not make use of the scdaemon tool.  This option has the effect of
 disabling the ability to do smartcard operations.  Note, that enabling
 this option at runtime does not kill an already forked scdaemon.
 
+ at ifset gpgtwoone
+ at item --disable-check-own-socket
+ at opindex disable-check-own-socket
+ at command{gpg-agent} employs a periodic self-test to detect a stolen
+socket.  This usually means a second instance of @command{gpg-agent}
+has taken over the socket and @command{gpg-agent} will then terminate
+itself.  This option may be used to disable this self-test for
+debugging purposes.
+ at end ifset
+
 @item --use-standard-socket
 @itemx --no-use-standard-socket
 @opindex use-standard-socket
@@ -695,14 +705,16 @@ Here is a list of supported signals:
 @item SIGHUP
 @cpindex SIGHUP
 This signal flushes all cached passphrases and if the program has been
-started with a configuration file, the configuration file is read again.
-Only certain options are honored: @code{quiet}, @code{verbose},
- at code{debug}, @code{debug-all}, @code{debug-level}, @code{no-grab},
- at code{pinentry-program}, @code{default-cache-ttl}, @code{max-cache-ttl},
- at code{ignore-cache-for-signing}, @code{allow-mark-trusted} and
- at code{disable-scdaemon}.  @code{scdaemon-program} is also supported but
-due to the current implementation, which calls the scdaemon only once,
-it is not of much use unless you manually kill the scdaemon.
+started with a configuration file, the configuration file is read
+again.  Only certain options are honored: @code{quiet},
+ at code{verbose}, @code{debug}, @code{debug-all}, @code{debug-level},
+ at code{no-grab}, @code{pinentry-program}, @code{default-cache-ttl},
+ at code{max-cache-ttl}, @code{ignore-cache-for-signing},
+ at code{allow-mark-trusted}, @code{disable-scdaemon}, and
+ at code{disable-check-own-socket}.  @code{scdaemon-program} is also
+supported but due to the current implementation, which calls the
+scdaemon only once, it is not of much use unless you manually kill the
+scdaemon.
 
 
 @item SIGTERM

-----------------------------------------------------------------------

Summary of changes:
 README               |   57 +++++---
 agent/agent.h        |    3 +-
 agent/command.c      |   24 +++-
 agent/cvt-openpgp.c  |  365 +++++++++++++++++++++++++++++++++++++-------------
 agent/cvt-openpgp.h  |    4 +
 agent/findkey.c      |    9 +-
 agent/gpg-agent.c    |   10 ++
 agent/keyformat.txt  |  126 +++++++++++------
 agent/protect-tool.c |   14 ++-
 agent/protect.c      |   32 ++++-
 agent/t-protect.c    |   11 ++
 common/logging.c     |   13 ++
 common/sexputil.c    |   86 ++++++++++++
 common/util.h        |    5 +
 doc/gpg-agent.texi   |   28 +++-
 g10/call-agent.c     |    5 +-
 g10/call-agent.h     |    2 +-
 g10/import.c         |    2 +-
 18 files changed, 613 insertions(+), 183 deletions(-)


hooks/post-receive
-- 
The GNU Privacy Guard
http://git.gnupg.org




More information about the Gnupg-commits mailing list