Fix spelling and grammar.
[gnupg.git] / sm / import.c
index deebccf..b2ad839 100644 (file)
@@ -1,5 +1,5 @@
 /* import.c - Import certificates
- *     Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2003, 2004, 2009, 2010 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include "keydb.h"
 #include "exechelp.h"
 #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 {
   unsigned long count;
@@ -45,7 +53,18 @@ struct stats_s {
  };
 
 
-static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader, FILE **retfp,
+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,
                               struct stats_s *stats);
 
 
@@ -54,12 +73,12 @@ static void
 print_imported_status (ctrl_t ctrl, ksba_cert_t cert, int new_cert)
 {
   char *fpr;
-     
+
   fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
   if (new_cert)
     gpgsm_status2 (ctrl, STATUS_IMPORTED, fpr, "[X.509]", NULL);
 
-  gpgsm_status2 (ctrl, STATUS_IMPORT_OK, 
+  gpgsm_status2 (ctrl, STATUS_IMPORT_OK,
                  new_cert? "1":"0",  fpr, NULL);
 
   xfree (fpr);
@@ -106,7 +125,7 @@ print_imported_summary (ctrl_t ctrl, struct stats_s *stats)
   if (!opt.quiet)
     {
       log_info (_("total number processed: %lu\n"), stats->count);
-      if (stats->imported) 
+      if (stats->imported)
         {
           log_info (_("              imported: %lu"), stats->imported );
           log_printf ("\n");
@@ -167,15 +186,16 @@ check_and_store (ctrl_t ctrl, struct stats_s *stats,
      to be different but because gpgsm_verify even imports
      certificates without any checks, it doesn't matter much and the
      code gets much cleaner.  A housekeeping function to remove
-     certificates w/o an anchor would be nice, though. 
-     
+     certificates w/o an anchor would be nice, though.
+
      Optionally we do a full validation in addition to the basic test.
   */
-  rc = gpgsm_basic_cert_check (cert);
+  rc = gpgsm_basic_cert_check (ctrl, cert);
   if (!rc && ctrl->with_validation)
-    rc = gpgsm_validate_chain (ctrl, cert, NULL, 0, NULL, 0);
+    rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL);
   if (!rc || (!ctrl->with_validation
-              && gpg_err_code (rc) == GPG_ERR_MISSING_CERT) )
+              && (gpg_err_code (rc) == GPG_ERR_MISSING_CERT
+                  || gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT)))
     {
       int existed;
 
@@ -195,7 +215,7 @@ check_and_store (ctrl_t ctrl, struct stats_s *stats,
               if (stats)
                 stats->unchanged++;
             }
-            
+
           if (opt.verbose > 1 && existed)
             {
               if (depth)
@@ -215,7 +235,7 @@ check_and_store (ctrl_t ctrl, struct stats_s *stats,
              the chain.  This is required in case we already stored
              parent certificates in the ephemeral keybox.  Do not
              update the statistics, though. */
-          if (!gpgsm_walk_cert_chain (cert, &next))
+          if (!gpgsm_walk_cert_chain (ctrl, cert, &next))
             {
               check_and_store (ctrl, NULL, next, depth+1);
               ksba_cert_release (next);
@@ -234,9 +254,14 @@ check_and_store (ctrl_t ctrl, struct stats_s *stats,
       log_error (_("basic certificate checks failed - not imported\n"));
       if (stats)
         stats->not_imported++;
-      print_import_problem (ctrl, cert,
-                            gpg_err_code (rc) == GPG_ERR_MISSING_CERT? 2 :
-                            gpg_err_code (rc) == GPG_ERR_BAD_CERT?     1 : 0);
+      /* We keep the test for GPG_ERR_MISSING_CERT only in case
+         GPG_ERR_MISSING_CERT has been used instead of the newer
+         GPG_ERR_MISSING_ISSUER_CERT.  */
+      print_import_problem
+        (ctrl, cert,
+         gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT? 2 :
+         gpg_err_code (rc) == GPG_ERR_MISSING_CERT? 2 :
+         gpg_err_code (rc) == GPG_ERR_BAD_CERT?     1 : 0);
     }
 }
 
@@ -251,14 +276,14 @@ import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
   ksba_reader_t reader;
   ksba_cert_t cert = NULL;
   ksba_cms_t cms = NULL;
-  FILE *fp = NULL;
+  estream_t fp = NULL;
   ksba_content_type_t ct;
   int any = 0;
 
-  fp = fdopen ( dup (in_fd), "rb");
+  fp = es_fdopen_nc (in_fd, "rb");
   if (!fp)
     {
-      rc = gpg_error (gpg_err_code_from_errno (errno));
+      rc = gpg_error_from_syserror ();
       log_error ("fdopen() failed: %s\n", strerror (errno));
       goto leave;
     }
@@ -269,25 +294,25 @@ import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
       log_error ("can't create reader: %s\n", gpg_strerror (rc));
       goto leave;
     }
-  
-  
+
+
   /* We need to loop here to handle multiple PEM objects in one
      file. */
   do
     {
       ksba_cms_release (cms); cms = NULL;
       ksba_cert_release (cert); cert = NULL;
-      
+
       ct = ksba_cms_identify (reader);
       if (ct == KSBA_CT_SIGNED_DATA)
         { /* This is probably a signed-only message - import the certs */
           ksba_stop_reason_t stopreason;
           int i;
-          
+
           rc = ksba_cms_new (&cms);
           if (rc)
             goto leave;
-          
+
           rc = ksba_cms_set_reader_writer (cms, reader, NULL);
           if (rc)
             {
@@ -296,7 +321,7 @@ import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
               goto leave;
             }
 
-          do 
+          do
             {
               rc = ksba_cms_parse (cms, &stopreason);
               if (rc)
@@ -308,12 +333,12 @@ import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
               if (stopreason == KSBA_SR_BEGIN_DATA)
                 log_info ("not a certs-only message\n");
             }
-          while (stopreason != KSBA_SR_READY);   
-      
+          while (stopreason != KSBA_SR_READY);
+
           for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++)
             {
               check_and_store (ctrl, stats, cert, 0);
-              ksba_cert_release (cert); 
+              ksba_cert_release (cert);
               cert = NULL;
             }
           if (!i)
@@ -322,51 +347,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. */
-          FILE *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;
-              
-              rewind (certfp);
-              rc = gpgsm_create_reader (&b64p12rdr, ctrl, certfp, 1, &p12rdr);
-              if (rc)
-                {
-                  log_error ("can't create reader: %s\n", gpg_strerror (rc));
-                  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);
-              fclose (certfp);
-              if (rc)
-                goto leave;
-            }
+            any = 1;
         }
       else if (ct == KSBA_CT_NONE)
         { /* Failed to identify this message - assume a certificate */
@@ -387,7 +372,7 @@ import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
           log_error ("can't extract certificates from input\n");
           rc = gpg_error (GPG_ERR_NO_DATA);
         }
-      
+
       ksba_reader_clear (reader, NULL, NULL);
     }
   while (!gpgsm_reader_eof_seen (b64reader));
@@ -398,20 +383,141 @@ import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
   ksba_cms_release (cms);
   ksba_cert_release (cert);
   gpgsm_destroy_reader (b64reader);
-  if (fp)
-    fclose (fp);
+  es_fclose (fp);
   return rc;
 }
 
 
+\f
+/* Re-import certifciates.  IN_FD is a list of linefeed delimited
+   fingerprints t re-import.  The actual re-import is done by clearing
+   the ephemeral flag.  */
+static int
+reimport_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
+{
+  gpg_error_t err = 0;
+  estream_t fp = NULL;
+  char line[100];  /* Sufficient for a fingerprint.  */
+  KEYDB_HANDLE kh;
+  KEYDB_SEARCH_DESC desc;
+  ksba_cert_t cert = NULL;
+  unsigned int flags;
+
+  kh = keydb_new (0);
+  if (!kh)
+    {
+      err = gpg_error (GPG_ERR_ENOMEM);;
+      log_error (_("failed to allocate keyDB handle\n"));
+      goto leave;
+    }
+  keydb_set_ephemeral (kh, 1);
+
+  fp = es_fdopen_nc (in_fd, "r");
+  if (!fp)
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("es_fdopen(%d) failed: %s\n", in_fd, gpg_strerror (err));
+      goto leave;
+    }
+
+  while (es_fgets (line, DIM(line)-1, fp) )
+    {
+      if (*line && line[strlen(line)-1] != '\n')
+        {
+          err = gpg_error (GPG_ERR_LINE_TOO_LONG);
+          goto leave;
+       }
+      trim_spaces (line);
+      if (!*line)
+        continue;
+
+      stats->count++;
+
+      err = classify_user_id (line, &desc, 0);
+      if (err)
+        {
+          print_import_problem (ctrl, NULL, 0);
+          stats->not_imported++;
+          continue;
+        }
+
+      keydb_search_reset (kh);
+      err = keydb_search (kh, &desc, 1);
+      if (err)
+        {
+          print_import_problem (ctrl, NULL, 0);
+          stats->not_imported++;
+          continue;
+        }
+
+      ksba_cert_release (cert);
+      cert = NULL;
+      err = keydb_get_cert (kh, &cert);
+      if (err)
+        {
+          log_error ("keydb_get_cert() failed: %s\n", gpg_strerror (err));
+          print_import_problem (ctrl, NULL, 1);
+          stats->not_imported++;
+          continue;
+        }
+
+      err = keydb_get_flags (kh, KEYBOX_FLAG_BLOB, 0, &flags);
+      if (err)
+        {
+          log_error (_("error getting stored flags: %s\n"), gpg_strerror (err));
+          print_imported_status (ctrl, cert, 0);
+          stats->not_imported++;
+          continue;
+        }
+      if ( !(flags & KEYBOX_FLAG_BLOB_EPHEMERAL) )
+        {
+          print_imported_status (ctrl, cert, 0);
+          stats->unchanged++;
+          continue;
+        }
+
+      err = keydb_set_cert_flags (cert, 1, KEYBOX_FLAG_BLOB, 0,
+                                  KEYBOX_FLAG_BLOB_EPHEMERAL, 0);
+      if (err)
+        {
+          log_error ("clearing ephemeral flag failed: %s\n",
+                     gpg_strerror (err));
+          print_import_problem (ctrl, cert, 0);
+          stats->not_imported++;
+          continue;
+        }
+
+      print_imported_status (ctrl, cert, 1);
+      stats->imported++;
+    }
+  err = 0;
+  if (es_ferror (fp))
+    {
+      err = gpg_error_from_syserror ();
+      log_error ("error reading fd %d: %s\n", in_fd, gpg_strerror (err));
+      goto leave;
+    }
+
+ leave:
+  ksba_cert_release (cert);
+  keydb_release (kh);
+  es_fclose (fp);
+  return err;
+}
+
+
+\f
 int
-gpgsm_import (ctrl_t ctrl, int in_fd)
+gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode)
 {
   int rc;
   struct stats_s stats;
 
   memset (&stats, 0, sizeof stats);
-  rc = import_one (ctrl, &stats, in_fd);
+  if (reimport_mode)
+    rc = reimport_one (ctrl, &stats, in_fd);
+  else
+    rc = import_one (ctrl, &stats, in_fd);
   print_imported_summary (ctrl, &stats);
   /* If we never printed an error message do it now so that a command
      line invocation will return with an error (log_error keeps a
@@ -430,7 +536,7 @@ gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
   struct stats_s stats;
 
   memset (&stats, 0, sizeof stats);
-  
+
   if (!nfiles)
     rc = import_one (ctrl, &stats, 0);
   else
@@ -454,209 +560,375 @@ gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
 }
 
 
-/* Fork and exec the protecttool, connect the file descriptor of
-   INFILE to stdin, return a new stream 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 (const char *pgmname,
-                    FILE *infile, FILE *outfile, FILE **statusfile, pid_t *pid)
+rsa_key_check (struct rsa_secret_key_s *skey)
 {
-  const char *argv[20];
-  int i=0;
-
-  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;
-    }
-  argv[i++] = "--",
-  argv[i] = NULL;
-  assert (i < sizeof argv);
-
-  return gnupg_spawn_process (pgmname, argv, infile, outfile,
-                              setup_pinentry_env,
-                              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 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,
-           FILE **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;
-  FILE *tmpfp, *certfp = NULL, *fp = 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 = 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 && 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 = tmpfile ();
-  if (!certfp)
+  /* GnuPG 2.0.4 accidentally 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 = gpgsm_agent_ask_passphrase
+    (ctrl,
+     i18n_utf8 ("Please enter the passphrase to unprotect the PKCS#12 object."),
+     0, &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)
+    {
+      log_error ("error parsing or decrypting the PKCS#12 file\n");
+      err = gpg_error (GPG_ERR_INV_OBJ);
+      goto leave;
     }
 
-  err = popen_protect_tool (pgmname, tmpfp, certfp, &fp, &pid);
+/*    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)
     {
-      pid = -1;
-      goto cleanup;
+      log_error ("failed to create S-expression from key: %s\n",
+                 gpg_strerror (err));
+      goto leave;
     }
-  fclose (tmpfp);
-  tmpfp = NULL;
 
-  /* Read stderr of the protect tool. */
-  pos = 0;
-  cont_line = 0;
-  while ((c=getc (fp)) != EOF)
+  /* Compute the keygrip. */
+  if (!gcry_pk_get_keygrip (s_key, grip))
     {
-      /* 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');
-        }
+      err = gpg_error (GPG_ERR_GENERAL);
+      log_error ("can't calculate keygrip\n");
+      goto leave;
     }
+  log_printhex ("keygrip=", grip, 20);
 
-  if (pos)
+  /* 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, 1, &key, &keylen);
+  if (err)
     {
-      buffer[pos] = 0;
-      if (cont_line)
-        log_printf ("%s\n", buffer);
-      else
-        log_info ("%s\n", buffer);
+      log_error ("error creating canonical S-expression\n");
+      goto leave;
     }
+  gcry_sexp_release (s_key);
+  s_key = NULL;
 
+  /* 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;
+    }
 
-  /* If we found no error in the output of the cild, 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);
+  /* 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;
 
- cleanup:
-  if (tmpfp)
-    fclose (tmpfp);
-  if (fp)
-    fclose (fp);
-  if (pid != -1)
+  wrappedkeylen = keylen + 8;
+  wrappedkey = xtrymalloc (wrappedkeylen);
+  if (!wrappedkey)
     {
-      if (!gnupg_wait_process (pgmname, pid))
-        child_err = 0;
+      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)
     {
-      if (certfp)
-        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)
     {
       /* We only write a plain error code and not direct
          BAD_PASSPHRASE because the pkcs12 parser might issue this
-         message multiple times, BAd_PASSPHRASE in general requires a
+         message multiple times, BAD_PASSPHRASE in general requires a
          keyID and parts of the import might actually succeed so that
          IMPORT_PROBLEM is also not appropriate. */
       gpgsm_status_with_err_code (ctrl, STATUS_ERROR,
                                   "import.parsep12", GPG_ERR_BAD_PASSPHRASE);
     }
-  
+
   return err;
 }