From a1b487a17a084b3d8d652d4feabb7de2d67ad292 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 19 Feb 2004 16:26:32 +0000 Subject: [PATCH] * 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. * 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. --- NEWS | 2 + agent/ChangeLog | 14 + agent/minip12.c | 587 +++++++++++++++++++++++++++++++--------- agent/minip12.h | 5 +- agent/protect-tool.c | 218 +++++++++++++-- common/ChangeLog | 4 + common/simple-pwquery.c | 7 +- doc/gpgsm.texi | 8 + scd/command.c | 2 +- sm/ChangeLog | 6 + sm/export.c | 371 ++++++++++++++++++++++++- sm/gpgsm.c | 10 + sm/gpgsm.h | 1 + sm/import.c | 2 +- 14 files changed, 1076 insertions(+), 161 deletions(-) diff --git a/NEWS b/NEWS index c0a8d6026..3d6837d2f 100644 --- 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. diff --git a/agent/ChangeLog b/agent/ChangeLog index ffd48f59e..241a60964 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,17 @@ +2004-02-19 Werner Koch + + * 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 * protect-tool.c (main): Setup the used character set. diff --git a/agent/minip12.c b/agent/minip12.c index 13b6aa3eb..e32a40de2 100644 --- a/agent/minip12.c +++ b/agent/minip12.c @@ -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; - n = compute_tag_length (needed); - needed += n; - /* And encapsulate all in an octet string. */ - octstrlen = needed; - n = compute_tag_length (needed); - needed += n; - /* And tag it with [0]. */ - out0taglen = needed; - n = compute_tag_length (needed); - needed += n; - /* Prepend an data OID. */ - needed += 2 + DIM (oid_data); - /* This all into a sequences. */ - notsooutseqlen = needed; - n = compute_tag_length (needed); - needed += n; - /* Prepend the version integer 3. */ - needed += 3; - /* And the final sequence. */ - outseqlen = needed; + + /* 6. This goes into a sequences. */ + len[6] = needed; n = compute_tag_length (needed); needed += n; + /* 5. Encapsulate all in an octet string. */ + len[5] = needed; + n = compute_tag_length (needed); + needed += n; + + /* 4. And tag it with [0]. */ + len[4] = needed; + n = compute_tag_length (needed); + needed += n; + + /* 3. Prepend an data OID. */ + needed += 2 + DIM (oid_data); + + /* 2. Put all into a sequences. */ + len[2] = needed; + n = compute_tag_length (needed); + needed += n; + + /* 1. Prepend the version integer 3. */ + needed += 3; + + /* 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; +} - /* 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; + +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); + + /* 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; - needed += compute_tag_length (needed); - /* This all goes into an octet string. */ - outoctstrlen = needed; - needed += compute_tag_length (needed); - /* Prepend it with a [0] tag. */ - out0taglen = needed; - needed += compute_tag_length (needed); - /* Prepend the data OID. */ - needed += 2 + DIM (oid_data); - /* And a sequence. */ - outseqlen = needed; + len[4] = needed; needed += compute_tag_length (needed); - cipher = gcry_malloc (needed); - if (!cipher) + /* 3. This all goes into an octet string. */ + len[3] = needed; + needed += compute_tag_length (needed); + + /* 2. Prepend another [0] tag. */ + len[2] = needed; + needed += compute_tag_length (needed); + + /* 1. Prepend the data OID. */ + needed += 2 + DIM (oid_data); + + /* 0. Prepend another sequence. */ + len[0] = needed; + needed += compute_tag_length (needed); + + /* 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; +} - { - struct buffer_s seqlist[2]; - seqlist[0].buffer = cipher; - seqlist[0].length = cipherlen; - seqlist[1].buffer = NULL; - seqlist[1].length = 0; +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; - cipher = create_final (seqlist, &cipherlen); - gcry_free (seqlist[0].buffer); - } + /* Walk 9 steps down to collect the info: */ - *r_length = cipherlen; - return cipher; + /* 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++; + } + + seqlist[seqlistidx].buffer = NULL; + seqlist[seqlistidx].length = 0; + + buffer = create_final (seqlist, &buflen); + + failure: + for ( ; seqlistidx; seqlistidx--) + gcry_free (seqlist[seqlistidx].buffer); + + *r_length = buffer? buflen : 0; + return buffer; } diff --git a/agent/minip12.h b/agent/minip12.h index 61a597926..2fbb490d7 100644 --- a/agent/minip12.h +++ b/agent/minip12.h @@ -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*/ diff --git a/agent/protect-tool.c b/agent/protect-tool.c index 59f6c6711..1bc7144be 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -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; +} + 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 (passphrase) - return passphrase; + if (opt_passphrase) + return xstrdup (opt_passphrase); - pw = simple_pwquery (NULL,NULL, - _("Enter passphrase:"), - _("Please enter the passphrase or the PIN\n" - "needed to complete this operation."), - &err); + 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, _("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, diff --git a/common/ChangeLog b/common/ChangeLog index 28395053c..6f02303e6 100644 --- a/common/ChangeLog +++ b/common/ChangeLog @@ -1,3 +1,7 @@ +2004-02-19 Werner Koch + + * simple-pwquery.c (agent_open): Don't mangle INFOSTR. + 2004-02-17 Werner Koch * simple-pwquery.c (agent_open): Ignore an empty GPG_AGENT_INFO. diff --git a/common/simple-pwquery.c b/common/simple-pwquery.c index 5ceddc613..50dabc218 100644 --- a/common/simple-pwquery.c +++ b/common/simple-pwquery.c @@ -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. */ diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index 10d487f82..53624558d 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -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 diff --git a/scd/command.c b/scd/command.c index d449eee52..d148ddb5a 100644 --- a/scd/command.c +++ b/scd/command.c @@ -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) diff --git a/sm/ChangeLog b/sm/ChangeLog index 4fcc2517e..922773ecb 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,9 @@ +2004-02-19 Werner Koch + + * 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 * gpgsm.c (set_debug): Set the new --debug-level flags. diff --git a/sm/export.c b/sm/export.c index 524576628..ab175f160 100644 --- a/sm/export.c +++ b/sm/export.c @@ -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. * @@ -26,14 +26,29 @@ #include #include #include +#include +#include +#include #include "gpgsm.h" #include #include #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; +} diff --git a/sm/gpgsm.c b/sm/gpgsm.c index de265c128..ede432b1b 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -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: diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 49a73186d..a1711a72e 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -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); diff --git a/sm/import.c b/sm/import.c index 47f062790..02d2e629f 100644 --- a/sm/import.c +++ b/sm/import.c @@ -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"),