* protect-tool.c: New options --have-cert and --prompt.
authorWerner Koch <wk@gnupg.org>
Thu, 19 Feb 2004 16:26:32 +0000 (16:26 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 19 Feb 2004 16:26:32 +0000 (16:26 +0000)
(export_p12_file): Read a certificate from STDIN and pass it to
p12_build.  Detect a keygrip and construct the filename in that
case.  Unprotcet a key if needed.  Print error messages for key
formats we can't handle.
(release_passphrase): New.
(get_passphrase): New arg PROMPTNO. Return the allocated
string. Changed all callers.

* minip12.c: Revamped the build part.
(p12_build): New args CERT and CERTLEN.

* simple-pwquery.c (agent_open): Don't mangle INFOSTR.

* export.c (export_p12, popen_protect_tool)
(gpgsm_p12_export): New.
* gpgsm.c (main): New command --export-secret-key-p12.

14 files changed:
NEWS
agent/ChangeLog
agent/minip12.c
agent/minip12.h
agent/protect-tool.c
common/ChangeLog
common/simple-pwquery.c
doc/gpgsm.texi
scd/command.c
sm/ChangeLog
sm/export.c
sm/gpgsm.c
sm/gpgsm.h
sm/import.c

diff --git a/NEWS b/NEWS
index c0a8d60..3d6837d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,8 @@ Noteworthy changes in version 1.9.5
 
  * [gpgsm] The --import command is now able to autodetect pkcs#12
    files and import secret and private keys from this file format.
+   A new command --export-secret-key-p12 is provided to allow
+   exporting of secret keys in PKCS\#12 format.
 
  * [gpgsm] The pinentry will now present a description of the key for
    whom the passphrase is requests.
index ffd48f5..241a609 100644 (file)
@@ -1,3 +1,17 @@
+2004-02-19  Werner Koch  <wk@gnupg.org>
+
+       * protect-tool.c: New options --have-cert and --prompt.
+       (export_p12_file): Read a certificate from STDIN and pass it to
+       p12_build.  Detect a keygrip and construct the filename in that
+       case.  Unprotcet a key if needed.  Print error messages for key
+       formats we can't handle.
+       (release_passphrase): New.
+       (get_passphrase): New arg PROMPTNO. Return the allocated
+       string. Changed all callers.
+
+       * minip12.c: Revamped the build part.
+       (p12_build): New args CERT and CERTLEN.  
+
 2004-02-18  Werner Koch  <wk@gnupg.org>
 
        * protect-tool.c (main): Setup the used character set.
index 13b6aa3..e32a40d 100644 (file)
@@ -106,12 +106,19 @@ static unsigned char const oid_rsaEncryption[9] = {
   0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
 
 
-static unsigned char const data_3desiter1024[30] = {
+static unsigned char const data_3desiter2048[30] = {
   0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86,
   0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03, 0x30, 0x0E, 
   0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-  0xFF, 0xFF, 0x02, 0x02, 0x04, 0x00 };
-#define DATA_3DESITER1024_SALT_OFF  18
+  0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 };
+#define DATA_3DESITER2048_SALT_OFF  18
+
+static unsigned char const data_rc2iter2048[30] = {
+  0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86,
+  0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x06, 0x30, 0x0E, 
+  0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+  0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 };
+#define DATA_RC2ITER2048_SALT_OFF  18
 
 
 struct buffer_s 
@@ -346,17 +353,22 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, int iter,
   if (rc)
     {
       log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(rc));
+      wipememory (buffer, length);
       return;
     }
   if (set_key_iv (chd, salt, iter, pw,
                   cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24))
-    goto leave;
+    {
+      wipememory (buffer, length);
+      goto leave;
+    }
 
   rc = encrypt? gcry_cipher_encrypt (chd, buffer, length, NULL, 0)
               : gcry_cipher_decrypt (chd, buffer, length, NULL, 0);
 
   if (rc)
     {
+      wipememory (buffer, length);
       log_error ( "en/de-crytion failed: %s\n", gpg_strerror (rc));
       goto leave;
     }
@@ -474,6 +486,13 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
   startoffset = 0;
   buffer = p = plain;
 
+/*   { */
+/*     FILE *fp = fopen ("tmp-rc2-plain.der", "wb"); */
+/*     if (!fp || fwrite (p, n, 1, fp) != 1) */
+/*       exit (2); */
+/*     fclose (fp); */
+/*   } */
+
   where = "outer.outer.seq";
   if (parse_tag (&p, &n, &ti))
     goto bailout;
@@ -708,13 +727,6 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
   startoffset = 0;
   buffer = p = plain;
 
-/*   { */
-/*     FILE *fp = fopen ("tmp-3des-plain.der", "wb"); */
-/*     if (!fp || fwrite (p, n, 1, fp) != 1) */
-/*       exit (2); */
-/*     fclose (fp); */
-/*   } */
-
   where = "decrypted-text";
   if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
     goto bailout;
@@ -970,37 +982,48 @@ create_final (struct buffer_s *sequences, size_t *r_length)
 {
   int i;
   size_t needed = 0;
-  size_t n, outseqlen, notsooutseqlen, out0taglen, octstrlen, inseqlen;
+  size_t len[8], n;
   unsigned char *result, *p;
   size_t resultlen;
 
+  /* 8 steps to create the pkcs#12 Krampf. */
+
+  /* 7. All the buffers. */
   for (i=0; sequences[i].buffer; i++)
     needed += sequences[i].length;
-  /* This goes into a sequences. */
-  inseqlen = needed;
+
+  /* 6. This goes into a sequences. */
+  len[6] = needed;
   n = compute_tag_length (needed);
   needed += n;
-  /* And encapsulate all in an octet string. */
-  octstrlen = needed;
+
+  /* 5. Encapsulate all in an octet string. */
+  len[5] = needed;
   n = compute_tag_length (needed);
   needed += n;
-  /* And tag it with [0]. */
-  out0taglen = needed;
+
+  /* 4. And tag it with [0]. */
+  len[4] = needed;
   n = compute_tag_length (needed);
   needed += n;
-  /* Prepend an data OID. */
+
+  /* 3. Prepend an data OID. */
   needed += 2 + DIM (oid_data);
-  /* This all into a sequences. */
-  notsooutseqlen = needed;
+
+  /* 2. Put all into a sequences. */
+  len[2] = needed;
   n = compute_tag_length (needed);
   needed += n;
-  /* Prepend the version integer 3. */
+
+  /* 1. Prepend the version integer 3. */
   needed += 3;
-  /* And the final sequence. */
-  outseqlen = needed;
+
+  /* 0. And the final outer sequence. */
+  len[0] = needed;
   n = compute_tag_length (needed);
   needed += n;
 
+  /* Allocate a buffer. */
   result = gcry_malloc (needed);
   if (!result)
     {
@@ -1009,25 +1032,32 @@ create_final (struct buffer_s *sequences, size_t *r_length)
     }
   p = result;
 
-  /* Store the very outer sequence. */
-  p = store_tag_length (p, TAG_SEQUENCE, outseqlen);
-  /* Store the version integer 3. */
+  /* 0. Store the very outer sequence. */
+  p = store_tag_length (p, TAG_SEQUENCE, len[0]);
+
+  /* 1. Store the version integer 3. */
   *p++ = TAG_INTEGER;
   *p++ = 1; 
-  *p++ = 3; 
-  /* Store another sequence. */
-  p = store_tag_length (p, TAG_SEQUENCE, notsooutseqlen);
-  /* Store the data OID. */
+  *p++ = 3;
+  /* 2. Store another sequence. */
+  p = store_tag_length (p, TAG_SEQUENCE, len[2]);
+
+  /* 3. Store the data OID. */
   p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
   memcpy (p, oid_data, DIM (oid_data)); 
   p += DIM (oid_data); 
-  /* Next comes a context tag. */
-  p = store_tag_length (p, 0xa0, out0taglen);
-  /* And an octet string. */
-  p = store_tag_length (p, TAG_OCTET_STRING, octstrlen);
-  /* And the inner sequence. */
-  p = store_tag_length (p, TAG_SEQUENCE, inseqlen);
-  /* And append all the buffers. */
+
+  /* 4. Next comes a context tag. */
+  p = store_tag_length (p, 0xa0, len[4]);
+
+  /* 5. And an octet string. */
+  p = store_tag_length (p, TAG_OCTET_STRING, len[5]);
+
+  /* 6. And the inner sequence. */
+  p = store_tag_length (p, TAG_SEQUENCE, len[6]);
+
+  /* 7. Append all the buffers. */
   for (i=0; sequences[i].buffer; i++)
     {
       memcpy (p, sequences[i].buffer, sequences[i].length);
@@ -1044,20 +1074,38 @@ create_final (struct buffer_s *sequences, size_t *r_length)
 }
 
 
-/* Expect the RSA key parameters in KPARMS and a password in
-   PW. Create a PKCS structure from it and return it as well as the
-   length in R_LENGTH; return NULL in case of an error. */
-unsigned char * 
-p12_build (gcry_mpi_t *kparms, const char *pw, size_t *r_length)
+/* Build a DER encoded SEQUENCE with the key:
+
+   SEQUENCE {
+     INTEGER 0
+     SEQUENCE {
+       OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
+       NULL
+       }
+     OCTET STRING, encapsulates {
+       SEQUENCE {
+         INTEGER 0
+         INTEGER
+         INTEGER 
+         INTEGER
+         INTEGER
+         INTEGER
+         INTEGER
+         INTEGER
+         INTEGER
+         }
+       }
+     }
+*/  
+  
+static unsigned char * 
+build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
 {
   int rc, i;
   size_t needed, n;
-  unsigned char *plain, *p, *cipher;
-  size_t plainlen, cipherlen;
+  unsigned char *plain, *p;
+  size_t plainlen;
   size_t outseqlen, oidseqlen, octstrlen, inseqlen;
-  size_t out0taglen, in0taglen, outoctstrlen;
-  size_t aseq1len, aseq2len, aseq3len;
-  char salt[8];
 
   needed = 3; /* The version(?) integer of value 0. */
   for (i=0; kparms[i]; i++)
@@ -1165,105 +1213,380 @@ p12_build (gcry_mpi_t *kparms, const char *pw, size_t *r_length)
   for (;(plainlen % 8); plainlen++)
     *p++ = n;
 
-/*   { */
-/*     FILE *fp = fopen("inner-out.der", "wb"); */
-/*     fwrite (plain, 1, plainlen, fp); */
-/*     fclose (fp); */
-/*   } */
+  *r_length = plainlen;
+  return plain;
+}
+
+
+
+static unsigned char *
+build_key_bag (unsigned char *buffer, size_t buflen, char *salt,
+               size_t *r_length)
+{
+  size_t len[11], needed;
+  unsigned char *p, *keybag;
+  size_t keybaglen;
+
+  /* Walk 11 steps down to collect the info: */
+
+  /* 10. The data goes into an octet string. */
+  needed = compute_tag_length (buflen);
+  needed += buflen;
 
+  /* 9. Prepend the algorithm identifier. */
+  needed += DIM (data_3desiter2048);
 
-  /* Encrypt it and prepend a lot of stupid things. */
-  gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
-  crypt_block (plain, plainlen, salt, 1024, pw, GCRY_CIPHER_3DES, 1);
-  /* the data goes into an octet string. */
-  needed = compute_tag_length (plainlen);
-  needed += plainlen;
-  /* we prepend the the algorithm identifier (we use a pre-encoded one)*/
-  needed += DIM (data_3desiter1024);
-  /* we put a sequence around. */
-  aseq3len = needed;
+  /* 8. Put a sequence around. */
+  len[8] = needed;
   needed += compute_tag_length (needed);
-  /* Prepend it with a [0] tag. */
-  in0taglen = needed;
+
+  /* 7. Prepend a [0] tag. */
+  len[7] = needed;
   needed += compute_tag_length (needed);
-  /* Prepend that shroudedKeyBag OID. */
+
+  /* 6. Prepend the shroudedKeyBag OID. */
   needed += 2 + DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag);
-  /* Put it all into two sequence. */
-  aseq2len = needed;
+
+  /* 5+4. Put all into two sequences. */
+  len[5] = needed;
   needed += compute_tag_length ( needed);
-  aseq1len = needed;
+  len[4] = needed;
   needed += compute_tag_length (needed);
-  /* This all goes into an octet string. */
-  outoctstrlen = needed;
+
+  /* 3. This all goes into an octet string. */
+  len[3] = needed;
   needed += compute_tag_length (needed);
-  /* Prepend it with a [0] tag. */
-  out0taglen = needed;
+
+  /* 2. Prepend another [0] tag. */
+  len[2] = needed;
   needed += compute_tag_length (needed);
-  /* Prepend the data OID. */
+
+  /* 1. Prepend the data OID. */
   needed += 2 + DIM (oid_data);
-  /* And a sequence. */
-  outseqlen = needed;
+
+  /* 0. Prepend another sequence. */
+  len[0] = needed;
   needed += compute_tag_length (needed);
 
-  cipher = gcry_malloc (needed);
-  if (!cipher)
+  /* Now that we have all length information, allocate a buffer. */
+  p = keybag = gcry_malloc (needed);
+  if (!keybag)
     {
       log_error ("error allocating buffer\n");
-      gcry_free (plain);
       return NULL;
     }
-  p = cipher;
-  /* Store the first sequence. */
-  p = store_tag_length (p, TAG_SEQUENCE, outseqlen);
-  /* Store the data OID. */
+
+  /* Walk 11 steps up to store the data. */
+
+  /* 0. Store the first sequence. */
+  p = store_tag_length (p, TAG_SEQUENCE, len[0]);
+
+  /* 1. Store the data OID. */
   p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
   memcpy (p, oid_data, DIM (oid_data)); 
   p += DIM (oid_data); 
-  /* Next comes a context tag. */
-  p = store_tag_length (p, 0xa0, out0taglen);
-  /* And an octet string. */
-  p = store_tag_length (p, TAG_OCTET_STRING, outoctstrlen);
-  /* Two sequences. */
-  p = store_tag_length (p, TAG_SEQUENCE, aseq1len);
-  p = store_tag_length (p, TAG_SEQUENCE, aseq2len);
-  /* Store the shroudedKeyBag OID. */
+
+  /* 2. Store a [0] tag. */
+  p = store_tag_length (p, 0xa0, len[2]);
+
+  /* 3. And an octet string. */
+  p = store_tag_length (p, TAG_OCTET_STRING, len[3]);
+
+  /* 4+5. Two sequences. */
+  p = store_tag_length (p, TAG_SEQUENCE, len[4]);
+  p = store_tag_length (p, TAG_SEQUENCE, len[5]);
+
+  /* 6. Store the shroudedKeyBag OID. */
   p = store_tag_length (p, TAG_OBJECT_ID,
                         DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag));
   memcpy (p, oid_pkcs_12_pkcs_8ShroudedKeyBag,
           DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag)); 
   p += DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag); 
-  /* Next comes a context tag. */
-  p = store_tag_length (p, 0xa0, in0taglen);
-  /* And a sequence. */
-  p = store_tag_length (p, TAG_SEQUENCE, aseq3len);
-  /* Now for the pre-encoded algorithm indentifier and the salt. */
-  memcpy (p, data_3desiter1024, DIM (data_3desiter1024));
-  memcpy (p + DATA_3DESITER1024_SALT_OFF, salt, 8);
-  p += DIM (data_3desiter1024);
-  /* And finally the octet string with the encrypted data. */
-  p = store_tag_length (p, TAG_OCTET_STRING, plainlen);
-  memcpy (p, plain, plainlen);
-  p += plainlen;
-  cipherlen = p - cipher;
+
+  /* 7. Store a [0] tag. */
+  p = store_tag_length (p, 0xa0, len[7]);
+
+  /* 8. Store a sequence. */
+  p = store_tag_length (p, TAG_SEQUENCE, len[8]);
+
+  /* 9. Now for the pre-encoded algorithm identifier and the salt. */
+  memcpy (p, data_3desiter2048, DIM (data_3desiter2048));
+  memcpy (p + DATA_3DESITER2048_SALT_OFF, salt, 8);
+  p += DIM (data_3desiter2048);
+
+  /* 10. And finally the octet string with the encrypted data. */
+  p = store_tag_length (p, TAG_OCTET_STRING, buflen);
+  memcpy (p, buffer, buflen);
+  p += buflen;
+  keybaglen = p - keybag;
   
-  if (needed != cipherlen)
-    log_debug ("length mismatch: %u, %u\n", needed, cipherlen);
-  gcry_free (plain);
+  if (needed != keybaglen)
+    log_debug ("length mismatch: %u, %u\n", needed, keybaglen);
+  
+  *r_length = keybaglen;
+  return keybag;
+}
+
+
+static unsigned char *
+build_cert_bag (unsigned char *buffer, size_t buflen, char *salt,
+                size_t *r_length)
+{
+  size_t len[9], needed;
+  unsigned char *p, *certbag;
+  size_t certbaglen;
+
+  /* Walk 9 steps down to collect the info: */
+
+  /* 8. The data goes into an octet string. */
+  needed = compute_tag_length (buflen);
+  needed += buflen;
+
+  /* 7. The algorithm identifier. */
+  needed += DIM (data_rc2iter2048);
+
+  /* 6. The data OID. */
+  needed += 2 + DIM (oid_data);
+
+  /* 5. A sequence. */
+  len[5] = needed;
+  needed += compute_tag_length ( needed);
+
+  /* 4. An integer. */
+  needed += 3;
+
+  /* 3. A sequence. */
+  len[3] = needed;
+  needed += compute_tag_length (needed);
+
+  /* 2.  A [0] tag. */
+  len[2] = needed;
+  needed += compute_tag_length (needed);
+
+  /* 1. The encryptedData OID. */
+  needed += 2 + DIM (oid_encryptedData);
+
+  /* 0. The first sequence. */
+  len[0] = needed;
+  needed += compute_tag_length (needed);
+
+  /* Now that we have all length information, allocate a buffer. */
+  p = certbag = gcry_malloc (needed);
+  if (!certbag)
+    {
+      log_error ("error allocating buffer\n");
+      return NULL;
+    }
+
+  /* Walk 9 steps up to store the data. */
+
+  /* 0. Store the first sequence. */
+  p = store_tag_length (p, TAG_SEQUENCE, len[0]);
+
+  /* 1. Store the encryptedData OID. */
+  p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_encryptedData));
+  memcpy (p, oid_encryptedData, DIM (oid_encryptedData)); 
+  p += DIM (oid_encryptedData); 
+
+  /* 2. Store a [0] tag. */
+  p = store_tag_length (p, 0xa0, len[2]);
+
+  /* 3. Store a sequence. */
+  p = store_tag_length (p, TAG_SEQUENCE, len[3]);
+
+  /* 4. Store the integer 0. */
+  *p++ = TAG_INTEGER;
+  *p++ = 1; 
+  *p++ = 0;
+
+  /* 5. Store a sequence. */
+  p = store_tag_length (p, TAG_SEQUENCE, len[5]);
+
+  /* 6. Store the data OID. */
+  p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
+  memcpy (p, oid_data, DIM (oid_data)); 
+  p += DIM (oid_data); 
+
+  /* 7. Now for the pre-encoded algorithm identifier and the salt. */
+  memcpy (p, data_rc2iter2048, DIM (data_rc2iter2048));
+  memcpy (p + DATA_RC2ITER2048_SALT_OFF, salt, 8);
+  p += DIM (data_rc2iter2048);
+
+  /* 8. And finally the [0] tag with the encrypted data. */
+  p = store_tag_length (p, 0xa0, buflen);
+  memcpy (p, buffer, buflen);
+  p += buflen;
+  certbaglen = p - certbag;
+  
+  if (needed != certbaglen)
+    log_debug ("length mismatch: %u, %u\n", needed, certbaglen);
+
+  *r_length = certbaglen;
+  return certbag;
+}
+
+
+static unsigned char *
+build_cert_sequence (unsigned char *buffer, size_t buflen, size_t *r_length)
+{
+  size_t len[8], needed, n;
+  unsigned char *p, *certseq;
+  size_t certseqlen;
+
+  /* Walk 8 steps down to collect the info: */
+
+  /* 7. The data goes into an octet string. */
+  needed = compute_tag_length (buflen);
+  needed += buflen;
+
+  /* 6. A [0] tag. */
+  len[6] = needed;
+  needed += compute_tag_length (needed);
+
+  /* 5. An OID. */
+  needed += 2 + DIM (oid_x509Certificate_for_pkcs_12);
+
+  /* 4. A sequence. */
+  len[4] = needed;
+  needed += compute_tag_length (needed);
+
+  /* 3. A [0] tag. */
+  len[3] = needed;
+  needed += compute_tag_length (needed);
+
+  /* 2. An OID. */
+  needed += 2 + DIM (oid_pkcs_12_CertBag);
+
+  /* 1. A sequence. */
+  len[1] = needed;
+  needed += compute_tag_length (needed);
+
+  /* 0. The first sequence. */
+  len[0] = needed;
+  needed += compute_tag_length (needed);
+
+  /* Now that we have all length information, allocate a buffer. */
+  p = certseq = gcry_malloc (needed + 8 /*(for padding)*/);
+  if (!certseq)
+    {
+      log_error ("error allocating buffer\n");
+      return NULL;
+    }
+
+  /* Walk 8 steps up to store the data. */
+
+  /* 0. Store the first sequence. */
+  p = store_tag_length (p, TAG_SEQUENCE, len[0]);
+
+  /* 1. Store the second sequence. */
+  p = store_tag_length (p, TAG_SEQUENCE, len[1]);
+
+  /* 2. Store the pkcs12-cert-bag OID. */
+  p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_pkcs_12_CertBag));
+  memcpy (p, oid_pkcs_12_CertBag, DIM (oid_pkcs_12_CertBag)); 
+  p += DIM (oid_pkcs_12_CertBag); 
+
+  /* 3. Store a [0] tag. */
+  p = store_tag_length (p, 0xa0, len[3]);
+
+  /* 4. Store a sequence. */
+  p = store_tag_length (p, TAG_SEQUENCE, len[4]);
+
+  /* 5. Store the x509Certificate OID. */
+  p = store_tag_length (p, TAG_OBJECT_ID,
+                        DIM (oid_x509Certificate_for_pkcs_12));
+  memcpy (p, oid_x509Certificate_for_pkcs_12,
+          DIM (oid_x509Certificate_for_pkcs_12)); 
+  p += DIM (oid_x509Certificate_for_pkcs_12); 
+
+  /* 6. Store a [0] tag. */
+  p = store_tag_length (p, 0xa0, len[6]);
+
+  /* 7. And finally the octet string with the actual certificate. */
+  p = store_tag_length (p, TAG_OCTET_STRING, buflen);
+  memcpy (p, buffer, buflen);
+  p += buflen;
+  certseqlen = p - certseq;
+  
+  if (needed != certseqlen)
+    log_debug ("length mismatch: %u, %u\n", needed, certseqlen);
+  
+  /* Append some pad characters; we already allocated extra space. */
+  n = 8 - certseqlen % 8;
+  for (;(certseqlen % 8); certseqlen++)
+    *p++ = n;
+  
+  *r_length = certseqlen;
+  return certseq;
+}
+
+
+/* Expect the RSA key parameters in KPARMS and a password in
+   PW. Create a PKCS structure from it and return it as well as the
+   length in R_LENGTH; return NULL in case of an error. */
+unsigned char * 
+p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen,
+           const char *pw, size_t *r_length)
+{
+  unsigned char *buffer;
+  size_t n, buflen;
+  char salt[8];
+  struct buffer_s seqlist[2];
+  int seqlistidx = 0;
+
+  if (cert && certlen)
+    {
+      /* Encode the certificate. */
+      buffer = build_cert_sequence (cert, certlen, &buflen);
+      if (!buffer)
+        goto failure;
+
+      /* Encrypt it. */
+      gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
+      crypt_block (buffer, buflen, salt, 2048, pw, GCRY_CIPHER_RFC2268_40, 1);
+      
+      /* Encode the encrypted stuff into a bag. */
+      seqlist[seqlistidx].buffer = build_cert_bag (buffer, buflen, salt, &n);
+      seqlist[seqlistidx].length = n;
+      gcry_free (buffer);
+      buffer = NULL;
+      if (!seqlist[seqlistidx].buffer)
+        goto failure;
+      seqlistidx++;
+    }
+
+  if (kparms)
+    {
+      /* Encode the key. */
+      buffer = build_key_sequence (kparms, &buflen);
+      if (!buffer)
+        goto failure;
+      
+      /* Encrypt it. */
+      gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
+      crypt_block (buffer, buflen, salt, 2048, pw, GCRY_CIPHER_3DES, 1);
+
+      /* Encode the encrypted stuff into a bag. */
+      seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt, &n);
+      seqlist[seqlistidx].length = n;
+      gcry_free (buffer);
+      buffer = NULL;
+      if (!seqlist[seqlistidx].buffer)
+        goto failure;
+      seqlistidx++;
+    }
 
-  {
-    struct buffer_s seqlist[2];
+  seqlist[seqlistidx].buffer = NULL;
+  seqlist[seqlistidx].length = 0;
 
-    seqlist[0].buffer = cipher;
-    seqlist[0].length = cipherlen;
-    seqlist[1].buffer = NULL;
-    seqlist[1].length = 0;
+  buffer = create_final (seqlist, &buflen);
 
-    cipher = create_final (seqlist, &cipherlen);
-    gcry_free (seqlist[0].buffer);
-  }
+ failure:
+  for ( ; seqlistidx; seqlistidx--)
+    gcry_free (seqlist[seqlistidx].buffer);
 
-  *r_length = cipherlen;
-  return cipher;
+  *r_length = buffer? buflen : 0;
+  return buffer;
 }
 
 
index 61a5979..2fbb490 100644 (file)
@@ -28,8 +28,9 @@ gcry_mpi_t *p12_parse (const unsigned char *buffer, size_t length,
                        void (*certcb)(void*, const unsigned char*, size_t),
                        void *certcbarg);
 
-unsigned char *p12_build (gcry_mpi_t *kparms, const char *pw,
-                          size_t *r_length);
+unsigned char *p12_build (gcry_mpi_t *kparms,
+                          unsigned char *cert, size_t certlen,
+                          const char *pw, size_t *r_length);
 
 
 #endif /*MINIP12_H*/
index 59f6c67..1bc7144 100644 (file)
@@ -1,5 +1,5 @@
 /* protect-tool.c - A tool to test the secret key protection
- *     Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ *     Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -54,8 +54,10 @@ enum cmd_and_opt_values
   oP12Export,
   oStore,
   oForce,
+  oHaveCert,
   oNoFailOnExist,
   oHomedir,
+  oPrompt,
 
 aTest };
 
@@ -75,9 +77,12 @@ static int opt_armor;
 static int opt_store;
 static int opt_force;
 static int opt_no_fail_on_exist;
-static const char *passphrase;
+static int opt_have_cert;
+static const char *opt_passphrase;
+static char *opt_prompt;
 
-static const char *get_passphrase (void);
+static char *get_passphrase (int promptno);
+static void release_passphrase (char *pw);
 static int store_private_key (const unsigned char *grip,
                               const void *buffer, size_t length, int force);
 
@@ -97,10 +102,12 @@ static ARGPARSE_OPTS opts[] = {
 
   { oP12Import, "p12-import", 256, "import a PKCS-12 encoded private key"},
   { oP12Export, "p12-export", 256, "export a private key PKCS-12 encoded"},
+  { oHaveCert, "have-cert", 0,  "certificate to export provided on STDIN"},
   { oStore,     "store", 0, "store the created key in the appropriate place"},
   { oForce,     "force", 0, "force overwriting"},
   { oNoFailOnExist, "no-fail-on-exist", 0, "@" },
   { oHomedir, "homedir", 2, "@" }, 
+  { oPrompt,  "prompt", 2, "|ESCSTRING|use ESCSTRING as prompt in pinentry"}, 
   {0}
 };
 
@@ -328,12 +335,15 @@ read_and_protect (const char *fname)
   unsigned char *key;
   unsigned char *result;
   size_t resultlen;
+  char *pw;
   
   key = read_key (fname);
   if (!key)
     return;
 
-  rc = agent_protect (key, get_passphrase (), &result, &resultlen);
+  pw = get_passphrase (1);
+  rc = agent_protect (key, pw, &result, &resultlen);
+  release_passphrase (pw);
   xfree (key);
   if (rc)
     {
@@ -363,12 +373,14 @@ read_and_unprotect (const char *fname)
   unsigned char *key;
   unsigned char *result;
   size_t resultlen;
+  char *pw;
   
   key = read_key (fname);
   if (!key)
     return;
 
-  rc = agent_unprotect (key, get_passphrase (), &result, &resultlen);
+  rc = agent_unprotect (key, (pw=get_passphrase (1)), &result, &resultlen);
+  release_passphrase (pw);
   xfree (key);
   if (rc)
     {
@@ -632,6 +644,7 @@ import_p12_file (const char *fname)
   gcry_sexp_t s_key;
   unsigned char *key;
   unsigned char grip[20];
+  char *pw;
 
   /* fixme: we should release some stuff on error */
   
@@ -639,8 +652,9 @@ import_p12_file (const char *fname)
   if (!buf)
     return;
 
-  kparms = p12_parse (buf, buflen, get_passphrase (),
+  kparms = p12_parse (buf, buflen, (pw=get_passphrase (0)),
                       import_p12_cert_cb, NULL);
+  release_passphrase (pw);
   xfree (buf);
   if (!kparms)
     {
@@ -714,7 +728,8 @@ import_p12_file (const char *fname)
   gcry_sexp_release (s_key);
 
 
-  rc = agent_protect (key, get_passphrase (), &result, &resultlen);
+  rc = agent_protect (key, (pw=get_passphrase (0)), &result, &resultlen);
+  release_passphrase (pw);
   xfree (key);
   if (rc)
     {
@@ -797,27 +812,113 @@ sexp_to_kparms (gcry_sexp_t sexp)
 }
 
 
+/* Check whether STRING is a KEYGRIP, i.e has the correct length and
+   does only consist of uppercase hex characters. */
+static int
+is_keygrip (const char *string)
+{
+  int i;
+
+  for(i=0; string[i] && i < 41; i++) 
+    if (!strchr("01234567890ABCDEF", string[i]))
+      return 0; 
+  return i == 40;
+}
 
 
 static void
 export_p12_file (const char *fname)
 {
+  int rc;
   gcry_mpi_t kparms[9], *kp;
   unsigned char *key;
   size_t keylen;
   gcry_sexp_t private;
   struct rsa_secret_key_s sk;
   int i;
+  unsigned char *cert = NULL;
+  size_t certlen = 0;
+  int keytype;
+  size_t keylen_for_wipe = 0;
+  char *pw;
+
+  if ( is_keygrip (fname) )
+    {
+      char hexgrip[40+4+1];
+      char *p;
   
-  key = read_key (fname);
+      assert (strlen(fname) == 40);
+      strcpy (stpcpy (hexgrip, fname), ".key");
+
+      p = make_filename (opt_homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
+      key = read_key (p);
+      xfree (p);
+    }
+  else
+    key = read_key (fname);
+
   if (!key)
     return;
 
+  keytype = agent_private_key_type (key);
+  if (keytype == PRIVATE_KEY_PROTECTED)
+    {
+      unsigned char *tmpkey;
+      size_t tmplen;
+
+      rc = agent_unprotect (key, (pw=get_passphrase (1)), &tmpkey, &tmplen);
+      release_passphrase (pw);
+      if (rc)
+        {
+          log_error ("unprotecting key `%s' failed: %s\n",
+                     fname, gpg_strerror (rc));
+          xfree (key);
+          return;
+        }
+      xfree (key);
+      key = tmpkey;
+      keylen_for_wipe = tmplen;
+
+      keytype = agent_private_key_type (key);
+    }
+
+  if (keytype == PRIVATE_KEY_SHADOWED)
+    {
+      log_error ("`%s' is a shadowed private key - can't export it\n", fname);
+      wipememory (key, keylen_for_wipe);
+      xfree (key);
+      return;
+    }
+  else if (keytype != PRIVATE_KEY_CLEAR)
+    {
+      log_error ("\%s' is not a private key\n", fname);
+      wipememory (key, keylen_for_wipe);
+      xfree (key);
+      return;
+    }
+
+
+  if (opt_have_cert)
+    {
+      cert = read_file ("-", &certlen);
+      if (!cert)
+        {
+          wipememory (key, keylen_for_wipe);
+          xfree (key);
+          return;
+        }
+    }
+
+
   if (gcry_sexp_new (&private, key, 0, 0))
     {
       log_error ("gcry_sexp_new failed\n");
+      wipememory (key, keylen_for_wipe);
+      xfree (key);
+      xfree (cert);
       return;
     } 
+  wipememory (key, keylen_for_wipe);
   xfree (key);
 
   kp = sexp_to_kparms (private);
@@ -825,6 +926,7 @@ export_p12_file (const char *fname)
   if (!kp)
     {
       log_error ("error converting key parameters\n");
+      xfree (cert);
       return;
     } 
   sk.n = kp[0];
@@ -850,7 +952,9 @@ export_p12_file (const char *fname)
   kparms[7] = sk.u;
   kparms[8] = NULL;
 
-  key = p12_build (kparms, get_passphrase (), &keylen);
+  key = p12_build (kparms, cert, certlen, (pw=get_passphrase (0)), &keylen);
+  release_passphrase (pw);
+  xfree (cert);
   for (i=0; i < 8; i++)
     gcry_mpi_release (kparms[i]);
   if (!key)
@@ -861,6 +965,54 @@ export_p12_file (const char *fname)
 }
 
 
+
+/* Do the percent and plus/space unescaping in place and return the
+   length of the valid buffer. */
+static size_t
+percent_plus_unescape (unsigned char *string)
+{
+  unsigned char *p = string;
+  size_t n = 0;
+
+  while (*string)
+    {
+      if (*string == '%' && string[1] && string[2])
+        { 
+          string++;
+          *p++ = xtoi_2 (string);
+          n++;
+          string+= 2;
+        }
+      else if (*string == '+')
+        {
+          *p++ = ' ';
+          n++;
+          string++;
+        }
+      else
+        {
+          *p++ = *string++;
+          n++;
+        }
+    }
+
+  return n;
+}
+
+/* Remove percent and plus escaping and make sure that the reuslt is a
+   string.  This is done in place. Returns STRING. */
+static char *
+percent_plus_unescape_string (char *string) 
+{
+  unsigned char *p = string;
+  size_t n;
+
+  n = percent_plus_unescape (p);
+  p[n] = 0;
+
+  return string;
+}
+
 \f
 int
 main (int argc, char **argv )
@@ -918,11 +1070,13 @@ main (int argc, char **argv )
         case oP12Import: cmd = oP12Import; break;
         case oP12Export: cmd = oP12Export; break;
 
-        case oPassphrase: passphrase = pargs.r.ret_str; break;
+        case oPassphrase: opt_passphrase = pargs.r.ret_str; break;
         case oStore: opt_store = 1; break;
         case oForce: opt_force = 1; break;
         case oNoFailOnExist: opt_no_fail_on_exist = 1; break;
-
+        case oHaveCert: opt_have_cert = 1; break;
+        case oPrompt: opt_prompt = pargs.r.ret_str; break;
+          
         default : pargs.err = 2; break;
        }
     }
@@ -935,6 +1089,9 @@ main (int argc, char **argv )
   else if (argc > 1)
     usage (1);
 
+  if (opt_prompt)
+    opt_prompt = percent_plus_unescape_string (xstrdup (opt_prompt));
+
   if (cmd == oProtect)
     read_and_protect (fname);
   else if (cmd == oUnprotect)
@@ -965,21 +1122,27 @@ agent_exit (int rc)
 
 
 /* Return the passphrase string and ask the agent if it has not been
-   set from the command line. */
-static const char *
-get_passphrase (void)
+   set from the command line  PROMPTNO select the prompt to display:
+     0 = default
+     1 = taken from the option --prompt
+*/
+static char *
+get_passphrase (int promptno)
 {
   char *pw;
   int err;
+  const char *desc;
+
+  if (opt_passphrase)
+    return xstrdup (opt_passphrase);
 
-  if (passphrase)
-    return passphrase;
+  if (promptno == 1 && opt_prompt)
+    desc = opt_prompt;
+  else
+    desc = _("Please enter the passphrase or the PIN\n"
+             "needed to complete this operation.");
 
-  pw = simple_pwquery (NULL,NULL, 
-                       _("Enter passphrase:"),
-                       _("Please enter the passphrase or the PIN\n"
-                         "needed to complete this operation."),
-                       &err);
+  pw = simple_pwquery (NULL,NULL, _("Passphrase:"), desc, &err);
   if (!pw)
     {
       if (err)
@@ -988,10 +1151,19 @@ get_passphrase (void)
         log_info ("cancelled\n");
       agent_exit (0);
     }
-  passphrase = pw;
-  return passphrase;
+
+  return pw;
 }
 
+static void
+release_passphrase (char *pw)
+{
+  if (pw)
+    {
+      wipememory (pw, strlen (pw));
+      xfree (pw);
+    }
+}
 
 static int
 store_private_key (const unsigned char *grip,
index 2839505..6f02303 100644 (file)
@@ -1,3 +1,7 @@
+2004-02-19  Werner Koch  <wk@gnupg.org>
+
+       * simple-pwquery.c (agent_open): Don't mangle INFOSTR.
+
 2004-02-17  Werner Koch  <wk@gnupg.org>
 
        * simple-pwquery.c (agent_open): Ignore an empty GPG_AGENT_INFO.
index 5ceddc6..50dabc2 100644 (file)
@@ -273,6 +273,11 @@ agent_open (int *rfd)
 #endif
       return SPWQ_NO_AGENT;
     }
+  p = spwq_malloc (strlen (infostr)+1);
+  if (!p)
+    return SPWQ_OUT_OF_CORE;
+  strcpy (p, infostr);
+  infostr = p;
 
   if ( !(p = strchr ( infostr, ':')) || p == infostr
        || (p-infostr)+1 >= sizeof client_addr.sun_path ) 
@@ -344,7 +349,7 @@ agent_open (int *rfd)
 }
 
 
-/* Copy text to BUFFER and escape as required.  Return a poiinter to
+/* Copy text to BUFFER and escape as required.  Return a pointer to
    the end of the new buffer.  NOte that BUFFER must be large enough
    to keep the entire text; allocataing it 3 times the size of TEXT
    is sufficient. */
index 10d487f..5362455 100644 (file)
@@ -152,6 +152,14 @@ Export all certificates stored in the Keybox or those specified by the
 optional @var{pattern}.  When using along with the @code{--armor} option
 a few informational lines are prepended before each block.
 
+@item --export-secret-key-p12 @var{key-id}
+@opindex export
+Export the private key and the certificate identified by @var{key-id}
+in a PKCS#12 format. When using along with the @code{--armor} option
+a few informational lines are prepended to the output.  Note, that the
+PKCS#12 format is higly insecure and this command is only provided if
+there is no other way to exchange the private key.
+
 @item --learn-card
 @opindex learn-card
 Read information about the private keys from the smartcard and import
index d449eee..d148ddb 100644 (file)
@@ -127,7 +127,7 @@ open_card (CTRL ctrl, const char *apptype)
 }
 
 
-/* Do the percent and plus/space unescaping in place and return tghe
+/* Do the percent and plus/space unescaping in place and return the
    length of the valid buffer. */
 static size_t
 percent_plus_unescape (unsigned char *string)
index 4fcc251..922773e 100644 (file)
@@ -1,3 +1,9 @@
+2004-02-19  Werner Koch  <wk@gnupg.org>
+
+       * export.c (export_p12, popen_protect_tool)
+       (gpgsm_p12_export): New.
+       * gpgsm.c (main): New command --export-secret-key-p12. 
+
 2004-02-18  Werner Koch  <wk@gnupg.org>
 
        * gpgsm.c (set_debug): Set the new --debug-level flags.
index 5245766..ab175f1 100644 (file)
@@ -1,5 +1,5 @@
 /* export.c
- * Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <unistd.h> 
 #include <time.h>
 #include <assert.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/wait.h>
 
 #include "gpgsm.h"
 #include <gcrypt.h>
 #include <ksba.h>
 
 #include "keydb.h"
+#include "i18n.h"
+
+#ifdef _POSIX_OPEN_MAX
+#define MAX_OPEN_FDS _POSIX_OPEN_MAX
+#else
+#define MAX_OPEN_FDS 20
+#endif
+
+
 
 static void print_short_info (ksba_cert_t cert, FILE *fp);
+static gpg_error_t export_p12 (const unsigned char *certimg, size_t certimglen,
+                               const char *prompt, const char *keygrip,
+                               FILE **retfp);
 
 
 
@@ -195,6 +210,147 @@ gpgsm_export (CTRL ctrl, STRLIST names, FILE *fp)
 }
 
 
+/* Export a certificates and its private key. */
+void
+gpgsm_p12_export (ctrl_t ctrl, const char *name, FILE *fp)
+{
+  KEYDB_HANDLE hd;
+  KEYDB_SEARCH_DESC *desc = NULL;
+  Base64Context b64writer = NULL;
+  ksba_writer_t writer;
+  ksba_cert_t cert = NULL;
+  int rc=0;
+  const unsigned char *image;
+  size_t imagelen;
+  char *keygrip = NULL;
+  char *prompt;
+  char buffer[1024];
+  int  nread;
+  FILE *datafp = NULL;
+
+  hd = keydb_new (0);
+  if (!hd)
+    {
+      log_error ("keydb_new failed\n");
+      goto leave;
+    }
+
+  desc = xtrycalloc (1, sizeof *desc);
+  if (!desc)
+    {
+      log_error ("allocating memory for export failed: %s\n",
+                 gpg_strerror (OUT_OF_CORE (errno)));
+      goto leave;
+    }
+
+  rc = keydb_classify_name (name, desc);
+  if (rc)
+    {
+      log_error ("key `%s' not found: %s\n",
+                 name, gpg_strerror (rc));
+      goto leave;
+    }
+
+  /* Lookup the certificate an make sure that it is unique. */
+  rc = keydb_search (hd, desc, 1);
+  if (!rc)
+    {
+      rc = keydb_get_cert (hd, &cert);
+      if (rc) 
+        {
+          log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc));
+          goto leave;
+        }
+      
+      rc = keydb_search (hd, desc, 1);
+      if (!rc)
+        rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
+      else if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
+        rc = 0;
+      if (rc)
+        {
+          log_error ("key `%s' not found: %s\n",
+                     name, gpg_strerror (rc));
+          goto leave;
+        }
+    }
+      
+  keygrip = gpgsm_get_keygrip_hexstring (cert);
+  if (!keygrip || gpgsm_agent_havekey (keygrip))
+    {
+      /* Note, that the !keygrip case indicates a bad certificate. */
+      rc = gpg_error (GPG_ERR_NO_SECKEY);
+      log_error ("can't export key `%s': %s\n", name, gpg_strerror (rc));
+      goto leave;
+    }
+  
+  image = ksba_cert_get_image (cert, &imagelen);
+  if (!image)
+    {
+      log_error ("ksba_cert_get_image failed\n");
+      goto leave;
+    }
+
+  if (ctrl->create_pem)
+    {
+      print_short_info (cert, fp);
+      putc ('\n', fp);
+    }
+
+  ctrl->pem_name = "PKCS12";
+  rc = gpgsm_create_writer (&b64writer, ctrl, fp, &writer);
+  if (rc)
+    {
+      log_error ("can't create writer: %s\n", gpg_strerror (rc));
+      goto leave;
+    }
+
+
+  prompt = gpgsm_format_keydesc (cert);
+  rc = export_p12 (image, imagelen, prompt, keygrip, &datafp);
+  xfree (prompt);
+  if (rc)
+    goto leave;
+  rewind (datafp);
+  while ( (nread = fread (buffer, 1, sizeof buffer, datafp)) > 0 )
+    if ((rc = ksba_writer_write (writer, buffer, nread)))
+      {
+        log_error ("write failed: %s\n", gpg_strerror (rc));
+        goto leave;
+      }
+  if (ferror (datafp))
+    {
+      rc = gpg_error_from_errno (rc);
+      log_error ("error reading temporary file: %s\n", gpg_strerror (rc));
+      goto leave;
+    }
+
+  if (ctrl->create_pem)
+    {
+      /* We want one certificate per PEM block */
+      rc = gpgsm_finish_writer (b64writer);
+      if (rc) 
+        {
+          log_error ("write failed: %s\n", gpg_strerror (rc));
+          goto leave;
+        }
+      gpgsm_destroy_writer (b64writer);
+      b64writer = NULL;
+    }
+  
+  ksba_cert_release (cert); 
+  cert = NULL;
+
+ leave:
+  if (datafp)
+    fclose (datafp);
+  gpgsm_destroy_writer (b64writer);
+  ksba_cert_release (cert);
+  xfree (desc);
+  keydb_release (hd);
+}
+
+
 /* Print some info about the certifciate CERT to FP */
 static void
 print_short_info (ksba_cert_t cert, FILE *fp)
@@ -243,7 +399,220 @@ print_short_info (ksba_cert_t cert, FILE *fp)
 }
 
 
+static gpg_error_t
+popen_protect_tool (const char *pgmname,
+                    FILE *infile, FILE *outfile, FILE **statusfile, 
+                    const char *prompt, const char *keygrip,
+                    pid_t *pid)
+{
+  gpg_error_t err;
+  int fd, fdout, rp[2];
+  int n, i;
+
+  fflush (infile);
+  rewind (infile);
+  fd = fileno (infile);
+  fdout = fileno (outfile);
+  if (fd == -1 || fdout == -1)
+    log_fatal ("no file descriptor for temporary file: %s\n",
+               strerror (errno));
+
+  /* Now start the protect-tool. */
+  if (pipe (rp) == -1)
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("error creating a pipe: %s\n"), strerror (errno));
+      return err;
+    }
+      
+  *pid = fork ();
+  if (*pid == -1)
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("error forking process: %s\n"), strerror (errno));
+      close (rp[0]);
+      close (rp[1]);
+      return err;
+    }
+
+  if (!*pid)
+    { /* Child. */
+      const char *arg0;
 
+      arg0 = strrchr (pgmname, '/');
+      if (arg0)
+        arg0++;
+      else
+        arg0 = pgmname;
 
+      /* Connect the infile to stdin. */
+      if (fd != 0 && dup2 (fd, 0) == -1)
+        log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
 
+      /* Connect the outfile to stdout. */
+      if (fdout != 1 && dup2 (fdout, 1) == -1)
+        log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
+      
+      /* Connect stderr to our pipe. */
+      if (rp[1] != 2 && dup2 (rp[1], 2) == -1)
+        log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
+
+      /* Close all other files. */
+      n = sysconf (_SC_OPEN_MAX);
+      if (n < 0)
+        n = MAX_OPEN_FDS;
+      for (i=3; i < n; i++)
+        close(i);
+      errno = 0;
+
+      execlp (pgmname, arg0,
+              "--homedir", opt.homedir,
+              "--p12-export",
+              "--prompt", prompt?prompt:"", 
+              "--",
+              keygrip,
+              NULL);
+      /* No way to print anything, as we have closed all streams. */
+      _exit (31);
+    }
+
+  /* Parent. */
+  close (rp[1]);
+  *statusfile = fdopen (rp[0], "r");
+  if (!*statusfile)
+    {
+      err = gpg_error_from_errno (errno);
+      log_error ("can't fdopen pipe for reading: %s", strerror (errno));
+      kill (*pid, SIGTERM);
+      return err;
+    }
+
+  return 0;
+}
+
+
+static gpg_error_t
+export_p12 (const unsigned char *certimg, size_t certimglen,
+            const char *prompt, const char *keygrip,
+            FILE **retfp)
+{
+  const char *pgmname;
+  gpg_error_t err = 0, child_err = 0;
+  int i, c, cont_line;
+  unsigned int pos;
+  FILE *infp = NULL, *outfp = NULL, *fp = NULL;
+  char buffer[1024];
+  pid_t pid = -1;
+
+  if (!opt.protect_tool_program || !*opt.protect_tool_program)
+    pgmname = GNUPG_DEFAULT_PROTECT_TOOL;
+  else
+    pgmname = opt.protect_tool_program;
+
+  infp = tmpfile ();
+  if (!infp)
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("error creating temporary file: %s\n"), strerror (errno));
+      goto cleanup;
+    }
+
+  if (fwrite (certimg, certimglen, 1, infp) != 1)
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("error writing to temporary file: %s\n"),
+                 strerror (errno));
+      goto cleanup;
+    }
+
+  outfp = tmpfile ();
+  if (!outfp)
+    {
+      err = gpg_error_from_errno (errno);
+      log_error (_("error creating temporary file: %s\n"), strerror (errno));
+      goto cleanup;
+    }
+
+  err = popen_protect_tool (pgmname, infp, outfp, &fp, prompt, keygrip, &pid);
+  if (err)
+    {
+      pid = -1;
+      goto cleanup;
+    }
+  fclose (infp);
+  infp = NULL;
+
+  /* Read stderr of the protect tool. */
+  pos = 0;
+  cont_line = 0;
+  while ((c=getc (fp)) != EOF)
+    {
+      /* 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 >= 5 /*sizeof buffer - 1*/ || c == '\n')
+        {
+          buffer[pos - (c == '\n')] = 0;
+          if (cont_line)
+            log_printf ("%s", buffer);
+          else
+            log_info ("%s", buffer);
+          pos = 0;
+          cont_line = (c != '\n');
+        }
+    }
+
+  if (pos)
+    {
+      buffer[pos] = 0;
+      if (cont_line)
+        log_printf ("%s\n", buffer);
+      else
+        log_info ("%s\n", buffer);
+    }
+  else if (cont_line)
+    log_printf ("\n");
+
+  /* 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);
+
+ cleanup:
+  if (infp)
+    fclose (infp);
+  if (fp)
+    fclose (fp);
+  if (pid != -1)
+    {
+      int status;
+
+      while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
+        ;
+      if (i == -1)
+        log_error (_("waiting for protect-tools to terminate failed: %s\n"),
+                   strerror (errno));
+      else if (WIFEXITED (status) && WEXITSTATUS (status) == 31)
+        log_error (_("error running `%s': probably not installed\n"), pgmname);
+      else if (WIFEXITED (status) && WEXITSTATUS (status))
+        log_error (_("error running `%s': exit status %d\n"), pgmname,
+                     WEXITSTATUS (status));
+      else if (!WIFEXITED (status))
+        log_error (_("error running `%s': terminated\n"), pgmname);
+      else 
+        child_err = 0;
+    }
+  if (!err)
+    err = child_err;
+  if (err)
+    {
+      if (outfp)
+        fclose (outfp);
+    }
+  else
+    *retfp = outfp;
+  return err;
+}
 
index de265c1..ede432b 100644 (file)
@@ -74,6 +74,7 @@ enum cmd_and_opt_values {
   aSendKeys,
   aRecvKeys,
   aExport,
+  aExportSecretKeyP12,
   aCheckKeys, /* nyi */
   aServer,                        
   aLearnCard,
@@ -344,6 +345,8 @@ static ARGPARSE_OPTS opts[] = {
 #endif
     { aDummy, "throw-keyid", 0, "@"},
     { aDummy, "notation-data", 2, "@"},
+    { aExportSecretKeyP12, "export-secret-key-p12", 256, "@"}, 
+    
 
     { 302, NULL, 0, N_(
   "@\n(See the man page for a complete listing of all commands and options)\n"
@@ -869,6 +872,7 @@ main ( int argc, char **argv)
         case aSendKeys: 
         case aRecvKeys: 
         case aExport: 
+        case aExportSecretKeyP12: 
         case aListKeys:
         case aListExternalKeys: 
         case aListSecretKeys: 
@@ -1406,6 +1410,12 @@ main ( int argc, char **argv)
       free_strlist(sl);
       break;
 
+    case aExportSecretKeyP12:
+      if (argc == 1)
+        gpgsm_p12_export (&ctrl, *argv, stdout);
+      else
+        wrong_args (_("--export-secret-key-p12 KEY-ID"));
+      break;
       
     case aSendKeys:
     case aRecvKeys:
index 49a7318..a1711a7 100644 (file)
@@ -247,6 +247,7 @@ int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
 
 /*-- export.c --*/
 void gpgsm_export (ctrl_t ctrl, STRLIST names, FILE *fp);
+void gpgsm_p12_export (ctrl_t ctrl, const char *name, FILE *fp);
 
 /*-- delete.c --*/
 int gpgsm_delete (ctrl_t ctrl, STRLIST names);
index 47f0627..02d2e62 100644 (file)
@@ -562,7 +562,7 @@ parse_p12 (ksba_reader_t reader, FILE **retfp)
     }
   while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread)))
     {
-      if (fwrite (buffer, nread, 1, tmpfp) != 1)
+      if (nread && fwrite (buffer, nread, 1, tmpfp) != 1)
         {
           err = gpg_error_from_errno (errno);
           log_error (_("error writing to temporary file: %s\n"),