Avoid using the protect-tool to import pkcs#12.
authorWerner Koch <wk@gnupg.org>
Thu, 17 Jun 2010 15:44:44 +0000 (15:44 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 17 Jun 2010 15:44:44 +0000 (15:44 +0000)
24 files changed:
agent/ChangeLog
agent/Makefile.am
agent/agent.h
agent/command.c
agent/findkey.c
agent/genkey.c
agent/preset-passphrase.c
agent/protect-tool.c
agent/protect.c
common/ChangeLog
common/membuf.c
common/sexputil.c
common/util.h
dirmngr/http.c
doc/dirmngr.texi
g10/ChangeLog
g10/gpg.c
sm/ChangeLog
sm/Makefile.am
sm/call-agent.c
sm/gpgsm.h
sm/import.c
sm/minip12.c [moved from agent/minip12.c with 99% similarity]
sm/minip12.h [moved from agent/minip12.h with 95% similarity]

index 91dd3ac..7ea18d9 100644 (file)
@@ -1,3 +1,29 @@
+2010-06-15  Werner Koch  <wk@g10code.com>
+
+       * command.c (cmd_keywrap_key, cmd_import_key): New.
+
+       * genkey.c (agent_genkey, agent_protect_and_store): Factor common
+       code out to...
+       (agent_ask_new_passphrase): .. new.
+
+       * findkey.c (agent_write_private_key): Return GPG_ERR_EEXIST
+       instead of GPG_ERR_GENERAL.
+
+2010-06-14  Werner Koch  <wk@g10code.com>
+
+       * protect-tool.c: Remove commands --p12-import and --p12-export.
+       * minip12.c, minip12.h: Move to ../sm.
+       * Makefile.am (gpg_protect_tool_SOURCES): Remove them.
+       * preset-passphrase.c: Remove unneeded minip12.h.
+
+       * command.c (cmd_keywrap_key): New.
+
+       * command.c (leave_cmd): New.
+       (cmd_istrusted, cmd_listtrusted, cmd_marktrusted, cmd_pksign)
+       (cmd_pkdecrypt, cmd_genkey, cmd_readkey, cmd_keyinfo)
+       (cmd_get_passphrase, cmd_get_confirmation, cmd_learn)
+       (cmd_passwd, cmd_preset_passphrase, cmd_getval, cmd_putval): Use it.
+
 2010-05-12  Werner Koch  <wk@g10code.com>
 
        * preset-passphrase.c (forget_passphrase): Actually implement
index 9258fc8..e221536 100644 (file)
@@ -79,8 +79,7 @@ gpg_agent_DEPENDENCIES = $(gpg_agent_res_deps)
 
 gpg_protect_tool_SOURCES = \
        protect-tool.c \
-       protect.c \
-       minip12.c minip12.h 
+       protect.c
 
 gpg_protect_tool_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS)
 gpg_protect_tool_LDADD = $(common_libs) $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) \
index 09519d4..b39f232 100644 (file)
@@ -284,6 +284,8 @@ int agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
 
 /*-- genkey.c --*/
 int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent);
+gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
+                                      char **r_passphrase);
 int agent_genkey (ctrl_t ctrl, 
                   const char *keyparam, size_t keyparmlen, membuf_t *outbuf);
 int agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey);
index 082e730..9bd5ce5 100644 (file)
 #include <assuan.h>
 #include "i18n.h"
 
-/* maximum allowed size of the inquired ciphertext */
+/* Maximum allowed size of the inquired ciphertext.  */
 #define MAXLEN_CIPHERTEXT 4096
-/* maximum allowed size of the key parameters */
+/* Maximum allowed size of the key parameters.  */
 #define MAXLEN_KEYPARAM 1024
+/* Maximum allowed size of key data as used in inquiries (bytes). */
+#define MAXLEN_KEYDATA 4096
+/* The size of the import/export KEK key (in bytes).  */
+#define KEYWRAP_KEYSIZE (128/8)
 
 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
 
@@ -63,6 +67,8 @@ struct server_local_s
                     the end of this session.  */
   int allow_pinentry_notify; /* Set if pinentry notifications should
                                 be done. */
+  void *import_key;  /* Malloced KEK for the import_key command.  */
+  void *export_key;  /* Malloced KEK for the export_key command.  */
 };
 
 
@@ -340,6 +346,26 @@ agent_inq_pinentry_launched (ctrl_t ctrl, unsigned long pid)
 }
 
 
+/* Helper to print a message while leaving a command.  */
+static gpg_error_t
+leave_cmd (assuan_context_t ctx, gpg_error_t err)
+{
+  if (err)
+    {
+      const char *name = assuan_get_command_name (ctx);
+      if (!name)
+        name = "?";
+      if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
+        log_error ("command '%s' failed: %s\n", name,
+                   gpg_strerror (err));
+      else
+        log_error ("command '%s' failed: %s <%s>\n", name,
+                   gpg_strerror (err), gpg_strsource (err));
+    }
+  return err;
+}
+
+
 \f
 static const char hlp_geteventcounter[] = 
   "GETEVENTCOUNTER\n"
@@ -432,10 +458,7 @@ cmd_istrusted (assuan_context_t ctx, char *line)
   else if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF )
     return gpg_error (GPG_ERR_NOT_TRUSTED);
   else
-    {
-      log_error ("command is_trusted failed: %s\n", gpg_strerror (rc));
-      return rc;
-    }
+    return leave_cmd (ctx, rc);
 }
 
 
@@ -451,9 +474,7 @@ cmd_listtrusted (assuan_context_t ctx, char *line)
   (void)line;
 
   rc = agent_listtrusted (ctx);
-  if (rc)
-    log_error ("command listtrusted failed: %s\n", gpg_strerror (rc));
-  return rc;
+  return leave_cmd (ctx, rc);
 }
 
 
@@ -494,9 +515,7 @@ cmd_marktrusted (assuan_context_t ctx, char *line)
     p++;
 
   rc = agent_marktrusted (ctrl, p, fpr, flag);
-  if (rc)
-    log_error ("command marktrusted failed: %s\n", gpg_strerror (rc));
-  return rc;
+  return leave_cmd (ctx, rc);
 }
 
 
@@ -695,11 +714,9 @@ cmd_pksign (assuan_context_t ctx, char *line)
     clear_outbuf (&outbuf);
   else
     rc = write_and_clear_outbuf (ctx, &outbuf);
-  if (rc)
-    log_error ("command pksign failed: %s\n", gpg_strerror (rc));
   xfree (ctrl->server_local->keydesc);
   ctrl->server_local->keydesc = NULL;
-  return rc;
+  return leave_cmd (ctx, rc);
 }
 
 
@@ -734,11 +751,9 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
     clear_outbuf (&outbuf);
   else
     rc = write_and_clear_outbuf (ctx, &outbuf);
-  if (rc)
-    log_error ("command pkdecrypt failed: %s\n", gpg_strerror (rc));
   xfree (ctrl->server_local->keydesc);
   ctrl->server_local->keydesc = NULL;
-  return rc;
+  return leave_cmd (ctx, rc);
 }
 
 
@@ -780,9 +795,7 @@ cmd_genkey (assuan_context_t ctx, char *line)
     clear_outbuf (&outbuf);
   else
     rc = write_and_clear_outbuf (ctx, &outbuf);
-  if (rc)
-    log_error ("command genkey failed: %s\n", gpg_strerror (rc));
-  return rc;
+  return leave_cmd (ctx, rc);
 }
 
 
@@ -825,9 +838,7 @@ cmd_readkey (assuan_context_t ctx, char *line)
       gcry_sexp_release (s_pkey);
     }
 
-  if (rc)
-    log_error ("command readkey failed: %s\n", gpg_strerror (rc));
-  return rc;
+  return leave_cmd (ctx, rc);
 }
 
 
@@ -967,7 +978,7 @@ cmd_keyinfo (assuan_context_t ctx, char *line)
   if (dir)
     closedir (dir);
   if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND)
-    log_error ("command keyinfo failed: %s\n", gpg_strerror (err));
+    leave_cmd (ctx, err);
   return err;
 }
 
@@ -1167,9 +1178,7 @@ cmd_get_passphrase (assuan_context_t ctx, char *line)
         }
     }
 
-  if (rc)
-    log_error ("command get_passphrase failed: %s\n", gpg_strerror (rc));
-  return rc;
+  return leave_cmd (ctx, rc);
 }
 
 
@@ -1240,9 +1249,7 @@ cmd_get_confirmation (assuan_context_t ctx, char *line)
     plus_to_blank (desc);
 
   rc = agent_get_confirmation (ctrl, desc, NULL, NULL, 0);
-  if (rc)
-    log_error ("command get_confirmation failed: %s\n", gpg_strerror (rc));
-  return rc;
+  return leave_cmd (ctx, rc);
 }
 
 
@@ -1259,9 +1266,7 @@ cmd_learn (assuan_context_t ctx, char *line)
   int rc;
 
   rc = agent_handle_learn (ctrl, has_option (line, "--send")? ctx : NULL);
-  if (rc)
-    log_error ("command learn failed: %s\n", gpg_strerror (rc));
-  return rc;
+  return leave_cmd (ctx, rc);
 }
 
 
@@ -1304,9 +1309,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
  leave:
   gcry_sexp_release (s_skey);
   xfree (shadow_info);
-  if (rc)
-    log_error ("command passwd failed: %s\n", gpg_strerror (rc));
-  return rc;
+  return leave_cmd (ctx, rc);
 }
 
 
@@ -1371,10 +1374,7 @@ cmd_preset_passphrase (assuan_context_t ctx, char *line)
   if (!rc)
     rc = agent_put_cache (grip_clear, CACHE_MODE_ANY, passphrase, ttl);
 
-  if (rc)
-    log_error ("command preset_passphrase failed: %s\n", gpg_strerror (rc));
-
-  return rc;
+  return leave_cmd (ctx, rc);
 }
 
 
@@ -1397,6 +1397,186 @@ cmd_scd (assuan_context_t ctx, char *line)
 
 
 \f
+static const char hlp_keywrap_key[] =
+  "KEYWRAP_KEY [--clear] <mode>\n"
+  "\n"
+  "Return a key to wrap another key.  For now the key is returned\n"
+  "verbatim and and thus makes not much sense because an eavesdropper on\n"
+  "the gpg-agent connection will see the key as well as the wrapped key.\n"
+  "However, this function may either be equipped with a public key\n"
+  "mechanism or not used at all if the key is a pre-shared key.  In any\n"
+  "case wrapping the import and export of keys is a requirement for\n"
+  "certain cryptographic validations and thus useful.  The key persists\n"
+  "a RESET command but may be cleared using the option --clear.\n"
+  "\n"
+  "Supported modes are:\n"
+  "  --import  - Return a key to import a key into gpg-agent\n"
+  "  --export  - Return a key to export a key from gpg-agent";
+static gpg_error_t
+cmd_keywrap_key (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
+  int clearopt = has_option (line, "--clear");
+
+
+  assuan_begin_confidential (ctx);
+  if (has_option (line, "--import"))
+    {
+      xfree (ctrl->server_local->import_key);
+      if (clearopt)
+        ctrl->server_local->import_key = NULL;
+      else if (!(ctrl->server_local->import_key = 
+                 gcry_random_bytes (KEYWRAP_KEYSIZE, GCRY_STRONG_RANDOM)))
+        err = gpg_error_from_syserror ();
+      else
+        err = assuan_send_data (ctx, ctrl->server_local->import_key,
+                                KEYWRAP_KEYSIZE);
+    }
+  else if (has_option (line, "--export"))
+    {
+      xfree (ctrl->server_local->export_key);
+      if (clearopt)
+        ctrl->server_local->export_key = NULL;
+      else if (!(ctrl->server_local->export_key = 
+            gcry_random_bytes (KEYWRAP_KEYSIZE, GCRY_STRONG_RANDOM)))
+        err = gpg_error_from_syserror ();
+      else
+        err = assuan_send_data (ctx, ctrl->server_local->export_key,
+                                KEYWRAP_KEYSIZE);
+    }
+  else
+    err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for MODE");
+  assuan_end_confidential (ctx);
+  
+  return leave_cmd (ctx, err);
+}
+
+
+\f
+static const char hlp_import_key[] =
+  "IMPORT_KEY\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.";
+static gpg_error_t
+cmd_import_key (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+  unsigned char *wrappedkey = NULL;
+  size_t wrappedkeylen;
+  gcry_cipher_hd_t cipherhd = NULL;
+  unsigned char *key = NULL;
+  size_t keylen, realkeylen;
+  char *passphrase = NULL;
+  unsigned char *finalkey = NULL;
+  size_t finalkeylen;
+  unsigned char grip[20];
+  
+  (void)line;
+
+  if (!ctrl->server_local->import_key)
+    {
+      err = gpg_error (GPG_ERR_BAD_KEY);
+      goto leave;
+    }
+
+  assuan_begin_confidential (ctx);
+  err = assuan_inquire (ctx, "KEYDATA",
+                        &wrappedkey, &wrappedkeylen, MAXLEN_KEYDATA);
+  assuan_end_confidential (ctx);
+  if (err)
+    goto leave;
+  if (wrappedkeylen < 24)
+    {
+      err = gpg_error (GPG_ERR_INV_LENGTH);
+      goto leave;
+    }
+  keylen = wrappedkeylen - 8;
+  key = xtrymalloc_secure (keylen);
+  if (!key)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+
+  err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128,
+                          GCRY_CIPHER_MODE_AESWRAP, 0);
+  if (err)
+    goto leave;
+  err = gcry_cipher_setkey (cipherhd,
+                            ctrl->server_local->import_key, KEYWRAP_KEYSIZE);
+  if (err)
+    goto leave;
+  err = gcry_cipher_decrypt (cipherhd, key, keylen, wrappedkey, wrappedkeylen);
+  if (err)
+    goto leave;
+  gcry_cipher_close (cipherhd);
+  cipherhd = NULL;
+  xfree (wrappedkey);
+  wrappedkey = NULL;
+
+  realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err);
+  if (!realkeylen)
+    goto leave; /* Invalid canonical encoded S-expression.  */
+  
+  err = keygrip_from_canon_sexp (key, realkeylen, grip);
+  if (err)
+    goto leave;
+
+  if (!agent_key_available (grip))
+    {
+      err = gpg_error (GPG_ERR_EEXIST);
+      goto leave;
+    }
+
+  err = agent_ask_new_passphrase 
+    (ctrl, _("Please enter the passphrase to protect the "
+             "imported object within the GnuPG system."),
+     &passphrase);
+  if (err)
+    goto leave;
+
+  if (passphrase)
+    {
+      err = agent_protect (key, passphrase, &finalkey, &finalkeylen);
+      if (!err)
+        err = agent_write_private_key (grip, finalkey, finalkeylen, 0);
+    }
+  else
+    err = agent_write_private_key (grip, key, realkeylen, 0);
+
+ leave:
+  xfree (finalkey);
+  xfree (passphrase);
+  xfree (key);
+  gcry_cipher_close (cipherhd);
+  xfree (wrappedkey);
+  return leave_cmd (ctx, err);
+}
+
+
+\f
+static const char hlp_export_key[] =
+  "EXPORT_KEY\n"
+  "\n";
+static gpg_error_t
+cmd_export_key (assuan_context_t ctx, char *line)
+{
+  gpg_error_t err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+
+ /* leave: */
+  return leave_cmd (ctx, err);
+}
+
+
+
+\f
 static const char hlp_getval[] = 
   "GETVAL <key>\n"
   "\n"
@@ -1435,9 +1615,7 @@ cmd_getval (assuan_context_t ctx, char *line)
   else
     return gpg_error (GPG_ERR_NO_DATA);
 
-  if (rc)
-    log_error ("command getval failed: %s\n", gpg_strerror (rc));
-  return rc;
+  return leave_cmd (ctx, rc);
 }
 
 
@@ -1520,9 +1698,7 @@ cmd_putval (assuan_context_t ctx, char *line)
         }
     }
 
-  if (rc)
-    log_error ("command putval failed: %s\n", gpg_strerror (rc));
-  return rc;
+  return leave_cmd (ctx, rc);
 }
 
 
@@ -1641,7 +1817,7 @@ static const char hlp_getinfo[] =
   "  std_session_env - List the standard session environment.\n"
   "  std_startup_env - List the standard startup environment.\n"
   "  cmd_has_option\n"
-  "              - Returns OK if the command CMD implements the option OPT.";
+  "              - Returns OK if the command CMD implements the option OPT\n.";
 static gpg_error_t
 cmd_getinfo (assuan_context_t ctx, char *line)
 {
@@ -1910,6 +2086,9 @@ register_commands (assuan_context_t ctx)
     { "INPUT",          NULL }, 
     { "OUTPUT",         NULL }, 
     { "SCD",            cmd_scd,       hlp_scd },
+    { "KEYWRAP_KEY",    cmd_keywrap_key, hlp_keywrap_key },
+    { "IMPORT_KEY",     cmd_import_key, hlp_import_key },
+    { "EXPORT_KEY",     cmd_export_key, hlp_export_key },
     { "GETVAL",         cmd_getval,    hlp_getval },
     { "PUTVAL",         cmd_putval,    hlp_putval },
     { "UPDATESTARTUPTTY",  cmd_updatestartuptty, hlp_updatestartuptty },
@@ -2021,6 +2200,9 @@ start_command_handler (ctrl_t ctrl, gnupg_fd_t listen_fd, gnupg_fd_t fd)
 
   /* Cleanup.  */
   assuan_release (ctx);
+  xfree (ctrl->server_local->keydesc);
+  xfree (ctrl->server_local->import_key);
+  xfree (ctrl->server_local->export_key);
   if (ctrl->server_local->stopme)
     agent_exit (0);
   xfree (ctrl->server_local);
index d6478ac..db610c1 100644 (file)
@@ -69,7 +69,7 @@ agent_write_private_key (const unsigned char *grip,
     {
       log_error ("secret key file `%s' already exists\n", fname);
       xfree (fname);
-      return gpg_error (GPG_ERR_GENERAL);
+      return gpg_error (GPG_ERR_EEXIST);
     }
 
   /* FIXME: On POSIX systems we used include S_IRGRP as well.  */
@@ -883,8 +883,8 @@ agent_public_key_from_file (ctrl_t ctrl,
 
 
 
-/* Return the secret key as an S-Exp after locating it using the grip.
-   Returns NULL if key is not available. 0 = key is available */
+/* Check whether the the secret key identified by GRIP is available.
+   Returns 0 is the key is available.  */
 int
 agent_key_available (const unsigned char *grip)
 {
index c5d2c9e..7c6b44b 100644 (file)
@@ -1,5 +1,5 @@
 /* genkey.c - Generate a keypair
- *     Copyright (C) 2002, 2003, 2004, 2007 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2003, 2004, 2007, 2010 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -286,6 +286,69 @@ reenter_compare_cb (struct pin_entry_info_s *pi)
 }
 
 
+/* Ask the user for a new passphrase using PROMPT.  On success the
+   function returns 0 and store the passphrase at R_PASSPHRASE; if the
+   user opted not to use a passphrase NULL will be stored there.  The
+   user needs to free the returned string.  In case of an error and
+   error code is returned and NULL stored at R_PASSPHRASE.  */
+gpg_error_t
+agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
+                          char **r_passphrase)
+{
+  gpg_error_t err;
+  const char *text1 = prompt;
+  const char *text2 = _("Please re-enter this passphrase");
+  const char *initial_errtext = NULL;
+  struct pin_entry_info_s *pi, *pi2;
+  
+  *r_passphrase = NULL;
+
+  pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
+  pi2 = pi + (sizeof *pi + 100);
+  pi->max_length = 100;
+  pi->max_tries = 3;
+  pi->with_qualitybar = 1;
+  pi2->max_length = 100;
+  pi2->max_tries = 3;
+  pi2->check_cb = reenter_compare_cb;
+  pi2->check_cb_arg = pi->pin;
+
+ next_try:
+  err = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
+  initial_errtext = NULL;
+  if (!err)
+    {
+      if (check_passphrase_constraints (ctrl, pi->pin, 0))
+        {
+          pi->failed_tries = 0;
+          pi2->failed_tries = 0;
+          goto next_try;
+        }
+      /* Unless the passphrase is empty, ask to confirm it.  */
+      if (pi->pin && *pi->pin)
+        {
+          err = agent_askpin (ctrl, text2, NULL, NULL, pi2);
+          if (err == -1)
+            { /* The re-entered one did not match and the user did not
+                 hit cancel. */
+              initial_errtext = _("does not match - try again");
+              goto next_try;
+            }
+        }
+    }
+  
+  if (!err && *pi->pin)
+    {
+      /* User wants a passphrase. */
+      *r_passphrase = xtrystrdup (pi->pin);
+      if (!*r_passphrase)
+        err = gpg_error_from_syserror ();
+    }
+  xfree (pi);
+  return err;
+}
+
+
 
 /* Generate a new keypair according to the parameters given in
    KEYPARAM */
@@ -294,7 +357,7 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
               membuf_t *outbuf) 
 {
   gcry_sexp_t s_keyparam, s_key, s_private, s_public;
-  struct pin_entry_info_s *pi, *pi2;
+  char *passphrase = NULL;
   int rc;
   size_t len;
   char *buf;
@@ -307,63 +370,19 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
     }
 
   /* Get the passphrase now, cause key generation may take a while. */
-  {
-    const char *text1 = _("Please enter the passphrase to%0A"
-                               "to protect your new key");
-    const char *text2 = _("Please re-enter this passphrase");
-    const char *initial_errtext = NULL;
-
-    pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
-    pi2 = pi + (sizeof *pi + 100);
-    pi->max_length = 100;
-    pi->max_tries = 3;
-    pi->with_qualitybar = 1;
-    pi2->max_length = 100;
-    pi2->max_tries = 3;
-    pi2->check_cb = reenter_compare_cb;
-    pi2->check_cb_arg = pi->pin;
-
-  next_try:
-    rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
-    initial_errtext = NULL;
-    if (!rc)
-      {
-        if (check_passphrase_constraints (ctrl, pi->pin, 0))
-          {
-            pi->failed_tries = 0;
-            pi2->failed_tries = 0;
-            goto next_try;
-          }
-        if (pi->pin && *pi->pin)
-          {
-            rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
-            if (rc == -1)
-              { /* The re-entered one did not match and the user did not
-                   hit cancel. */
-                initial_errtext = _("does not match - try again");
-                goto next_try;
-              }
-          }
-      }
-    if (rc)
-      {
-        xfree (pi);
-        return rc;
-      }
-        
-    if (!*pi->pin)
-      {
-        xfree (pi);
-        pi = NULL; /* User does not want a passphrase. */
-      }
-  }
+  rc = agent_ask_new_passphrase (ctrl, 
+                                 _("Please enter the passphrase to%0A"
+                                   "to protect your new key"),
+                                 &passphrase);
+  if (rc)
+    return rc;
 
   rc = gcry_pk_genkey (&s_key, s_keyparam );
   gcry_sexp_release (s_keyparam);
   if (rc)
     {
       log_error ("key generation failed: %s\n", gpg_strerror (rc));
-      xfree (pi);
+      xfree (passphrase);
       return rc;
     }
 
@@ -373,7 +392,7 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
     {
       log_error ("key generation failed: invalid return value\n");
       gcry_sexp_release (s_key);
-      xfree (pi);
+      xfree (passphrase);
       return gpg_error (GPG_ERR_INV_DATA);
     }
   s_public = gcry_sexp_find_token (s_key, "public-key", 0);
@@ -382,7 +401,7 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
       log_error ("key generation failed: invalid return value\n");
       gcry_sexp_release (s_private);
       gcry_sexp_release (s_key);
-      xfree (pi);
+      xfree (passphrase);
       return gpg_error (GPG_ERR_INV_DATA);
     }
   gcry_sexp_release (s_key); s_key = NULL;
@@ -390,8 +409,9 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
   /* store the secret key */
   if (DBG_CRYPTO)
     log_debug ("storing private key\n");
-  rc = store_key (s_private, pi? pi->pin:NULL, 0);
-  xfree (pi); pi = NULL;
+  rc = store_key (s_private, passphrase, 0);
+  xfree (passphrase);
+  passphrase = NULL;
   gcry_sexp_release (s_private);
   if (rc)
     {
@@ -423,65 +443,20 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
 
 
 \f
-/* Apply a new passpahrse to the key S_SKEY and store it. */
+/* Apply a new passphrase to the key S_SKEY and store it. */
 int
 agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey) 
 {
-  struct pin_entry_info_s *pi, *pi2;
   int rc;
+  char *passphrase;
 
-  {
-    const char *text1 = _("Please enter the new passphrase");
-    const char *text2 = _("Please re-enter this passphrase");
-    const char *initial_errtext = NULL;
-
-    pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
-    pi2 = pi + (sizeof *pi + 100);
-    pi->max_length = 100;
-    pi->max_tries = 3;
-    pi->with_qualitybar = 1;
-    pi2->max_length = 100;
-    pi2->max_tries = 3;
-    pi2->check_cb = reenter_compare_cb;
-    pi2->check_cb_arg = pi->pin;
-
-  next_try:
-    rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
-    initial_errtext = NULL;
-    if (!rc)
-      {
-        if (check_passphrase_constraints (ctrl, pi->pin, 0))
-          {
-            pi->failed_tries = 0;
-            pi2->failed_tries = 0;
-            goto next_try;
-          }
-        /* Unless the passphrase is empty, ask to confirm it.  */
-        if (pi->pin && *pi->pin)
-          {
-            rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
-            if (rc == -1)
-              { /* The re-entered one did not match and the user did not
-                   hit cancel. */
-                initial_errtext = _("does not match - try again");
-                goto next_try;
-              }
-          }
-      }
-    if (rc)
-      {
-        xfree (pi);
-        return rc;
-      }
-
-    if (!*pi->pin)
-      {
-        xfree (pi);
-        pi = NULL; /* User does not want a passphrase. */
-      }
-  }
-
-  rc = store_key (s_skey, pi? pi->pin:NULL, 1);
-  xfree (pi);
+  rc = agent_ask_new_passphrase (ctrl, 
+                                 _("Please enter the new passphrase"),
+                                 &passphrase);
+  if (!rc)
+    {
+      rc = store_key (s_skey, passphrase, 1);
+      xfree (passphrase);
+    }
   return rc;
 }
index ae202bf..2037d95 100644 (file)
@@ -43,7 +43,6 @@
 
 #define JNLIB_NEED_LOG_LOGV
 #include "agent.h"
-#include "minip12.h"
 #include "simple-pwquery.h"
 #include "i18n.h"
 #include "sysutils.h"
index 38debb9..48186d2 100644 (file)
@@ -40,7 +40,6 @@
 
 #define JNLIB_NEED_LOG_LOGV
 #include "agent.h"
-#include "minip12.h"
 #include "i18n.h"
 #include "get-passphrase.h"
 #include "sysutils.h"
@@ -63,8 +62,6 @@ enum cmd_and_opt_values
   oS2Kcalibration,
   oCanonical,
 
-  oP12Import,
-  oP12Export,
   oP12Charset,
   oStore,
   oForce,
@@ -116,11 +113,6 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_c (oShadow,    "shadow", "create a shadow entry for a public key"),
   ARGPARSE_c (oShowShadowInfo,  "show-shadow-info", "return the shadow info"),
   ARGPARSE_c (oShowKeygrip, "show-keygrip", "show the \"keygrip\""),
-  ARGPARSE_c (oP12Import, "p12-import", 
-              "import a pkcs#12 encoded private key"),
-  ARGPARSE_c (oP12Export, "p12-export",
-              "export a private key pkcs#12 encoded"),
-
   ARGPARSE_c (oS2Kcalibration, "s2k-calibration", "@"),
   
   ARGPARSE_group (301, N_("@\nOptions:\n ")),
@@ -635,7 +627,7 @@ rsa_key_check (struct rsa_secret_key_s *skey)
   return err? -1:0;
 }
 
-
+#if 0
 /* A callback used by p12_parse to return a certificate.  */
 static void
 import_p12_cert_cb (void *opaque, const unsigned char *cert, size_t certlen)
@@ -793,6 +785,7 @@ import_p12_file (const char *fname)
 
   xfree (result);
 }
+#endif
 
 \f
 
@@ -865,6 +858,7 @@ is_keygrip (const char *string)
 }
 
 
+#if 0
 static void
 export_p12_file (const char *fname)
 {
@@ -1009,6 +1003,7 @@ export_p12_file (const char *fname)
   fwrite (key, keylen, 1, stdout);
   xfree (key);
 }
+#endif
 
 
 \f
@@ -1059,8 +1054,6 @@ main (int argc, char **argv )
         case oShadow: cmd = oShadow; break;
         case oShowShadowInfo: cmd = oShowShadowInfo; break;
         case oShowKeygrip: cmd = oShowKeygrip; break;
-        case oP12Import: cmd = oP12Import; break;
-        case oP12Export: cmd = oP12Export; break;
         case oP12Charset: opt_p12_charset = pargs.r.ret_str; break;
 
         case oS2Kcalibration: cmd = oS2Kcalibration; break;
@@ -1105,10 +1098,6 @@ main (int argc, char **argv )
     show_shadow_info (fname);
   else if (cmd == oShowKeygrip)
     show_keygrip (fname);
-  else if (cmd == oP12Import)
-    import_p12_file (fname);
-  else if (cmd == oP12Export)
-    export_p12_file (fname);
   else if (cmd == oS2Kcalibration)
     {
       if (!opt.verbose)
index 7f3c1cc..db6caa4 100644 (file)
@@ -415,7 +415,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   unsigned char *p;
   gcry_md_hd_t md;
 
-  /* Create an S-expression with the procted-at timestamp.  */
+  /* Create an S-expression with the protected-at timestamp.  */
   memcpy (timestamp_exp, "(12:protected-at15:", 19);
   gnupg_get_isotime (timestamp_exp+19);
   timestamp_exp[19+15] = ')';
index 5aa3991..e581573 100644 (file)
@@ -1,3 +1,11 @@
+2010-06-17  Werner Koch  <wk@g10code.com>
+
+       * sexputil.c (make_canon_sexp_pad): New.
+
+2010-06-14  Werner Koch  <wk@g10code.com>
+
+       * membuf.c (put_membuf): Add shortcut for !LEN.
+
 2010-06-11  Marcus Brinkmann  <marcus@g10code.de>
 
        * sysutils.c (translate_sys2libc_fd): Revert last change.
index dc8f6f6..f9f82d3 100644 (file)
@@ -59,7 +59,7 @@ init_membuf_secure (membuf_t *mb, int initiallen)
 void
 put_membuf (membuf_t *mb, const void *buf, size_t len)
 {
-  if (mb->out_of_core)
+  if (mb->out_of_core || !len)
     return;
 
   if (mb->len + len >= mb->size)
index 736cade..b336145 100644 (file)
@@ -36,7 +36,7 @@
 #include "sexp-parse.h"
 
 
-/* Helper function to create a canonical encoded S-expression from a
+/* 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
    the allocated length at R_BUFLEN.  On error an error code is
@@ -71,6 +71,36 @@ make_canon_sexp (gcry_sexp_t sexp, unsigned char **r_buffer, size_t *r_buflen)
 }
 
 
+/* Same as make_canon_sexp but pad the buffer to multiple of 64
+   bits.  */
+gpg_error_t
+make_canon_sexp_pad (gcry_sexp_t sexp,
+                     unsigned char **r_buffer, size_t *r_buflen)
+{
+  size_t len;
+  unsigned char *buf;
+
+  *r_buffer = NULL;
+  if (r_buflen)
+    *r_buflen = 0;;
+  
+  len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0);
+  if (!len)
+    return gpg_error (GPG_ERR_BUG);
+  len += (8 - len % 8) % 8;
+  buf = xtrycalloc (1, len);
+  if (!buf)
+    return gpg_error_from_syserror ();
+  if (!gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, len))
+    return gpg_error (GPG_ERR_BUG);
+
+  *r_buffer = buf;
+  if (r_buflen)
+    *r_buflen = len;
+
+  return 0;
+}
+
 /* Return the so called "keygrip" which is the SHA-1 hash of the
    public key parameters expressed in a way depended on the algorithm.
 
index 97ecef1..519bc5d 100644 (file)
@@ -146,6 +146,8 @@ gpg_error_t b64dec_finish (struct b64state *state);
 /*-- sexputil.c */
 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,
+                                 unsigned char **r_buffer, size_t *r_buflen);
 gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen,
                                      unsigned char *grip);
 int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b);
index b10ba25..b65a92a 100644 (file)
@@ -38,6 +38,8 @@
     an exit handler to cleanup the socket layer.
 */
 
+#warning Duplicated code with common/http.c 
+
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 #endif
index bb15766..3445047 100644 (file)
@@ -786,3 +786,254 @@ as a binary blob.
 @end ifset
 @include see-also-note.texi
 
+@c 
+@c !!! UNDER CONSTRUCTION !!!
+@c 
+@c 
+@c @section Verifying a Certificate
+@c 
+@c There are several ways to request services from Dirmngr.  Almost all of
+@c them are done using the Assuan protocol.  What we describe here is the
+@c Assuan command CHECKCRL as used for example by the dirmnr-client tool if
+@c invoked as
+@c 
+@c @example
+@c   dirmngr-client foo.crt
+@c @end example
+@c 
+@c This command will send an Assuan request to an already running Dirmngr
+@c instance.  foo.crt is expected to be a standard X.509 certificate and
+@c dirmngr will receive the Assuan command
+@c 
+@c @example
+@c    CHECKCRL @var [{fingerprint}]
+@c @end example
+@c 
+@c @var{fingerprint} is optional and expected to be the SHA-1 has of the
+@c DER encoding of the certificate under question.  It is to be HEX
+@c encoded.  The rationale for sending the fingerprint is that it allows
+@c dirmngr to reply immediatly if it has already cached such a request.  If
+@c this is not the case and no certificate has been found in dirmngr's
+@c internal certificate storage, dirmngr will request the certificate using
+@c the Assuan inquiry
+@c 
+@c @example
+@c       INQUIRE TARGETCERT
+@c @end example
+@c 
+@c The caller (in our example dirmngr-client) is then expected to return
+@c the certificate for the request (which should match @var{fingerprint})
+@c as a binary blob.
+@c 
+@c Dirmngr now passes control to @code{crl_cache_cert_isvalid}.  This
+@c function checks whether a CRL item exists for target certificate.  These
+@c CRL items are kept in a database of already loaded and verified CRLs.
+@c This mechanism is called the CRL cache.  Obviously timestamps are kept
+@c there with each item to cope with the expiration date of the CRL.  The
+@c possible return values are: @code{0} to indicate that a valid CRL is
+@c available for the certificate and the certificate itself is not listed
+@c in this CRL, @code{GPG_ERR_CERT_REVOKED} to indicate that the certificate is
+@c listed in the CRL or @code{GPG_ERR_NO_CRL_KNOWN} in cases where no CRL or no
+@c information is available.  The first two codes are immediatly returned to
+@c the caller and the processing of this request has been done.
+@c 
+@c Only the @code{GPG_ERR_NO_CRL_KNOWN} needs more attention: Dirmngr now
+@c calls @code{clr_cache_reload_crl} and if this succeeds calls
+@c @code{crl_cache_cert_isvald) once more.  All further errors are
+@c immediately returned to the caller.
+@c 
+@c @code{crl_cache_reload_crl} is the actual heart of the CRL management.
+@c It locates the corresponding CRL for the target certificate, reads and
+@c verifies this CRL and stores it in the CRL cache.  It works like this:
+@c 
+@c * Loop over all crlDPs in the target certificate.
+@c     * If the crlDP is invalid immediately terminate the loop.
+@c     * Loop over all names in the current crlDP.
+@c         * If the URL scheme is unknown or not enabled 
+@c           (--ignore-http-dp, --ignore-ldap-dp) continues with
+@c           the next name.
+@c         * @code{crl_fetch} is called to actually retrieve the CRL.
+@c           In case of problems this name is ignore and we continue with
+@c           the next name.  Note that @code{crl_fetch} does only return 
+@c           a descriptor for the CRL for further reading so does the CRL
+@c           does not yet end up in memory.
+@c         * @code{crl_cache_insert} is called with that descriptor to
+@c           actually read the CRL into the cache. See below for a
+@c           description of this function.  If there is any error (e.g. read
+@c           problem, CRL not correctly signed or verification of signature
+@c           not possible), this descriptor is rejected and we continue
+@c           with the next name.  If the CRL has been successfully loaded,
+@c           the loop is terminated.
+@c * If no crlDP has been found in the previous loop use a default CRL.
+@c   Note, that if any crlDP has been found but loading of the CRL failed,
+@c   this condition is not true.
+@c     * Try to load a CRL from all configured servers (ldapservers.conf)
+@c       in turn.  The first server returning a CRL is used.
+@c     * @code(crl_cache_insert) is then used to actually insert the CRL
+@c       into the cache.  If this failed we give up immediatley without
+@c       checking the rest of the servers from the first step.
+@c * Ready. 
+@c 
+@c 
+@c The @code{crl_cache_insert} function takes care of reading the bulk of
+@c the CRL, parsing it and checking the signature.  It works like this: A
+@c new database file is created using a temporary file name.  The CRL
+@c parsing machinery is started and all items of the CRL are put into
+@c this database file.  At the end the issuer certificate of the CRL
+@c needs to be retrieved.  Three cases are to be distinguished:
+@c 
+@c  a) An authorityKeyIdentifier with an issuer and serialno exits: The
+@c     certificate is retrieved using @code{find_cert_bysn}.  If
+@c     the certificate is in the certificate cache, it is directly
+@c     returned. Then the requester (i.e. the client who requested the
+@c     CRL check) is asked via the Assuan inquiry ``SENDCERT'' whether
+@c     he can provide this certificate.  If this succeed the returned
+@c     certificate gets cached and returned.  Note, that dirmngr does not
+@c     verify in any way whether the expected certificate is returned.
+@c     It is in the interest of the client to return a useful certificate
+@c     as otherwise the service request will fail due to a bad signature.
+@c     The last way to get the certificate is by looking it up at
+@c     external resources.  This is done using the @code{ca_cert_fetch}
+@c     and @code{fetch_next_ksba_cert} and comparing the returned
+@c     certificate to match the requested issuer and seriano (This is
+@c     needed because the LDAP layer may return several certificates as
+@c     LDAP as no standard way to retrieve by serial number).
+@c 
+@c  b) An authorityKeyIdentifier with a key ID exists: The certificate is
+@c     retrieved using @code{find_cert_bysubject}.  If the certificate is
+@c     in the certificate cache, it is directly returned.  Then the
+@c     requester is asked via the Assuan inquiry ``SENDCERT_SKI'' whether
+@c     he can provide this certificate.  If this succeed the returned
+@c     certificate gets cached and returned.  Note, that dirmngr does not
+@c     verify in any way whether the expected certificate is returned.
+@c     It is in the interest of the client to return a useful certificate
+@c     as otherwise the service request will fail due to a bad signature.
+@c     The last way to get the certificate is by looking it up at
+@c     external resources.  This is done using the @code{ca_cert_fetch}
+@c     and @code{fetch_next_ksba_cert} and comparing the returned
+@c     certificate to match the requested subject and key ID.
+@c 
+@c  c) No authorityKeyIdentifier exits: The certificate is retrieved
+@c     using @code{find_cert_bysubject} without the key ID argument.  If
+@c     the certificate is in the certificate cache the first one with a
+@c     matching subject is is directly returned.  Then the requester is
+@c     asked via the Assuan inquiry ``SENDCERT'' and an exact
+@c     specification of the subject whether he can
+@c     provide this certificate.  If this succeed the returned
+@c     certificate gets cached and returned.  Note, that dirmngr does not
+@c     verify in any way whether the expected certificate is returned.
+@c     It is in the interest of the client to return a useful certificate
+@c     as otherwise the service request will fail due to a bad signature.
+@c     The last way to get the certificate is by looking it up at
+@c     external resources.  This is done using the @code{ca_cert_fetch}
+@c     and @code{fetch_next_ksba_cert} and comparing the returned
+@c     certificate to match the requested subject; the first certificate
+@c     with a matching subject is then returned.
+@c 
+@c If no certificate was found, the function returns with the error
+@c GPG_ERR_MISSING_CERT.  Now the signature is verified.  If this fails,
+@c the erro is returned.  On success the @code{validate_cert_chain} is
+@c used to verify that the certificate is actually valid. 
+@c 
+@c Here we may encounter a recursive situation:
+@c @code{validate_cert_chain} needs to look at other certificates and
+@c also at CRLs to check whether tehse other certificates and well, the
+@c CRL issuer certificate itself are not revoked.  FIXME: We need to make
+@c sure that @code{validate_cert_chain} does not try to lookup the CRL we
+@c are currently processing. This would be a catch-22 and may indicate a
+@c broken PKI.  However, due to overlapping expiring times and imprecise
+@c clocks thsi may actually happen.
+@c     
+@c For historical reasons the Assuan command ISVALID is a bit different
+@c to CHECKCRL but this is mainly due to different calling conventions.
+@c In the end the same fucntionality is used, albeit hidden by a couple
+@c of indirection and argument and result code mangling.  It furthere
+@c ingetrages OCSP checking depending on options are the way it is
+@c called.  GPGSM still uses this command but might eventuall switch over
+@c to CHECKCRL and CHECKOCSP so that ISVALID can be retired.
+@c   
+@c 
+@c @section Validating a certificate
+@c 
+@c We describe here how the internal function @code{validate_cert_chain}
+@c works. Note that mainly testing purposes this functionality may be
+@c called directly using @cmd{dirmngr-client --validate @file{foo.crt}}.
+@c 
+@c For backward compatibility this function returns success if Dirmngr is
+@c not used as a system daemon.  Thus not validating the certicates at
+@c all. FIXME:  This is definitely not correct and should be fixed ASAP.
+@c 
+@c The function takes the target certificate and a mode argument as
+@c parameters and returns an error code and optionally the closes
+@c expiration time of all certificates in the chain.
+@c 
+@c We first check that the certificate may be used for the requested
+@c purpose (i.e. OCSP or CRL signing).  If this is not the case
+@c GPG_ERR_WRONG_KEY_USAGE is returned.
+@c 
+@c The next step is to find the trust anchor (root certificate) and to
+@c assemble the chain in memory: Starting with the target certificate,
+@c the expiration time is checked against the current date, unknown
+@c critical extensions are detected and certificate policies are matched
+@c (We only allow 2.289.9.9 but I have no clue about that OID and from
+@c where I got it - it does not even seem to be assigned - debug cruft?).
+@c 
+@c Now if this certificate is a self-signed one, we have reached the
+@c trust anchor.  In this case we check that the signature is good, the
+@c certificate is allowed to act as a CA, that it is a trusted one (by
+@c checking whether it is has been put into the trusted-certs
+@c configuration directory) and finally prepend into to our list
+@c representing the certificate chain.  This steps ends then.
+@c 
+@c If it is not a self-signed certificate, we check that the chain won't
+@c get too long (current limit is 100), if this is the case we terminate
+@c with the error GPG_ERR_BAD_CERT_CHAIN.
+@c 
+@c Now the issuer's certificate is looked up: If an
+@c authorityKeyIdentifier is available, this one is used to locate the
+@c certificate either using issuer and serialnumber or subject DN
+@c (i.e. the issuer's DN) and the keyID.  The functions
+@c @code{find_cert_bysn) and @code{find_cert_bysubject} are used
+@c respectively. The have already been described above under the
+@c description of @code{crl_cache_insert}.  If no certificate was found
+@c or with no authorityKeyIdentifier, only the cache is consulted using
+@c @code{get_cert_bysubject}.  The latter is is done under the assumption
+@c that a matching certificate has explicitly been put into the
+@c certificate cache.  If the issuer's certificate could not be found,
+@c the validation terminates with the error code @code{GPG_ERR_MISSING_CERT}.
+@c 
+@c If the issuer's certificate has been found, the signature of the
+@c actual certificate is checked and in case this fails the error
+@c #code{GPG_ERR_BAD_CERT_CHAIN} is returned.  If the signature checks out, the
+@c maximum cahin length of the issueing certificate is checked as well as
+@c the capiblity of the certificate (i.e. whether he may be used for
+@c certificate signing).  Then the certificate is prepended to our list
+@c representing the certificate chain.  Finally the loop is continued now
+@c with the issuer's certificate as the current certificate.
+@c 
+@c After the end of the loop and if no error as been encountered
+@c (i.e. the certificate chain has been assempled correctly), a check is
+@c done whether any certificate expired or a critical policy has not been
+@c met.  In any of these cases the validation terminates with an
+@c appropriate error. 
+@c 
+@c Finally the function @code{check_revocations} is called to verify no
+@c certificate in the assempled chain has been revoked: This is an
+@c recursive process because a CRL has to be checked for each certificate
+@c in the chain except for the root certificate, of which we already know
+@c that it is trusted and we avoid checking a CRL here due to common
+@c setup problems and the assumption that a revoked root certifcate has
+@c been removed from the list of trusted certificates.
+@c 
+@c 
+@c 
+@c 
+@c @section Looking up certificates through LDAP.
+@c 
+@c This describes the LDAP layer to retrieve certificates.
+@c the functions @code{ca_cert_fetch} and @code{fetch_next_ksba_cert} are
+@c used for this.  The first one starts a search and the second one is
+@c used to retrieve certificate after certificate.
+@c 
+
+  
index c661be1..e54a8ed 100644 (file)
@@ -1,3 +1,8 @@
+2010-06-17  Werner Koch  <wk@g10code.com>
+
+       * gpg.c (main): Use CAST5 as default s2k algo.  The macro
+       USE_CAST5 was only used with GnuPG 1.x.
+
 2010-06-07  Werner Koch  <wk@g10code.com>
 
        * cpr.c: Use estream for status output.
index 2b7b4be..909736d 100644 (file)
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -1976,11 +1976,7 @@ main (int argc, char **argv)
     opt.compress_algo = -1; /* defaults to DEFAULT_COMPRESS_ALGO */
     opt.s2k_mode = 3; /* iterated+salted */
     opt.s2k_count = 0; /* Auto-calibrate when needed.  */
-#ifdef USE_CAST5
     opt.s2k_cipher_algo = CIPHER_ALGO_CAST5;
-#else
-    opt.s2k_cipher_algo = CIPHER_ALGO_3DES;
-#endif
     opt.completes_needed = 1;
     opt.marginals_needed = 3;
     opt.max_cert_depth = 5;
index ebcd305..686baca 100644 (file)
@@ -1,3 +1,19 @@
+2010-06-17  Werner Koch  <wk@g10code.com>
+
+       * import.c (parse_p12): Remove arg retfp.  Use the agent's new
+       import command.
+       (import_one): Adjust call to pkcs12.
+       (store_cert_cb, rsa_key_check): New.
+       (popen_protect_tool): Remove.
+       * minip12.c (parse_bag_encrypted_data, p12_parse): Add arg
+       R_BADPASS.
+       * call-agent.c (gpgsm_agent_ask_passphrase): New.
+       (gpgsm_agent_keywrap_key): New.
+       (struct import_key_parm_s): New.
+       (gpgsm_agent_import_key): New.
+       * minip12.c, minip12.h: Move from ../agent/.
+       * Makefile.am (gpgsm_SOURCES): Add them.
+
 2010-06-11  Marcus Brinkmann  <marcus@g10code.de>
 
        * server.c (cmd_message) [HAVE_W32CE_SYSTEM]: Finish pipe.
index 2754b85..7386deb 100644 (file)
@@ -49,6 +49,7 @@ gpgsm_SOURCES = \
        delete.c \
        certreqgen.c \
        certreqgen-ui.c \
+       minip12.c minip12.h \
        qualified.c
 
 
index 402cb7d..e77f038 100644 (file)
@@ -1,6 +1,6 @@
 /* call-agent.c - Divert GPGSM operations to the agent
  * Copyright (C) 2001, 2002, 2003, 2005, 2007,
- *               2008, 2009 Free Software Foundation, Inc.
+ *               2008, 2009, 2010 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -66,6 +66,14 @@ struct learn_parm_s
   membuf_t *data;
 };
 
+struct import_key_parm_s
+{
+  ctrl_t ctrl;
+  assuan_context_t ctx;
+  const void *key;
+  size_t keylen;
+};
+
 
 \f
 /* Try to connect to the agent via socket or fork it off and work by
@@ -1066,3 +1074,130 @@ gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno)
   return err;
 }
 
+
+\f
+/* Ask for the passphrase (this is used for pkcs#12 import/export.  On
+   success the caller needs to free the string stored at R_PASSPHRASE.
+   On error NULL will be stored at R_PASSPHRASE and an appropriate
+   error code returned. */
+gpg_error_t
+gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg,
+                            char **r_passphrase)
+{
+  gpg_error_t err;
+  char line[ASSUAN_LINELENGTH];
+  char *arg4 = NULL;
+  membuf_t data;
+
+  *r_passphrase = NULL;
+
+  err = start_agent (ctrl);
+  if (err)
+    return err;
+
+  if (desc_msg && *desc_msg && !(arg4 = percent_plus_escape (desc_msg)))
+    return gpg_error_from_syserror ();
+  
+  snprintf (line, DIM(line)-1, "GET_PASSPHRASE --data -- X X X %s", arg4);
+  xfree (arg4);
+
+  init_membuf_secure (&data, 64);
+  err = assuan_transact (agent_ctx, line, 
+                         membuf_data_cb, &data,
+                         default_inq_cb, NULL, NULL, NULL);
+
+  if (err)
+    xfree (get_membuf (&data, NULL));
+  else 
+    {
+      put_membuf (&data, "", 1);
+      *r_passphrase = get_membuf (&data, NULL);
+      if (!*r_passphrase)
+        err = gpg_error_from_syserror ();
+    }
+  return err;
+}
+
+
+\f
+/* Retrieve a key encryption key from the agent.  With FOREXPORT true
+   the key shall be use for export, with false for import.  On success
+   the new key is stored at R_KEY and its length at R_KEKLEN.  */
+gpg_error_t
+gpgsm_agent_keywrap_key (ctrl_t ctrl, int forexport,
+                         void **r_kek, size_t *r_keklen)
+{
+  gpg_error_t err;
+  membuf_t data;
+  size_t len;
+  unsigned char *buf;
+  char line[ASSUAN_LINELENGTH];
+
+  *r_kek = NULL;
+  err = start_agent (ctrl);
+  if (err)
+    return err;
+
+  snprintf (line, DIM(line)-1, "KEYWRAP_KEY %s",
+            forexport? "--export":"--import");
+
+  init_membuf_secure (&data, 64);
+  err = assuan_transact (agent_ctx, line,
+                         membuf_data_cb, &data, 
+                         default_inq_cb, ctrl, NULL, NULL);
+  if (err)
+    {
+      xfree (get_membuf (&data, &len));
+      return err;
+    }
+  buf = get_membuf (&data, &len);
+  if (!buf)
+    return gpg_error_from_syserror ();
+  *r_kek = buf;
+  *r_keklen = len;
+  return 0;
+}
+
+
+
+\f
+/* Handle the inquiry for an IMPORT_KEY command.  */
+static gpg_error_t
+inq_import_key_parms (void *opaque, const char *line)
+{
+  struct import_key_parm_s *parm = opaque; 
+  gpg_error_t err;
+
+  if (!strncmp (line, "KEYDATA", 7) && (line[7]==' '||!line[7]))
+    {
+      assuan_begin_confidential (parm->ctx);
+      err = assuan_send_data (parm->ctx, parm->key, parm->keylen);
+      assuan_end_confidential (parm->ctx);
+    }
+  else
+    err = default_inq_cb (parm->ctrl, line);
+
+  return err; 
+}
+
+
+/* Call the agent to import a key into the agent.  */
+gpg_error_t
+gpgsm_agent_import_key (ctrl_t ctrl, const void *key, size_t keylen)
+{
+  gpg_error_t err;
+  struct import_key_parm_s parm;
+
+  err = start_agent (ctrl);
+  if (err)
+    return err;
+
+  parm.ctrl   = ctrl;
+  parm.ctx    = agent_ctx;
+  parm.key    = key;
+  parm.keylen = keylen;
+
+  err = assuan_transact (agent_ctx, "IMPORT_KEY",
+                         NULL, NULL, inq_import_key_parms, &parm, NULL, NULL);
+  return err;
+}
index 542c292..f065bfa 100644 (file)
@@ -405,6 +405,12 @@ gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc);
 gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl);
 gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip,
                                  char **r_serialno);
+gpg_error_t gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg,
+                                        char **r_passphrase);
+gpg_error_t gpgsm_agent_keywrap_key (ctrl_t ctrl, int forexport,
+                                     void **r_kek, size_t *r_keklen);
+gpg_error_t gpgsm_agent_import_key (ctrl_t ctrl,
+                                    const void *key, size_t keylen);
 
 /*-- call-dirmngr.c --*/
 int gpgsm_dirmngr_isvalid (ctrl_t ctrl,
index 6a012ca..c920ac5 100644 (file)
 #include "i18n.h"
 #include "sysutils.h"
 #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */
+#include "../common/membuf.h"
+#include "minip12.h"
+
+/* The arbitrary limit of one PKCS#12 object.  */
+#define MAX_P12OBJ_SIZE 128 /*kb*/
 
 
 struct stats_s {
@@ -48,8 +53,19 @@ struct stats_s {
  };
 
 
+struct rsa_secret_key_s 
+{
+  gcry_mpi_t n;            /* public modulus */
+  gcry_mpi_t e;            /* public exponent */
+  gcry_mpi_t d;            /* exponent */
+  gcry_mpi_t p;            /* prime  p. */
+  gcry_mpi_t q;            /* prime  q. */
+  gcry_mpi_t u;            /* inverse of p mod q. */
+};
+
+
 static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader,
-                              estream_t *retfp, struct stats_s *stats);
+                              struct stats_s *stats);
 
 
 
@@ -325,51 +341,11 @@ import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
             any = 1;
         }
       else if (ct == KSBA_CT_PKCS12)
-        { /* This seems to be a pkcs12 message.  We use an external
-             tool to parse the message and to store the private keys.
-             We need to use a another reader here to parse the
-             certificate we included in the p12 file; then we continue
-             to look for other pkcs12 files (works only if they are in
-             PEM format. */
-          estream_t certfp;
-          Base64Context b64p12rdr;
-          ksba_reader_t p12rdr;
-          
-          rc = parse_p12 (ctrl, reader, &certfp, stats);
+        { 
+          /* This seems to be a pkcs12 message. */
+          rc = parse_p12 (ctrl, reader, stats);
           if (!rc)
-            {
-              any = 1;
-              
-              es_rewind (certfp);
-              rc = gpgsm_create_reader (&b64p12rdr, ctrl, certfp, 1, &p12rdr);
-              if (rc)
-                {
-                  log_error ("can't create reader: %s\n", gpg_strerror (rc));
-                  es_fclose (certfp);
-                  goto leave;
-                }
-
-              do
-                {
-                  ksba_cert_release (cert); cert = NULL;
-                  rc = ksba_cert_new (&cert);
-                  if (!rc)
-                    {
-                      rc = ksba_cert_read_der (cert, p12rdr);
-                      if (!rc)
-                        check_and_store (ctrl, stats, cert, 0);
-                    }
-                  ksba_reader_clear (p12rdr, NULL, NULL);
-                }
-              while (!rc && !gpgsm_reader_eof_seen (b64p12rdr));
-
-              if (gpg_err_code (rc) == GPG_ERR_EOF)
-                rc = 0;
-              gpgsm_destroy_reader (b64p12rdr);
-              es_fclose (certfp);
-              if (rc)
-                goto leave;
-            }
+            any = 1;
         }
       else if (ct == KSBA_CT_NONE)
         { /* Failed to identify this message - assume a certificate */
@@ -578,213 +554,363 @@ gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
 }
 
 
-/* Fork and exec the protect tool, connect the file descriptor of
-   INFILE to stdin, return a new estream in STATUSFILE, write the
-   output to OUTFILE and the pid of the process in PID.  Returns 0 on
-   success or an error code. */
+/* Check that the RSA secret key SKEY is valid.  Swap parameters to
+   the libgcrypt standard.  */
 static gpg_error_t
-popen_protect_tool (ctrl_t ctrl, const char *pgmname,
-                    estream_t infile, estream_t outfile, 
-                    estream_t *statusfile, pid_t *pid)
+rsa_key_check (struct rsa_secret_key_s *skey)
 {
-  const char *argv[22];
-  int i=0;
-
-  /* Make sure that the agent is running so that the protect tool is
-     able to ask for a passphrase.  This has only an effect under W32
-     where the agent is started on demand; sending a NOP does not harm
-     on other platforms.  This is not really necessary anymore because
-     the protect tool does this now by itself; it does not harm either. */
-  gpgsm_agent_send_nop (ctrl);
-
-  argv[i++] = "--homedir";
-  argv[i++] = opt.homedir;
-  argv[i++] = "--p12-import";
-  argv[i++] = "--store";
-  argv[i++] = "--no-fail-on-exist";
-  argv[i++] = "--enable-status-msg";
-  if (opt.fixed_passphrase)
-    {
-      argv[i++] = "--passphrase";
-      argv[i++] = opt.fixed_passphrase;
-    }
-  if (opt.agent_program)
-    {
-      argv[i++] = "--agent-program";
-      argv[i++] = opt.agent_program;
-    }
-  argv[i++] = "--",
-  argv[i] = NULL;
-  assert (i < sizeof argv);
-
-  return gnupg_spawn_process (pgmname, argv, infile, outfile,
-                              setup_pinentry_env, (128 | 64),
-                              statusfile, pid);
+  int err = 0;
+  gcry_mpi_t t = gcry_mpi_snew (0);
+  gcry_mpi_t t1 = gcry_mpi_snew (0);
+  gcry_mpi_t t2 = gcry_mpi_snew (0);
+  gcry_mpi_t phi = gcry_mpi_snew (0);
+
+  /* Check that n == p * q.  */
+  gcry_mpi_mul (t, skey->p, skey->q);
+  if (gcry_mpi_cmp( t, skey->n) )
+    {
+      log_error ("RSA oops: n != p * q\n");
+      err++;
+    }
+
+  /* Check that p is less than q.  */
+  if (gcry_mpi_cmp (skey->p, skey->q) > 0)
+    {
+      gcry_mpi_t tmp;
+
+      log_info ("swapping secret primes\n");
+      tmp = gcry_mpi_copy (skey->p);
+      gcry_mpi_set (skey->p, skey->q);
+      gcry_mpi_set (skey->q, tmp);
+      gcry_mpi_release (tmp);
+      /* Recompute u.  */
+      gcry_mpi_invm (skey->u, skey->p, skey->q);
+    }
+
+  /* Check that e divides neither p-1 nor q-1.  */
+  gcry_mpi_sub_ui (t, skey->p, 1 );
+  gcry_mpi_div (NULL, t, t, skey->e, 0);
+  if (!gcry_mpi_cmp_ui( t, 0) )
+    {
+      log_error ("RSA oops: e divides p-1\n");
+      err++;
+    }
+  gcry_mpi_sub_ui (t, skey->q, 1);
+  gcry_mpi_div (NULL, t, t, skey->e, 0);
+  if (!gcry_mpi_cmp_ui( t, 0))
+    {
+      log_info ("RSA oops: e divides q-1\n" );
+      err++;
+    }
+
+  /* Check that d is correct.  */
+  gcry_mpi_sub_ui (t1, skey->p, 1);
+  gcry_mpi_sub_ui (t2, skey->q, 1);
+  gcry_mpi_mul (phi, t1, t2);
+  gcry_mpi_invm (t, skey->e, phi);
+  if (gcry_mpi_cmp (t, skey->d))
+    { 
+      /* No: try universal exponent. */
+      gcry_mpi_gcd (t, t1, t2);
+      gcry_mpi_div (t, NULL, phi, t, 0);
+      gcry_mpi_invm (t, skey->e, t);
+      if (gcry_mpi_cmp (t, skey->d))
+        {
+          log_error ("RSA oops: bad secret exponent\n");
+          err++;
+        }
+    }
+
+  /* Check for correctness of u.  */
+  gcry_mpi_invm (t, skey->p, skey->q);
+  if (gcry_mpi_cmp (t, skey->u))
+    {
+      log_info ("RSA oops: bad u parameter\n");
+      err++;
+    }
+
+  if (err)
+    log_info ("RSA secret key check failed\n");
+
+  gcry_mpi_release (t);
+  gcry_mpi_release (t1);
+  gcry_mpi_release (t2);
+  gcry_mpi_release (phi);
+
+  return err? gpg_error (GPG_ERR_BAD_SECKEY):0;
+}
+
+
+/* Object passed to store_cert_cb.  */
+struct store_cert_parm_s
+{
+  gpg_error_t err;        /* First error seen.  */
+  struct stats_s *stats;  /* The stats object.  */
+  ctrl_t ctrl;            /* The control object.  */
+};
+
+/* Helper to store the DER encoded certificate CERTDATA of length
+   CERTDATALEN.  */ 
+static void
+store_cert_cb (void *opaque,
+               const unsigned char *certdata, size_t certdatalen)
+{
+  struct store_cert_parm_s *parm = opaque;
+  gpg_error_t err;
+  ksba_cert_t cert;
+
+  err = ksba_cert_new (&cert);
+  if (err)
+    {
+      if (!parm->err)
+        parm->err = err;
+      return;
+    }
+
+  err = ksba_cert_init_from_mem (cert, certdata, certdatalen);
+  if (err)
+    {
+      log_error ("failed to parse a certificate: %s\n", gpg_strerror (err));
+      if (!parm->err)
+        parm->err = err;
+    }
+  else
+    check_and_store (parm->ctrl, parm->stats, cert, 0);
+  ksba_cert_release (cert);
 }
 
 
 /* Assume that the reader is at a pkcs#12 message and try to import
-   certificates from that stupid format.  We will also store secret
-   keys.  All of the pkcs#12 parsing and key storing is handled by the
-   gpg-protect-tool, we merely have to take care of receiving the
-   certificates.  On success RETFP returns a stream to a temporary
-   file with certificates.  */
+   certificates from that stupid format.  We will transfer secret
+   keys to the agent.  */
 static gpg_error_t
-parse_p12 (ctrl_t ctrl, ksba_reader_t reader,
-           estream_t *retfp, struct stats_s *stats)
+parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats)
 {
-  const char *pgmname;
-  gpg_error_t err = 0, child_err = 0;
-  int c, cont_line;
-  unsigned int pos;
-  estream_t tmpfp;
-  estream_t fp = NULL;
-  estream_t certfp = NULL;
+  gpg_error_t err = 0;
   char buffer[1024];
-  size_t nread;
-  pid_t pid = -1;
+  size_t ntotal, nread;
+  membuf_t p12mbuf;
+  char *p12buffer = NULL;
+  size_t p12buflen;
+  size_t p12bufoff;
+  gcry_mpi_t *kparms = NULL;
+  struct rsa_secret_key_s sk;
+  char *passphrase = NULL;
+  unsigned char *key = NULL;
+  size_t keylen;
+  void *kek = NULL;
+  size_t keklen;
+  unsigned char *wrappedkey = NULL;
+  size_t wrappedkeylen;
+  gcry_cipher_hd_t cipherhd = NULL;
+  gcry_sexp_t s_key = NULL;
+  unsigned char grip[20];
   int bad_pass = 0;
+  int i;
+  struct store_cert_parm_s store_cert_parm;
 
-  if (!opt.protect_tool_program || !*opt.protect_tool_program)
-    pgmname = gnupg_module_name (GNUPG_MODULE_NAME_PROTECT_TOOL);
-  else
-    pgmname = opt.protect_tool_program;
-
-  *retfp = NULL;
+  memset (&store_cert_parm, 0, sizeof store_cert_parm);
+  store_cert_parm.ctrl = ctrl;
+  store_cert_parm.stats = stats;
 
-  /* To avoid an extra feeder process or doing selects and because
-     gpg-protect-tool will anyway parse the entire pkcs#12 message in
-     memory, we simply use tempfiles here and pass them to
-     the gpg-protect-tool. */
-  tmpfp = es_tmpfile ();
-  if (!tmpfp)
-    {
-      err = gpg_error_from_syserror ();
-      log_error (_("error creating temporary file: %s\n"), strerror (errno));
-      goto cleanup;
-    }
+  init_membuf (&p12mbuf, 4096);
+  ntotal = 0;
   while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread)))
     {
-      if (nread && es_fwrite (buffer, nread, 1, tmpfp) != 1)
+      if (ntotal >= MAX_P12OBJ_SIZE*1024)
         {
-          err = gpg_error_from_syserror ();
-          log_error (_("error writing to temporary file: %s\n"),
-                     strerror (errno));
-          goto cleanup;
+          /* Arbitrary limit to avoid DoS attacks. */
+          err = gpg_error (GPG_ERR_TOO_LARGE);
+          log_error ("pkcs#12 object is larger than %dk\n", MAX_P12OBJ_SIZE);
+          break;
         }
+      put_membuf (&p12mbuf, buffer, nread);
+      ntotal += nread;
     }
   if (gpg_err_code (err) == GPG_ERR_EOF)
     err = 0;
+  if (!err)
+    {
+      p12buffer = get_membuf (&p12mbuf, &p12buflen);
+      if (!p12buffer)
+        err = gpg_error_from_syserror ();
+    }
   if (err)
     {
       log_error (_("error reading input: %s\n"), gpg_strerror (err));
-      goto cleanup;
+      goto leave;
     }
 
-  certfp = es_tmpfile ();
-  if (!certfp)
+  /* GnuPG 2.0.4 accidently created binary P12 files with the string
+     "The passphrase is %s encoded.\n\n" prepended to the ASN.1 data.
+     We fix that here.  */
+  if (p12buflen > 29 && !memcmp (p12buffer, "The passphrase is ", 18))
     {
-      err = gpg_error_from_syserror ();
-      log_error (_("error creating temporary file: %s\n"), strerror (errno));
-      goto cleanup;
+      for (p12bufoff=18;
+           p12bufoff < p12buflen && p12buffer[p12bufoff] != '\n';
+           p12bufoff++)
+        ;
+      p12bufoff++;
+      if (p12bufoff < p12buflen && p12buffer[p12bufoff] == '\n')
+        p12bufoff++;
     }
+  else
+    p12bufoff = 0;
 
-  err = popen_protect_tool (ctrl, pgmname, tmpfp, certfp, &fp, &pid);
+
+  err = gpgsm_agent_ask_passphrase
+    (ctrl, _("Please enter the passphrase to unprotect the PKCS#12 object."),
+     &passphrase);
   if (err)
+    goto leave;
+
+  kparms = p12_parse (p12buffer + p12bufoff, p12buflen - p12bufoff,
+                      passphrase, store_cert_cb, &store_cert_parm, &bad_pass);
+
+  xfree (passphrase);
+  passphrase = NULL;
+
+  if (!kparms)
     {
-      pid = -1;
-      goto cleanup;
+      log_error ("error parsing or decrypting the PKCS#12 file\n");
+      err = gpg_error (GPG_ERR_INV_OBJ);
+      goto leave;
     }
-  es_fclose (tmpfp);
-  tmpfp = NULL;
 
-  /* Read stderr of the protect tool. */
-  pos = 0;
-  cont_line = 0;
-  while ((c=es_getc (fp)) != EOF)
+/*    print_mpi ("   n", kparms[0]); */
+/*    print_mpi ("   e", kparms[1]); */
+/*    print_mpi ("   d", kparms[2]); */
+/*    print_mpi ("   p", kparms[3]); */
+/*    print_mpi ("   q", kparms[4]); */
+/*    print_mpi ("dmp1", kparms[5]); */
+/*    print_mpi ("dmq1", kparms[6]); */
+/*    print_mpi ("   u", kparms[7]); */
+
+  sk.n = kparms[0];
+  sk.e = kparms[1];
+  sk.d = kparms[2];
+  sk.q = kparms[3];
+  sk.p = kparms[4];
+  sk.u = kparms[7];
+  err = rsa_key_check (&sk);
+  if (err)
+    goto leave;
+/*    print_mpi ("   n", sk.n); */
+/*    print_mpi ("   e", sk.e); */
+/*    print_mpi ("   d", sk.d); */
+/*    print_mpi ("   p", sk.p); */
+/*    print_mpi ("   q", sk.q); */
+/*    print_mpi ("   u", sk.u); */
+  
+  /* Create an S-expresion from the parameters. */
+  err = gcry_sexp_build (&s_key, NULL,
+                         "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
+                         sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL);
+  for (i=0; i < 8; i++)
+    gcry_mpi_release (kparms[i]);
+  gcry_free (kparms);
+  kparms = NULL;
+  if (err)
     {
-      /* fixme: We could here grep for status information of the
-         protect tool to figure out better error codes for
-         CHILD_ERR. */
-      buffer[pos++] = c;
-      if (pos >= sizeof buffer - 5 || c == '\n')
-        {
-          buffer[pos - (c == '\n')] = 0;
-          if (cont_line)
-            log_printf ("%s", buffer);
-          else
-            {
-              if (!strncmp (buffer, "gpg-protect-tool: [PROTECT-TOOL:] ",34))
-                {
-                  char *p, *pend;
-
-                  p = buffer + 34;
-                  pend = strchr (p, ' ');
-                  if (pend)
-                    *pend = 0;
-                  if ( !strcmp (p, "secretkey-stored"))
-                    {
-                      stats->count++;
-                      stats->secret_read++;
-                      stats->secret_imported++;
-                    }
-                  else if ( !strcmp (p, "secretkey-exists"))
-                    {
-                      stats->count++;
-                      stats->secret_read++;
-                      stats->secret_dups++;
-                    }
-                  else if ( !strcmp (p, "bad-passphrase"))
-                    {
-
-                    }
-                }
-              else 
-                {
-                  log_info ("%s", buffer);
-                  if (!strncmp (buffer, "gpg-protect-tool: "
-                                "possibly bad passphrase given",46))
-                    bad_pass++;
-                }
-            }
-          pos = 0;
-          cont_line = (c != '\n');
-        }
+      log_error ("failed to created S-expression from key: %s\n",
+                 gpg_strerror (err));
+      goto leave;
     }
 
-  if (pos)
+  /* Compute the keygrip. */
+  if (!gcry_pk_get_keygrip (s_key, grip))
     {
-      buffer[pos] = 0;
-      if (cont_line)
-        log_printf ("%s\n", buffer);
-      else
-        log_info ("%s\n", buffer);
+      err = gpg_error (GPG_ERR_GENERAL);
+      log_error ("can't calculate keygrip\n");
+      goto leave;
     }
+  log_printhex ("keygrip=", grip, 20);
 
+  /* Convert to canonical encoding using a function which pads it to a
+     multiple of 64 bits.  We need this padding for AESWRAP.  */
+  err = make_canon_sexp_pad (s_key, &key, &keylen);
+  if (err)
+    {
+      log_error ("error creating canonical S-expression\n");
+      goto leave;
+    }
+  gcry_sexp_release (s_key);
+  s_key = NULL;
 
-  /* If we found no error in the output of the child, setup a suitable
-     error code, which will later be reset if the exit status of the
-     child is 0. */
-  if (!child_err)
-    child_err = gpg_error (GPG_ERR_DECRYPT_FAILED);
+  /* Get the current KEK.  */
+  err = gpgsm_agent_keywrap_key (ctrl, 0, &kek, &keklen);
+  if (err)
+    {
+      log_error ("error getting the KEK: %s\n", gpg_strerror (err));
+      goto leave;
+    }
 
- cleanup:
-  es_fclose (tmpfp);
-  es_fclose (fp);
-  if (pid != -1)
+  /* Wrap the key.  */
+  err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128,
+                          GCRY_CIPHER_MODE_AESWRAP, 0);
+  if (err)
+    goto leave;
+  err = gcry_cipher_setkey (cipherhd, kek, keklen);
+  if (err)
+    goto leave;
+  xfree (kek);
+  kek = NULL;
+
+  wrappedkeylen = keylen + 8;
+  wrappedkey = xtrymalloc (wrappedkeylen);
+  if (!wrappedkey)
     {
-      if (!gnupg_wait_process (pgmname, pid, 0, NULL))
-        child_err = 0;
-      gnupg_release_process (pid);
+      err = gpg_error_from_syserror ();
+      goto leave;
     }
-  if (!err)
-    err = child_err;
+
+  err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen, key, keylen);
   if (err)
+    goto leave;
+  xfree (key);
+  key = NULL;
+  gcry_cipher_close (cipherhd);
+  cipherhd = NULL;
+
+  /* Send the wrapped key to the agent.  */
+  err = gpgsm_agent_import_key (ctrl, wrappedkey, wrappedkeylen);
+  if (!err)
     {
-      es_fclose (certfp);
+      stats->count++;
+      stats->secret_read++;
+      stats->secret_imported++;
     }
-  else
-    *retfp = certfp;
+  else if ( gpg_err_code (err) == GPG_ERR_EEXIST )
+    {
+      err = 0;
+      stats->count++;
+      stats->secret_read++;
+      stats->secret_dups++;
+    }
+
+  /* If we did not get an error from storing the secret key we return
+     a possible error from parsing the certificates.  We do this after
+     storing the secret keys so that a bad certificate does not
+     inhibit our chance to store the secret key.  */
+  if (!err && store_cert_parm.err)
+    err = store_cert_parm.err;
+
+ leave:
+  if (kparms)
+    {
+      for (i=0; i < 8; i++)
+        gcry_mpi_release (kparms[i]);
+      gcry_free (kparms);
+      kparms = NULL;
+    }
+  xfree (key);
+  gcry_sexp_release (s_key);
+  xfree (passphrase);
+  gcry_cipher_close (cipherhd);
+  xfree (wrappedkey);
+  xfree (kek);
+  xfree (get_membuf (&p12mbuf, NULL));
+  xfree (p12buffer);
 
   if (bad_pass)
     {
similarity index 99%
rename from agent/minip12.c
rename to sm/minip12.c
index 6f512e9..f50fbd4 100644 (file)
@@ -608,7 +608,8 @@ static int
 parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
                           int startoffset, size_t *r_consumed, const char *pw,
                           void (*certcb)(void*, const unsigned char*, size_t),
-                          void *certcbarg, gcry_mpi_t **r_result)
+                          void *certcbarg, gcry_mpi_t **r_result,
+                          int *r_badpass)
 {
   struct tag_info ti;
   const unsigned char *p = buffer;
@@ -1003,6 +1004,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
          to check for a bad passphrase; it should therefore not be
          translated or changed. */
       log_error ("possibly bad passphrase given\n");
+      *r_badpass = 1;
     }
   return -1;
 }
@@ -1277,7 +1279,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
 gcry_mpi_t *
 p12_parse (const unsigned char *buffer, size_t length, const char *pw,
            void (*certcb)(void*, const unsigned char*, size_t),
-           void *certcbarg)
+           void *certcbarg, int *r_badpass)
 {
   struct tag_info ti;
   const unsigned char *p = buffer;
@@ -1289,6 +1291,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
   gcry_mpi_t *result = NULL;
   unsigned char *cram_buffer = NULL;
 
+  *r_badpass = 0;
   where = "pfx";
   if (parse_tag (&p, &n, &ti))
     goto bailout;
@@ -1384,7 +1387,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
           where = "bag.encryptedData";
           if (parse_bag_encrypted_data (p, n, (p - p_start), &consumed, pw,
                                         certcb, certcbarg,
-                                        result? NULL : &result))
+                                        result? NULL : &result, r_badpass))
             goto bailout;
           if (lenndef)
             len += consumed;
similarity index 95%
rename from agent/minip12.h
rename to sm/minip12.h
index 998f82f..f2af709 100644 (file)
@@ -25,7 +25,7 @@
 gcry_mpi_t *p12_parse (const unsigned char *buffer, size_t length,
                        const char *pw,
                        void (*certcb)(void*, const unsigned char*, size_t),
-                       void *certcbarg);
+                       void *certcbarg, int *r_badpass);
 
 unsigned char *p12_build (gcry_mpi_t *kparms,
                           unsigned char *cert, size_t certlen,