* 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.
This commit is contained in:
Werner Koch 2004-02-19 16:26:32 +00:00
parent 50ad027c9a
commit a1b487a17a
14 changed files with 1076 additions and 161 deletions

2
NEWS
View File

@ -7,6 +7,8 @@ Noteworthy changes in version 1.9.5
* [gpgsm] The --import command is now able to autodetect pkcs#12 * [gpgsm] The --import command is now able to autodetect pkcs#12
files and import secret and private keys from this file format. 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 * [gpgsm] The pinentry will now present a description of the key for
whom the passphrase is requests. whom the passphrase is requests.

View 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> 2004-02-18 Werner Koch <wk@gnupg.org>
* protect-tool.c (main): Setup the used character set. * protect-tool.c (main): Setup the used character set.

View File

@ -106,12 +106,19 @@ static unsigned char const oid_rsaEncryption[9] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; 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, 0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86,
0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03, 0x30, 0x0E, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03, 0x30, 0x0E,
0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x02, 0x02, 0x04, 0x00 }; 0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 };
#define DATA_3DESITER1024_SALT_OFF 18 #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 struct buffer_s
@ -346,17 +353,22 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, int iter,
if (rc) if (rc)
{ {
log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(rc)); log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(rc));
wipememory (buffer, length);
return; return;
} }
if (set_key_iv (chd, salt, iter, pw, if (set_key_iv (chd, salt, iter, pw,
cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24)) 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) rc = encrypt? gcry_cipher_encrypt (chd, buffer, length, NULL, 0)
: gcry_cipher_decrypt (chd, buffer, length, NULL, 0); : gcry_cipher_decrypt (chd, buffer, length, NULL, 0);
if (rc) if (rc)
{ {
wipememory (buffer, length);
log_error ( "en/de-crytion failed: %s\n", gpg_strerror (rc)); log_error ( "en/de-crytion failed: %s\n", gpg_strerror (rc));
goto leave; goto leave;
} }
@ -474,6 +486,13 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
startoffset = 0; startoffset = 0;
buffer = p = plain; 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"; where = "outer.outer.seq";
if (parse_tag (&p, &n, &ti)) if (parse_tag (&p, &n, &ti))
goto bailout; goto bailout;
@ -708,13 +727,6 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
startoffset = 0; startoffset = 0;
buffer = p = plain; 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"; where = "decrypted-text";
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
goto bailout; goto bailout;
@ -970,37 +982,48 @@ create_final (struct buffer_s *sequences, size_t *r_length)
{ {
int i; int i;
size_t needed = 0; size_t needed = 0;
size_t n, outseqlen, notsooutseqlen, out0taglen, octstrlen, inseqlen; size_t len[8], n;
unsigned char *result, *p; unsigned char *result, *p;
size_t resultlen; size_t resultlen;
/* 8 steps to create the pkcs#12 Krampf. */
/* 7. All the buffers. */
for (i=0; sequences[i].buffer; i++) for (i=0; sequences[i].buffer; i++)
needed += sequences[i].length; needed += sequences[i].length;
/* This goes into a sequences. */
inseqlen = needed; /* 6. This goes into a sequences. */
n = compute_tag_length (needed); len[6] = 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;
n = compute_tag_length (needed); n = compute_tag_length (needed);
needed += n; 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); result = gcry_malloc (needed);
if (!result) if (!result)
{ {
@ -1009,25 +1032,32 @@ create_final (struct buffer_s *sequences, size_t *r_length)
} }
p = result; p = result;
/* Store the very outer sequence. */ /* 0. Store the very outer sequence. */
p = store_tag_length (p, TAG_SEQUENCE, outseqlen); p = store_tag_length (p, TAG_SEQUENCE, len[0]);
/* Store the version integer 3. */
/* 1. Store the version integer 3. */
*p++ = TAG_INTEGER; *p++ = TAG_INTEGER;
*p++ = 1; *p++ = 1;
*p++ = 3; *p++ = 3;
/* Store another sequence. */
p = store_tag_length (p, TAG_SEQUENCE, notsooutseqlen); /* 2. Store another sequence. */
/* Store the data OID. */ 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)); p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
memcpy (p, oid_data, DIM (oid_data)); memcpy (p, oid_data, DIM (oid_data));
p += DIM (oid_data); p += DIM (oid_data);
/* Next comes a context tag. */
p = store_tag_length (p, 0xa0, out0taglen); /* 4. Next comes a context tag. */
/* And an octet string. */ p = store_tag_length (p, 0xa0, len[4]);
p = store_tag_length (p, TAG_OCTET_STRING, octstrlen);
/* And the inner sequence. */ /* 5. And an octet string. */
p = store_tag_length (p, TAG_SEQUENCE, inseqlen); p = store_tag_length (p, TAG_OCTET_STRING, len[5]);
/* And append all the buffers. */
/* 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++) for (i=0; sequences[i].buffer; i++)
{ {
memcpy (p, sequences[i].buffer, sequences[i].length); 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 /* Build a DER encoded SEQUENCE with the key:
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. */ SEQUENCE {
unsigned char * INTEGER 0
p12_build (gcry_mpi_t *kparms, const char *pw, size_t *r_length) 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; int rc, i;
size_t needed, n; size_t needed, n;
unsigned char *plain, *p, *cipher; unsigned char *plain, *p;
size_t plainlen, cipherlen; size_t plainlen;
size_t outseqlen, oidseqlen, octstrlen, inseqlen; 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. */ needed = 3; /* The version(?) integer of value 0. */
for (i=0; kparms[i]; i++) 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++) for (;(plainlen % 8); plainlen++)
*p++ = n; *p++ = n;
/* { */ *r_length = plainlen;
/* FILE *fp = fopen("inner-out.der", "wb"); */ return plain;
/* fwrite (plain, 1, plainlen, fp); */ }
/* fclose (fp); */
/* } */
/* Encrypt it and prepend a lot of stupid things. */
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); static unsigned char *
crypt_block (plain, plainlen, salt, 1024, pw, GCRY_CIPHER_3DES, 1); build_key_bag (unsigned char *buffer, size_t buflen, char *salt,
/* the data goes into an octet string. */ size_t *r_length)
needed = compute_tag_length (plainlen); {
needed += plainlen; size_t len[11], needed;
/* we prepend the the algorithm identifier (we use a pre-encoded one)*/ unsigned char *p, *keybag;
needed += DIM (data_3desiter1024); size_t keybaglen;
/* we put a sequence around. */
aseq3len = needed; /* 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); 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); needed += compute_tag_length (needed);
/* Prepend that shroudedKeyBag OID. */
/* 6. Prepend the shroudedKeyBag OID. */
needed += 2 + DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag); 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); needed += compute_tag_length ( needed);
aseq1len = needed; len[4] = 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;
needed += compute_tag_length (needed); needed += compute_tag_length (needed);
cipher = gcry_malloc (needed); /* 3. This all goes into an octet string. */
if (!cipher) 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"); log_error ("error allocating buffer\n");
gcry_free (plain);
return NULL; return NULL;
} }
p = cipher;
/* Store the first sequence. */ /* Walk 11 steps up to store the data. */
p = store_tag_length (p, TAG_SEQUENCE, outseqlen);
/* Store the data OID. */ /* 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)); p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
memcpy (p, oid_data, DIM (oid_data)); memcpy (p, oid_data, DIM (oid_data));
p += DIM (oid_data); p += DIM (oid_data);
/* Next comes a context tag. */
p = store_tag_length (p, 0xa0, out0taglen); /* 2. Store a [0] tag. */
/* And an octet string. */ p = store_tag_length (p, 0xa0, len[2]);
p = store_tag_length (p, TAG_OCTET_STRING, outoctstrlen);
/* Two sequences. */ /* 3. And an octet string. */
p = store_tag_length (p, TAG_SEQUENCE, aseq1len); p = store_tag_length (p, TAG_OCTET_STRING, len[3]);
p = store_tag_length (p, TAG_SEQUENCE, aseq2len);
/* Store the shroudedKeyBag OID. */ /* 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, p = store_tag_length (p, TAG_OBJECT_ID,
DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag)); DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag));
memcpy (p, oid_pkcs_12_pkcs_8ShroudedKeyBag, memcpy (p, oid_pkcs_12_pkcs_8ShroudedKeyBag,
DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag)); DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag));
p += 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); /* 7. Store a [0] tag. */
/* And a sequence. */ p = store_tag_length (p, 0xa0, len[7]);
p = store_tag_length (p, TAG_SEQUENCE, aseq3len);
/* Now for the pre-encoded algorithm indentifier and the salt. */ /* 8. Store a sequence. */
memcpy (p, data_3desiter1024, DIM (data_3desiter1024)); p = store_tag_length (p, TAG_SEQUENCE, len[8]);
memcpy (p + DATA_3DESITER1024_SALT_OFF, salt, 8);
p += DIM (data_3desiter1024); /* 9. Now for the pre-encoded algorithm identifier and the salt. */
/* And finally the octet string with the encrypted data. */ memcpy (p, data_3desiter2048, DIM (data_3desiter2048));
p = store_tag_length (p, TAG_OCTET_STRING, plainlen); memcpy (p + DATA_3DESITER2048_SALT_OFF, salt, 8);
memcpy (p, plain, plainlen); p += DIM (data_3desiter2048);
p += plainlen;
cipherlen = p - cipher; /* 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) if (needed != keybaglen)
log_debug ("length mismatch: %u, %u\n", needed, cipherlen); log_debug ("length mismatch: %u, %u\n", needed, keybaglen);
gcry_free (plain);
*r_length = keybaglen;
return keybag;
}
{
struct buffer_s seqlist[2];
seqlist[0].buffer = cipher; static unsigned char *
seqlist[0].length = cipherlen; build_cert_bag (unsigned char *buffer, size_t buflen, char *salt,
seqlist[1].buffer = NULL; size_t *r_length)
seqlist[1].length = 0; {
size_t len[9], needed;
unsigned char *p, *certbag;
size_t certbaglen;
cipher = create_final (seqlist, &cipherlen); /* Walk 9 steps down to collect the info: */
gcry_free (seqlist[0].buffer);
}
*r_length = cipherlen; /* 8. The data goes into an octet string. */
return cipher; 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;
} }

View 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 (*certcb)(void*, const unsigned char*, size_t),
void *certcbarg); void *certcbarg);
unsigned char *p12_build (gcry_mpi_t *kparms, const char *pw, unsigned char *p12_build (gcry_mpi_t *kparms,
size_t *r_length); unsigned char *cert, size_t certlen,
const char *pw, size_t *r_length);
#endif /*MINIP12_H*/ #endif /*MINIP12_H*/

View File

@ -1,5 +1,5 @@
/* protect-tool.c - A tool to test the secret key protection /* 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. * This file is part of GnuPG.
* *
@ -54,8 +54,10 @@ enum cmd_and_opt_values
oP12Export, oP12Export,
oStore, oStore,
oForce, oForce,
oHaveCert,
oNoFailOnExist, oNoFailOnExist,
oHomedir, oHomedir,
oPrompt,
aTest }; aTest };
@ -75,9 +77,12 @@ static int opt_armor;
static int opt_store; static int opt_store;
static int opt_force; static int opt_force;
static int opt_no_fail_on_exist; 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, static int store_private_key (const unsigned char *grip,
const void *buffer, size_t length, int force); 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"}, { oP12Import, "p12-import", 256, "import a PKCS-12 encoded private key"},
{ oP12Export, "p12-export", 256, "export a private key PKCS-12 encoded"}, { 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"}, { oStore, "store", 0, "store the created key in the appropriate place"},
{ oForce, "force", 0, "force overwriting"}, { oForce, "force", 0, "force overwriting"},
{ oNoFailOnExist, "no-fail-on-exist", 0, "@" }, { oNoFailOnExist, "no-fail-on-exist", 0, "@" },
{ oHomedir, "homedir", 2, "@" }, { oHomedir, "homedir", 2, "@" },
{ oPrompt, "prompt", 2, "|ESCSTRING|use ESCSTRING as prompt in pinentry"},
{0} {0}
}; };
@ -328,12 +335,15 @@ read_and_protect (const char *fname)
unsigned char *key; unsigned char *key;
unsigned char *result; unsigned char *result;
size_t resultlen; size_t resultlen;
char *pw;
key = read_key (fname); key = read_key (fname);
if (!key) if (!key)
return; 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); xfree (key);
if (rc) if (rc)
{ {
@ -363,12 +373,14 @@ read_and_unprotect (const char *fname)
unsigned char *key; unsigned char *key;
unsigned char *result; unsigned char *result;
size_t resultlen; size_t resultlen;
char *pw;
key = read_key (fname); key = read_key (fname);
if (!key) if (!key)
return; return;
rc = agent_unprotect (key, get_passphrase (), &result, &resultlen); rc = agent_unprotect (key, (pw=get_passphrase (1)), &result, &resultlen);
release_passphrase (pw);
xfree (key); xfree (key);
if (rc) if (rc)
{ {
@ -632,6 +644,7 @@ import_p12_file (const char *fname)
gcry_sexp_t s_key; gcry_sexp_t s_key;
unsigned char *key; unsigned char *key;
unsigned char grip[20]; unsigned char grip[20];
char *pw;
/* fixme: we should release some stuff on error */ /* fixme: we should release some stuff on error */
@ -639,8 +652,9 @@ import_p12_file (const char *fname)
if (!buf) if (!buf)
return; return;
kparms = p12_parse (buf, buflen, get_passphrase (), kparms = p12_parse (buf, buflen, (pw=get_passphrase (0)),
import_p12_cert_cb, NULL); import_p12_cert_cb, NULL);
release_passphrase (pw);
xfree (buf); xfree (buf);
if (!kparms) if (!kparms)
{ {
@ -714,7 +728,8 @@ import_p12_file (const char *fname)
gcry_sexp_release (s_key); 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); xfree (key);
if (rc) 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 static void
export_p12_file (const char *fname) export_p12_file (const char *fname)
{ {
int rc;
gcry_mpi_t kparms[9], *kp; gcry_mpi_t kparms[9], *kp;
unsigned char *key; unsigned char *key;
size_t keylen; size_t keylen;
gcry_sexp_t private; gcry_sexp_t private;
struct rsa_secret_key_s sk; struct rsa_secret_key_s sk;
int i; 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) if (!key)
return; 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)) if (gcry_sexp_new (&private, key, 0, 0))
{ {
log_error ("gcry_sexp_new failed\n"); log_error ("gcry_sexp_new failed\n");
wipememory (key, keylen_for_wipe);
xfree (key);
xfree (cert);
return; return;
} }
wipememory (key, keylen_for_wipe);
xfree (key); xfree (key);
kp = sexp_to_kparms (private); kp = sexp_to_kparms (private);
@ -825,6 +926,7 @@ export_p12_file (const char *fname)
if (!kp) if (!kp)
{ {
log_error ("error converting key parameters\n"); log_error ("error converting key parameters\n");
xfree (cert);
return; return;
} }
sk.n = kp[0]; sk.n = kp[0];
@ -850,7 +952,9 @@ export_p12_file (const char *fname)
kparms[7] = sk.u; kparms[7] = sk.u;
kparms[8] = NULL; 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++) for (i=0; i < 8; i++)
gcry_mpi_release (kparms[i]); gcry_mpi_release (kparms[i]);
if (!key) 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 int
main (int argc, char **argv ) main (int argc, char **argv )
@ -918,11 +1070,13 @@ main (int argc, char **argv )
case oP12Import: cmd = oP12Import; break; case oP12Import: cmd = oP12Import; break;
case oP12Export: cmd = oP12Export; 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 oStore: opt_store = 1; break;
case oForce: opt_force = 1; break; case oForce: opt_force = 1; break;
case oNoFailOnExist: opt_no_fail_on_exist = 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; default : pargs.err = 2; break;
} }
} }
@ -935,6 +1089,9 @@ main (int argc, char **argv )
else if (argc > 1) else if (argc > 1)
usage (1); usage (1);
if (opt_prompt)
opt_prompt = percent_plus_unescape_string (xstrdup (opt_prompt));
if (cmd == oProtect) if (cmd == oProtect)
read_and_protect (fname); read_and_protect (fname);
else if (cmd == oUnprotect) 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 /* Return the passphrase string and ask the agent if it has not been
set from the command line. */ set from the command line PROMPTNO select the prompt to display:
static const char * 0 = default
get_passphrase (void) 1 = taken from the option --prompt
*/
static char *
get_passphrase (int promptno)
{ {
char *pw; char *pw;
int err; int err;
const char *desc;
if (passphrase) if (opt_passphrase)
return passphrase; return xstrdup (opt_passphrase);
pw = simple_pwquery (NULL,NULL, if (promptno == 1 && opt_prompt)
_("Enter passphrase:"), desc = opt_prompt;
_("Please enter the passphrase or the PIN\n" else
"needed to complete this operation."), desc = _("Please enter the passphrase or the PIN\n"
&err); "needed to complete this operation.");
pw = simple_pwquery (NULL,NULL, _("Passphrase:"), desc, &err);
if (!pw) if (!pw)
{ {
if (err) if (err)
@ -988,10 +1151,19 @@ get_passphrase (void)
log_info ("cancelled\n"); log_info ("cancelled\n");
agent_exit (0); 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 static int
store_private_key (const unsigned char *grip, store_private_key (const unsigned char *grip,

View 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> 2004-02-17 Werner Koch <wk@gnupg.org>
* simple-pwquery.c (agent_open): Ignore an empty GPG_AGENT_INFO. * simple-pwquery.c (agent_open): Ignore an empty GPG_AGENT_INFO.

View File

@ -273,6 +273,11 @@ agent_open (int *rfd)
#endif #endif
return SPWQ_NO_AGENT; 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 if ( !(p = strchr ( infostr, ':')) || p == infostr
|| (p-infostr)+1 >= sizeof client_addr.sun_path ) || (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 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 to keep the entire text; allocataing it 3 times the size of TEXT
is sufficient. */ is sufficient. */

View 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 optional @var{pattern}. When using along with the @code{--armor} option
a few informational lines are prepended before each block. 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 @item --learn-card
@opindex learn-card @opindex learn-card
Read information about the private keys from the smartcard and import Read information about the private keys from the smartcard and import

View 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. */ length of the valid buffer. */
static size_t static size_t
percent_plus_unescape (unsigned char *string) percent_plus_unescape (unsigned char *string)

View 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> 2004-02-18 Werner Koch <wk@gnupg.org>
* gpgsm.c (set_debug): Set the new --debug-level flags. * gpgsm.c (set_debug): Set the new --debug-level flags.

View File

@ -1,5 +1,5 @@
/* export.c /* 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. * This file is part of GnuPG.
* *
@ -26,14 +26,29 @@
#include <unistd.h> #include <unistd.h>
#include <time.h> #include <time.h>
#include <assert.h> #include <assert.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/wait.h>
#include "gpgsm.h" #include "gpgsm.h"
#include <gcrypt.h> #include <gcrypt.h>
#include <ksba.h> #include <ksba.h>
#include "keydb.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 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 */ /* Print some info about the certifciate CERT to FP */
static void static void
print_short_info (ksba_cert_t cert, FILE *fp) 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;
}

View File

@ -74,6 +74,7 @@ enum cmd_and_opt_values {
aSendKeys, aSendKeys,
aRecvKeys, aRecvKeys,
aExport, aExport,
aExportSecretKeyP12,
aCheckKeys, /* nyi */ aCheckKeys, /* nyi */
aServer, aServer,
aLearnCard, aLearnCard,
@ -344,6 +345,8 @@ static ARGPARSE_OPTS opts[] = {
#endif #endif
{ aDummy, "throw-keyid", 0, "@"}, { aDummy, "throw-keyid", 0, "@"},
{ aDummy, "notation-data", 2, "@"}, { aDummy, "notation-data", 2, "@"},
{ aExportSecretKeyP12, "export-secret-key-p12", 256, "@"},
{ 302, NULL, 0, N_( { 302, NULL, 0, N_(
"@\n(See the man page for a complete listing of all commands and options)\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 aSendKeys:
case aRecvKeys: case aRecvKeys:
case aExport: case aExport:
case aExportSecretKeyP12:
case aListKeys: case aListKeys:
case aListExternalKeys: case aListExternalKeys:
case aListSecretKeys: case aListSecretKeys:
@ -1406,6 +1410,12 @@ main ( int argc, char **argv)
free_strlist(sl); free_strlist(sl);
break; 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 aSendKeys:
case aRecvKeys: case aRecvKeys:

View File

@ -247,6 +247,7 @@ int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files,
/*-- export.c --*/ /*-- export.c --*/
void gpgsm_export (ctrl_t ctrl, STRLIST names, FILE *fp); void gpgsm_export (ctrl_t ctrl, STRLIST names, FILE *fp);
void gpgsm_p12_export (ctrl_t ctrl, const char *name, FILE *fp);
/*-- delete.c --*/ /*-- delete.c --*/
int gpgsm_delete (ctrl_t ctrl, STRLIST names); int gpgsm_delete (ctrl_t ctrl, STRLIST names);

View File

@ -562,7 +562,7 @@ parse_p12 (ksba_reader_t reader, FILE **retfp)
} }
while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread))) 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); err = gpg_error_from_errno (errno);
log_error (_("error writing to temporary file: %s\n"), log_error (_("error writing to temporary file: %s\n"),