Fix spelling and grammar.
[gnupg.git] / sm / import.c
index 7fde823..b2ad839 100644 (file)
@@ -1,11 +1,11 @@
 /* import.c - Import certificates
- *     Copyright (C) 2001 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2003, 2004, 2009, 2010 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -14,8 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <unistd.h> 
 #include <time.h>
 #include <assert.h>
+#include <unistd.h>
 
+#include "gpgsm.h"
 #include <gcrypt.h>
 #include <ksba.h>
 
-#include "gpgsm.h"
+#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 reader_cb_parm_s {
-  FILE *fp;
+struct stats_s {
+  unsigned long count;
+  unsigned long imported;
+  unsigned long unchanged;
+  unsigned long not_imported;
+  unsigned long secret_read;
+  unsigned long secret_imported;
+  unsigned long secret_dups;
+ };
+
+
+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 int
-reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
+static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader,
+                              struct stats_s *stats);
+
+
+
+static void
+print_imported_status (ctrl_t ctrl, ksba_cert_t cert, int new_cert)
 {
-  struct reader_cb_parm_s *parm = cb_value;
-  size_t n;
-  int c = 0;
+  char *fpr;
+
+  fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+  if (new_cert)
+    gpgsm_status2 (ctrl, STATUS_IMPORTED, fpr, "[X.509]", NULL);
 
-  *nread = 0;
-  if (!buffer)
-    return -1; /* not supported */
+  gpgsm_status2 (ctrl, STATUS_IMPORT_OK,
+                 new_cert? "1":"0",  fpr, NULL);
 
-  for (n=0; n < count; n++)
+  xfree (fpr);
+}
+
+
+/* Print an IMPORT_PROBLEM status.  REASON is one of:
+   0 := "No specific reason given".
+   1 := "Invalid Certificate".
+   2 := "Issuer Certificate missing".
+   3 := "Certificate Chain too long".
+   4 := "Error storing certificate".
+*/
+static void
+print_import_problem (ctrl_t ctrl, ksba_cert_t cert, int reason)
+{
+  char *fpr = NULL;
+  char buf[25];
+  int i;
+
+  sprintf (buf, "%d", reason);
+  if (cert)
     {
-      c = getc (parm->fp);
-      if (c == EOF)
+      fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
+      /* detetect an error (all high) value */
+      for (i=0; fpr[i] == 'F'; i++)
+        ;
+      if (!fpr[i])
         {
-          if ( ferror (parm->fp) )
-            return -1;
-          if (n)
-            break; /* return what we have before an EOF */
-          return -1;
+          xfree (fpr);
+          fpr = NULL;
         }
-      *(byte *)buffer++ = c;
     }
+  gpgsm_status2 (ctrl, STATUS_IMPORT_PROBLEM, buf, fpr, NULL);
+  xfree (fpr);
+}
+
+
+void
+print_imported_summary (ctrl_t ctrl, struct stats_s *stats)
+{
+  char buf[14*25];
 
-  *nread = n;
-  return 0;
+  if (!opt.quiet)
+    {
+      log_info (_("total number processed: %lu\n"), stats->count);
+      if (stats->imported)
+        {
+          log_info (_("              imported: %lu"), stats->imported );
+          log_printf ("\n");
+       }
+      if (stats->unchanged)
+        log_info (_("             unchanged: %lu\n"), stats->unchanged);
+      if (stats->secret_read)
+        log_info (_("      secret keys read: %lu\n"), stats->secret_read );
+      if (stats->secret_imported)
+        log_info (_("  secret keys imported: %lu\n"), stats->secret_imported );
+      if (stats->secret_dups)
+        log_info (_(" secret keys unchanged: %lu\n"), stats->secret_dups );
+      if (stats->not_imported)
+        log_info (_("          not imported: %lu\n"), stats->not_imported);
+    }
+
+  sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+          stats->count,
+          0l /*stats->no_user_id*/,
+          stats->imported,
+          0l /*stats->imported_rsa*/,
+          stats->unchanged,
+          0l /*stats->n_uids*/,
+          0l /*stats->n_subk*/,
+          0l /*stats->n_sigs*/,
+          0l /*stats->n_revoc*/,
+          stats->secret_read,
+          stats->secret_imported,
+          stats->secret_dups,
+          0l /*stats->skipped_new_keys*/,
+          stats->not_imported
+          );
+  gpgsm_status (ctrl, STATUS_IMPORT_RES, buf);
 }
 
 
+
 static void
-print_integer (unsigned char *p)
+check_and_store (ctrl_t ctrl, struct stats_s *stats,
+                 ksba_cert_t cert, int depth)
 {
-  unsigned long len;
+  int rc;
+
+  if (stats)
+    stats->count++;
+  if ( depth >= 50 )
+    {
+      log_error (_("certificate chain too long\n"));
+      if (stats)
+        stats->not_imported++;
+      print_import_problem (ctrl, cert, 3);
+      return;
+    }
+
+  /* Some basic checks, but don't care about missing certificates;
+     this is so that we are able to import entire certificate chains
+     w/o requiring a special order (i.e. root-CA first).  This used
+     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.
+
+     Optionally we do a full validation in addition to the basic test.
+  */
+  rc = gpgsm_basic_cert_check (ctrl, cert);
+  if (!rc && ctrl->with_validation)
+    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_ISSUER_CERT)))
+    {
+      int existed;
 
-  if (!p)
-    fputs ("none", stdout);
+      if (!keydb_store_cert (cert, 0, &existed))
+        {
+          ksba_cert_t next = NULL;
+
+          if (!existed)
+            {
+              print_imported_status (ctrl, cert, 1);
+              if (stats)
+                stats->imported++;
+            }
+          else
+            {
+              print_imported_status (ctrl, cert, 0);
+              if (stats)
+                stats->unchanged++;
+            }
+
+          if (opt.verbose > 1 && existed)
+            {
+              if (depth)
+                log_info ("issuer certificate already in DB\n");
+              else
+                log_info ("certificate already in DB\n");
+            }
+          else if (opt.verbose && !existed)
+            {
+              if (depth)
+                log_info ("issuer certificate imported\n");
+              else
+                log_info ("certificate imported\n");
+            }
+
+          /* Now lets walk up the chain and import all certificates up
+             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 (ctrl, cert, &next))
+            {
+              check_and_store (ctrl, NULL, next, depth+1);
+              ksba_cert_release (next);
+            }
+        }
+      else
+        {
+          log_error (_("error storing certificate\n"));
+          if (stats)
+            stats->not_imported++;
+          print_import_problem (ctrl, cert, 4);
+        }
+    }
   else
     {
-      len = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
-      for (p+=4; len; len--, p++)
-        printf ("%02X", *p);
+      log_error (_("basic certificate checks failed - not imported\n"));
+      if (stats)
+        stats->not_imported++;
+      /* 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);
     }
 }
 
-static void
-print_time (time_t t)
+
+\f
+
+static int
+import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd)
 {
+  int rc;
+  Base64Context b64reader = NULL;
+  ksba_reader_t reader;
+  ksba_cert_t cert = NULL;
+  ksba_cms_t cms = NULL;
+  estream_t fp = NULL;
+  ksba_content_type_t ct;
+  int any = 0;
+
+  fp = es_fdopen_nc (in_fd, "rb");
+  if (!fp)
+    {
+      rc = gpg_error_from_syserror ();
+      log_error ("fdopen() failed: %s\n", strerror (errno));
+      goto leave;
+    }
 
-  if (!t)
-    fputs ("none", stdout);
-  else if ( t == (time_t)(-1) )
-    fputs ("error", stdout);
-  else
+  rc = gpgsm_create_reader (&b64reader, ctrl, fp, 1, &reader);
+  if (rc)
+    {
+      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
     {
-      struct tm *tp;
+      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)
+            {
+              log_error ("ksba_cms_set_reader_writer failed: %s\n",
+                         gpg_strerror (rc));
+              goto leave;
+            }
+
+          do
+            {
+              rc = ksba_cms_parse (cms, &stopreason);
+              if (rc)
+                {
+                  log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc));
+                  goto leave;
+                }
+
+              if (stopreason == KSBA_SR_BEGIN_DATA)
+                log_info ("not a certs-only message\n");
+            }
+          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);
+              cert = NULL;
+            }
+          if (!i)
+            log_error ("no certificate found\n");
+          else
+            any = 1;
+        }
+      else if (ct == KSBA_CT_PKCS12)
+        {
+          /* This seems to be a pkcs12 message. */
+          rc = parse_p12 (ctrl, reader, stats);
+          if (!rc)
+            any = 1;
+        }
+      else if (ct == KSBA_CT_NONE)
+        { /* Failed to identify this message - assume a certificate */
+
+          rc = ksba_cert_new (&cert);
+          if (rc)
+            goto leave;
 
-      tp = gmtime (&t);
-      printf ("%04d-%02d-%02d %02d:%02d:%02d",
-              1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
-              tp->tm_hour, tp->tm_min, tp->tm_sec);
-      assert (!tp->tm_isdst);
+          rc = ksba_cert_read_der (cert, reader);
+          if (rc)
+            goto leave;
+
+          check_and_store (ctrl, stats, cert, 0);
+          any = 1;
+        }
+      else
+        {
+          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));
+
+ leave:
+  if (any && gpg_err_code (rc) == GPG_ERR_EOF)
+    rc = 0;
+  ksba_cms_release (cms);
+  ksba_cert_release (cert);
+  gpgsm_destroy_reader (b64reader);
+  es_fclose (fp);
+  return rc;
 }
 
-static void
-print_dn (char *p)
+
+\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);
 
-  if (!p)
-    fputs ("error", stdout);
-  else
-    printf ("`%s'", p);
+  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;
 }
 
-static void 
-print_cert (KsbaCert cert)
+
+\f
+int
+gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode)
 {
-  unsigned char *p;
-  char *dn;
-  time_t t;
-    
-  p = ksba_cert_get_serial (cert);
-  fputs ("serial: ", stdout);
-  print_integer (p);
-  ksba_free (p);
-  putchar ('\n');
-
-  t = ksba_cert_get_validity (cert, 0);
-  fputs ("notBefore: ", stdout);
-  print_time (t);
-  putchar ('\n');
-  t = ksba_cert_get_validity (cert, 1);
-  fputs ("notAfter: ", stdout);
-  print_time (t);
-  putchar ('\n');
-    
-  dn = ksba_cert_get_issuer (cert);
-  fputs ("issuer: ", stdout);
-  print_dn (dn);
-  ksba_free (dn);
-  putchar ('\n');
-    
-  dn = ksba_cert_get_subject (cert);
-  fputs ("subject: ", stdout);
-  print_dn (dn);
-  ksba_free (dn);
-  putchar ('\n');
-
-  printf ("hash algo: %d\n", ksba_cert_get_digest_algo (cert));
-}
+  int rc;
+  struct stats_s stats;
 
+  memset (&stats, 0, sizeof stats);
+  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
+     global errorcount) */
+  if (rc && !log_get_errorcount (0))
+    log_error (_("error importing certificate: %s\n"), gpg_strerror (rc));
+  return rc;
+}
 
 
-static MPI
-do_encode_md (GCRY_MD_HD md, int algo, size_t len, unsigned nbits,
-             const byte *asn, size_t asnlen)
+int
+gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
+                    int (*of)(const char *fname))
 {
-    int nframe = (nbits+7) / 8;
-    byte *frame;
-    int i,n;
-    MPI a;
-
-    if( len + asnlen + 4  > nframe )
-       log_bug("can't encode a %d bit MD into a %d bits frame\n",
-                   (int)(len*8), (int)nbits);
-
-    /* We encode the MD in this way:
-     *
-     *    0  A PAD(n bytes)   0  ASN(asnlen bytes)  MD(len bytes)
-     *
-     * PAD consists of FF bytes.
-     */
-    frame = xmalloc (nframe);
-    n = 0;
-    frame[n++] = 0;
-    frame[n++] = 1; /* block type */
-    i = nframe - len - asnlen -3 ;
-    assert( i > 1 );
-    memset( frame+n, 0xff, i ); n += i;
-    frame[n++] = 0;
-    memcpy( frame+n, asn, asnlen ); n += asnlen;
-    memcpy( frame+n, gcry_md_read(md, algo), len ); n += len;
-    assert( n == nframe );
-    gcry_mpi_scan ( &a, GCRYMPI_FMT_USG, frame, &nframe);
-    xfree(frame);
-    return a;
-}
+  int rc = 0;
+  struct stats_s stats;
 
+  memset (&stats, 0, sizeof stats);
 
+  if (!nfiles)
+    rc = import_one (ctrl, &stats, 0);
+  else
+    {
+      for (; nfiles && !rc ; nfiles--, files++)
+        {
+          int fd = of (*files);
+          rc = import_one (ctrl, &stats, fd);
+          close (fd);
+          if (rc == -1)
+            rc = 0;
+        }
+    }
+  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
+     global errorcount) */
+  if (rc && !log_get_errorcount (0))
+    log_error (_("error importing certificate: %s\n"), gpg_strerror (rc));
+  return rc;
+}
 
 
-static void
-check_selfsigned_cert (KsbaCert cert)
+/* Check that the RSA secret key SKEY is valid.  Swap parameters to
+   the libgcrypt standard.  */
+static gpg_error_t
+rsa_key_check (struct rsa_secret_key_s *skey)
 {
-  /* OID for MD5 as defined in PKCS#1 (rfc2313) */
-  static byte asn[18] = /* Object ID is 1.2.840.113549.2.5 (md5) */
-  { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48,
-    0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10
-  };
-
-  GCRY_MD_HD md;
-  int rc, algo;
-  GCRY_MPI frame;
-  char *p;
-  GCRY_SEXP s_sig, s_hash, s_pkey;
-
-  algo = ksba_cert_get_digest_algo (cert);
-  md = gcry_md_open (algo, 0);
-  if (!md)
-    {
-      log_error ("md_open failed: %s\n", gcry_strerror (-1));
-      return;
+  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++;
     }
 
-  gcry_md_start_debug (md, "cert");
-  rc = ksba_cert_hash (cert, gcry_md_write, md);
-  if (rc)
+  /* Check that p is less than q.  */
+  if (gcry_mpi_cmp (skey->p, skey->q) > 0)
     {
-      log_error ("ksba_cert_hash failed: %s\n", ksba_strerror (rc));
-      gcry_md_close (md);
-      return;
+      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++;
     }
-  gcry_md_final (md);
 
-  p = ksba_cert_get_sig_val (cert);
-  printf ("signature: %s\n", p);
+  /* 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++;
+        }
+    }
 
-  rc = gcry_sexp_sscan ( &s_sig, NULL, p, strlen(p));
-  if (rc)
+  /* Check for correctness of u.  */
+  gcry_mpi_invm (t, skey->p, skey->q);
+  if (gcry_mpi_cmp (t, skey->u))
     {
-      log_error ("gcry_sexp_scan failed: %s\n", gcry_strerror (rc));
-      return;
+      log_info ("RSA oops: bad u parameter\n");
+      err++;
     }
-  /*gcry_sexp_dump (s_sig);*/
 
+  if (err)
+    log_info ("RSA secret key check failed\n");
 
-  /* FIXME: need to map the algo to the ASN OID - we assume a fixed
-     one for now */
-  frame = do_encode_md (md, algo, 16, 2048, asn, DIM(asn));
+  gcry_mpi_release (t);
+  gcry_mpi_release (t1);
+  gcry_mpi_release (t2);
+  gcry_mpi_release (phi);
 
-  /* put hash into the S-Exp s_hash */
-  if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) )
-    BUG ();
-  /*fputs ("hash:\n", stderr); gcry_sexp_dump (s_hash);*/
-  _gcry_log_mpidump ("hash", frame);
+  return err? gpg_error (GPG_ERR_BAD_SECKEY):0;
+}
 
-  p = ksba_cert_get_public_key (cert);
-  printf ("public key: %s\n", p);
 
-  rc = gcry_sexp_sscan ( &s_pkey, NULL, p, strlen(p));
-  if (rc)
+/* 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)
     {
-      log_error ("gcry_sexp_scan failed: %s\n", gcry_strerror (rc));
+      if (!parm->err)
+        parm->err = err;
       return;
     }
-  /*gcry_sexp_dump (s_pkey);*/
-  
-  rc = gcry_pk_verify (s_sig, s_hash, s_pkey);
-  log_error ("gcry_pk_verify: %s\n", gcry_strerror (rc));
 
+  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);
 }
 
 
-\f
-int
-gpgsm_import (int in_fd)
+/* Assume that the reader is at a pkcs#12 message and try to import
+   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, struct stats_s *stats)
 {
-  int rc;
-  KsbaReader reader = NULL;
-  KsbaCert cert = NULL;
-  struct reader_cb_parm_s rparm;
+  gpg_error_t err = 0;
+  char buffer[1024];
+  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;
+
+  memset (&store_cert_parm, 0, sizeof store_cert_parm);
+  store_cert_parm.ctrl = ctrl;
+  store_cert_parm.stats = stats;
+
+  init_membuf (&p12mbuf, 4096);
+  ntotal = 0;
+  while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread)))
+    {
+      if (ntotal >= MAX_P12OBJ_SIZE*1024)
+        {
+          /* 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 leave;
+    }
+
+  /* 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))
+    {
+      for (p12bufoff=18;
+           p12bufoff < p12buflen && p12buffer[p12bufoff] != '\n';
+           p12bufoff++)
+        ;
+      p12bufoff++;
+      if (p12bufoff < p12buflen && p12buffer[p12bufoff] == '\n')
+        p12bufoff++;
+    }
+  else
+    p12bufoff = 0;
+
 
-  memset (&rparm, 0, sizeof rparm);
+  err = gpgsm_agent_ask_passphrase
+    (ctrl,
+     i18n_utf8 ("Please enter the passphrase to unprotect the PKCS#12 object."),
+     0, &passphrase);
+  if (err)
+    goto leave;
 
-  rparm.fp = fdopen ( dup (in_fd), "rb");
-  if (!rparm.fp)
+  kparms = p12_parse (p12buffer + p12bufoff, p12buflen - p12bufoff,
+                      passphrase, store_cert_cb, &store_cert_parm, &bad_pass);
+
+  xfree (passphrase);
+  passphrase = NULL;
+
+  if (!kparms)
     {
-      log_error ("fdopen() failed: %s\n", strerror (errno));
-      rc = seterr (IO_Error);
+      log_error ("error parsing or decrypting the PKCS#12 file\n");
+      err = gpg_error (GPG_ERR_INV_OBJ);
       goto leave;
     }
 
-  /* setup a skaba reader which uses a callback function so that we can 
-     strip off a base64 encoding when necessary */
-  reader = ksba_reader_new ();
-  if (!reader)
+/*    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)
     {
-      rc = seterr (Out_Of_Core);
+      log_error ("failed to create S-expression from key: %s\n",
+                 gpg_strerror (err));
       goto leave;
     }
 
-  rc = ksba_reader_set_cb (reader, reader_cb, &rparm );
-  if (rc)
+  /* Compute the keygrip. */
+  if (!gcry_pk_get_keygrip (s_key, grip))
     {
-      ksba_reader_release (reader);
-      rc = map_ksba_err (rc);
+      err = gpg_error (GPG_ERR_GENERAL);
+      log_error ("can't calculate keygrip\n");
       goto leave;
     }
+  log_printhex ("keygrip=", grip, 20);
 
-  cert = ksba_cert_new ();
-  if (!cert)
+  /* 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)
     {
-      rc = seterr (Out_Of_Core);
+      log_error ("error creating canonical S-expression\n");
       goto leave;
     }
+  gcry_sexp_release (s_key);
+  s_key = NULL;
 
-  rc = ksba_cert_read_der (cert, reader);
-  if (rc)
+  /* 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;
+    }
+
+  /* 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)
     {
-      rc = map_ksba_err (rc);
+      err = gpg_error_from_syserror ();
       goto leave;
     }
 
-  print_cert (cert);
-  check_selfsigned_cert (cert);
+  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)
+    {
+      stats->count++;
+      stats->secret_read++;
+      stats->secret_imported++;
+    }
+  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:
-  ksba_cert_release (cert);
-  ksba_reader_release (reader);
-  if (rparm.fp)
-    fclose (rparm.fp);
-  return rc;
-}
+  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
+         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;
+}