Fixed importing certs created by newer versions of Mozilla.

This commit is contained in:
Werner Koch 2005-12-16 15:52:48 +00:00
parent f80ad71f1c
commit 38e7c4c50a
9 changed files with 285 additions and 56 deletions

5
NEWS
View File

@ -4,6 +4,11 @@ Noteworthy changes in version 1.9.20
* [scdaemon] Support for keypads of some readers. Tested only with
SPR532. New option --disable-keypad.
* New debug tool gpgparsemail.
* Importing pkcs#12 files created be recent versions of Mozilla works
again.
Noteworthy changes in version 1.9.19 (2005-09-12)
-------------------------------------------------

4
TODO
View File

@ -94,3 +94,7 @@ might want to have an agent context for each service request
* sm/
** --include-certs is as of now still a dummy command line option
** check that we issue NO_SECKEY xxx if a -u key was not found
* gpg/
** issue a NO_SECKEY xxxx if a -u key was not found.

View File

@ -1,3 +1,13 @@
2005-12-16 Werner Koch <wk@g10code.com>
* minip12.c (cram_octet_string): New
(p12_parse): Use it for NDEFed bags.
(parse_bag_data): Ditto.
(string_to_key, set_key_iv, crypt_block): New arg SALTLEN.
(p12_build): Use old value 8 for new arg.
(parse_bag_encrypted_data, parse_bag_data): Allow for salts of 8
to 16 bytes. Add new arg R_CONSUMED.
2005-11-24 Werner Koch <wk@g10code.com>
* minip12.c (p12_parse): Fixed for case that the key object comes

View File

@ -141,7 +141,8 @@ struct tag_info
/* Parse the buffer at the address BUFFER which is of SIZE and return
the tag and the length part from the TLV triplet. Update BUFFER
and SIZE on success. */
and SIZE on success. Checks that the encoded length does not
exhaust the length of the provided buffer. */
static int
parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
{
@ -221,8 +222,76 @@ parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
}
/* Given an ASN.1 chunk of a structure like:
24 NDEF: OCTET STRING -- This is not passed to us
04 1: OCTET STRING -- INPUT point s to here
: 30
04 1: OCTET STRING
: 80
[...]
04 2: OCTET STRING
: 00 00
: } -- This denotes a Null tag and are the last
-- two bytes in INPUT.
Create a new buffer with the content of that octet string. INPUT
is the orginal buffer with a length as stored at LENGTH. Returns
NULL on error or a new malloced buffer with the length of this new
buffer stored at LENGTH and the number of bytes parsed from input
are added to the value stored at INPUT_CONSUMED. INPUT_CONSUMED is
allowed to be passed as NULL if the caller is not interested in
this value. */
static unsigned char *
cram_octet_string (const unsigned char *input, size_t *length,
size_t *input_consumed)
{
const unsigned char *s = input;
size_t n = *length;
unsigned char *output, *d;
struct tag_info ti;
/* Allocate output buf. We know that it won't be longer than the
input buffer. */
d = output = gcry_malloc (n);
if (!output)
goto bailout;
for (;;)
{
if (parse_tag (&s, &n, &ti))
goto bailout;
if (ti.class == UNIVERSAL && ti.tag == TAG_OCTET_STRING
&& !ti.ndef && !ti.is_constructed)
{
memcpy (d, s, ti.length);
s += ti.length;
d += ti.length;
n -= ti.length;
}
else if (ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed)
break; /* Ready */
else
goto bailout;
}
*length = d - output;
if (input_consumed)
*input_consumed += s - input;
return output;
bailout:
if (input_consumed)
*input_consumed += s - input;
gcry_free (output);
return NULL;
}
static int
string_to_key (int id, char *salt, int iter, const char *pw,
string_to_key (int id, char *salt, size_t saltlen, int iter, const char *pw,
int req_keylen, unsigned char *keybuf)
{
int rc, i, j;
@ -241,10 +310,16 @@ string_to_key (int id, char *salt, int iter, const char *pw,
return -1;
}
if (saltlen < 8)
{
log_error ("salt too short\n");
return -1;
}
/* Store salt and password in BUF_I */
p = buf_i;
for(i=0; i < 64; i++)
*p++ = salt [i%8];
*p++ = salt [i%saltlen];
for(i=j=0; i < 64; i += 2)
{
*p++ = 0;
@ -314,14 +389,14 @@ string_to_key (int id, char *salt, int iter, const char *pw,
static int
set_key_iv (gcry_cipher_hd_t chd, char *salt, int iter, const char *pw,
int keybytes)
set_key_iv (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter,
const char *pw, int keybytes)
{
unsigned char keybuf[24];
int rc;
assert (keybytes == 5 || keybytes == 24);
if (string_to_key (1, salt, iter, pw, keybytes, keybuf))
if (string_to_key (1, salt, saltlen, iter, pw, keybytes, keybuf))
return -1;
rc = gcry_cipher_setkey (chd, keybuf, keybytes);
if (rc)
@ -330,7 +405,7 @@ set_key_iv (gcry_cipher_hd_t chd, char *salt, int iter, const char *pw,
return -1;
}
if (string_to_key (2, salt, iter, pw, 8, keybuf))
if (string_to_key (2, salt, saltlen, iter, pw, 8, keybuf))
return -1;
rc = gcry_cipher_setiv (chd, keybuf, 8);
if (rc)
@ -343,8 +418,8 @@ set_key_iv (gcry_cipher_hd_t chd, char *salt, int iter, const char *pw,
static void
crypt_block (unsigned char *buffer, size_t length, char *salt, int iter,
const char *pw, int cipher_algo, int encrypt)
crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
int iter, const char *pw, int cipher_algo, int encrypt)
{
gcry_cipher_hd_t chd;
int rc;
@ -356,7 +431,7 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, int iter,
wipememory (buffer, length);
return;
}
if (set_key_iv (chd, salt, iter, pw,
if (set_key_iv (chd, salt, saltlen, iter, pw,
cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24))
{
wipememory (buffer, length);
@ -381,18 +456,22 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, int iter,
static int
parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
int startoffset, const char *pw,
int startoffset, size_t *r_consumed, const char *pw,
void (*certcb)(void*, const unsigned char*, size_t),
void *certcbarg)
{
struct tag_info ti;
const unsigned char *p = buffer;
const unsigned char *p_start = buffer;
size_t n = length;
const char *where;
char salt[8];
char salt[16];
size_t saltlen;
unsigned int iter;
unsigned char *plain = NULL;
int bad_pass = 0;
unsigned char *cram_buffer = NULL;
size_t consumed = 0; /* Number of bytes consumed from the orginal buffer. */
where = "start";
if (parse_tag (&p, &n, &ti))
@ -449,11 +528,13 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_OCTET_STRING || ti.length != 8 )
if (ti.class || ti.tag != TAG_OCTET_STRING
|| ti.length < 8 || ti.length > 16 )
goto bailout;
memcpy (salt, p, 8);
p += 8;
n -= 8;
saltlen = ti.length;
memcpy (salt, p, saltlen);
p += saltlen;
n -= saltlen;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_INTEGER || !ti.length )
@ -468,7 +549,25 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
where = "rc2-ciphertext";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != CONTEXT || ti.tag != 0 || !ti.length )
consumed = p - p_start;
if (ti.class == CONTEXT && ti.tag == 0 && ti.is_constructed && ti.ndef)
{
/* Mozilla exported certs now come with single byte chunks of
octect strings. (Mozilla Firefox 1.0.4). Arghh. */
where = "cram-rc2-ciphertext";
cram_buffer = cram_octet_string ( p, &n, &consumed);
if (!cram_buffer)
goto bailout;
p = p_start = cram_buffer;
if (r_consumed)
*r_consumed = consumed;
r_consumed = NULL; /* Ugly hack to not update that value any further. */
ti.length = n;
}
else if (ti.class == CONTEXT && ti.tag == 0 && ti.length )
;
else
goto bailout;
log_info ("%lu bytes of RC2 encrypted text\n", ti.length);
@ -480,10 +579,11 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
goto bailout;
}
memcpy (plain, p, ti.length);
crypt_block (plain, ti.length, salt, iter, pw, GCRY_CIPHER_RFC2268_40, 0);
crypt_block (plain, ti.length, salt, saltlen,
iter, pw, GCRY_CIPHER_RFC2268_40, 0);
n = ti.length;
startoffset = 0;
buffer = p = plain;
p_start = p = plain;
/* { */
/* # warning debug code is enabled */
@ -615,13 +715,19 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
}
}
if (r_consumed)
*r_consumed = consumed;
gcry_free (plain);
gcry_free (cram_buffer);
return 0;
bailout:
if (r_consumed)
*r_consumed = consumed;
gcry_free (plain);
gcry_free (cram_buffer);
log_error ("encryptedData error at \"%s\", offset %u\n",
where, (p - buffer)+startoffset);
where, (p - p_start)+startoffset);
if (bad_pass)
{
/* Note, that the following string might be used by other programs
@ -634,19 +740,23 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
static gcry_mpi_t *
parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
const char *pw)
size_t *r_consumed, const char *pw)
{
int rc;
struct tag_info ti;
const unsigned char *p = buffer;
const unsigned char *p_start = buffer;
size_t n = length;
const char *where;
char salt[8];
char salt[16];
size_t saltlen;
unsigned int iter;
int len;
unsigned char *plain = NULL;
gcry_mpi_t *result = NULL;
int result_count, i;
unsigned char *cram_buffer = NULL;
size_t consumed = 0; /* Number of bytes consumed from the orginal buffer. */
where = "start";
if (parse_tag (&p, &n, &ti))
@ -658,6 +768,22 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
if (ti.class || ti.tag != TAG_OCTET_STRING)
goto bailout;
consumed = p - p_start;
if (ti.is_constructed && ti.ndef)
{
/* Mozilla exported certs now come with single byte chunks of
octect strings. (Mozilla Firefox 1.0.4). Arghh. */
where = "cram-data.outersegs";
cram_buffer = cram_octet_string ( p, &n, &consumed);
if (!cram_buffer)
goto bailout;
p = p_start = cram_buffer;
if (r_consumed)
*r_consumed = consumed;
r_consumed = NULL; /* Ugly hack to not update that value any further. */
}
where = "data.outerseqs";
if (parse_tag (&p, &n, &ti))
goto bailout;
@ -709,11 +835,13 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_OCTET_STRING || ti.length != 8 )
if (ti.class || ti.tag != TAG_OCTET_STRING
|| ti.length < 8 || ti.length > 16)
goto bailout;
memcpy (salt, p, 8);
p += 8;
n -= 8;
saltlen = ti.length;
memcpy (salt, p, saltlen);
p += saltlen;
n -= saltlen;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_INTEGER || !ti.length )
@ -740,10 +868,11 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
goto bailout;
}
memcpy (plain, p, ti.length);
crypt_block (plain, ti.length, salt, iter, pw, GCRY_CIPHER_3DES, 0);
consumed += p - p_start + ti.length;
crypt_block (plain, ti.length, salt, saltlen, iter, pw, GCRY_CIPHER_3DES, 0);
n = ti.length;
startoffset = 0;
buffer = p = plain;
p_start = p = plain;
/* { */
/* # warning debug code is enabled */
@ -828,6 +957,9 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
if (len)
goto bailout;
gcry_free (cram_buffer);
if (r_consumed)
*r_consumed = consumed;
return result;
bailout:
@ -838,8 +970,11 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
gcry_mpi_release (result[i]);
gcry_free (result);
}
gcry_free (cram_buffer);
log_error ( "data error at \"%s\", offset %u\n",
where, (p - buffer) + startoffset);
if (r_consumed)
*r_consumed = consumed;
return NULL;
}
@ -857,10 +992,13 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
{
struct tag_info ti;
const unsigned char *p = buffer;
const unsigned char *p_start = buffer;
size_t n = length;
const char *where;
int bagseqlength, len;
int bagseqndef, lenndef;
gcry_mpi_t *result = NULL;
unsigned char *cram_buffer = NULL;
where = "pfx";
if (parse_tag (&p, &n, &ti))
@ -897,71 +1035,121 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
if (ti.class != UNIVERSAL || ti.tag != TAG_OCTET_STRING)
goto bailout;
if (ti.is_constructed && ti.ndef)
{
/* Mozilla exported certs now come with single byte chunks of
octect strings. (Mozilla Firefox 1.0.4). Arghh. */
where = "cram-bags";
cram_buffer = cram_octet_string ( p, &n, NULL);
if (!cram_buffer)
goto bailout;
p = p_start = cram_buffer;
}
where = "bags";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE)
goto bailout;
bagseqndef = ti.ndef;
bagseqlength = ti.length;
while (bagseqlength)
while (bagseqlength || bagseqndef)
{
/*log_debug ( "at offset %u\n", (p - buffer));*/
log_debug ( "at offset %u\n", (p - p_start));
where = "bag-sequence";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (bagseqndef && ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed)
break; /* Ready */
if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE)
goto bailout;
if (bagseqlength < ti.nhdr)
goto bailout;
bagseqlength -= ti.nhdr;
if (bagseqlength < ti.length)
goto bailout;
bagseqlength -= ti.length;
if (!bagseqndef)
{
if (bagseqlength < ti.nhdr)
goto bailout;
bagseqlength -= ti.nhdr;
if (bagseqlength < ti.length)
goto bailout;
bagseqlength -= ti.length;
}
lenndef = ti.ndef;
len = ti.length;
if (parse_tag (&p, &n, &ti))
goto bailout;
len -= ti.nhdr;
if (lenndef)
len = ti.nhdr;
else
len -= ti.nhdr;
if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_encryptedData)
&& !memcmp (p, oid_encryptedData, DIM(oid_encryptedData)))
{
size_t consumed = 0;
p += DIM(oid_encryptedData);
n -= DIM(oid_encryptedData);
len -= DIM(oid_encryptedData);
if (!lenndef)
len -= DIM(oid_encryptedData);
where = "bag.encryptedData";
if (parse_bag_encrypted_data (p, n, (p - buffer), pw,
if (parse_bag_encrypted_data (p, n, (p - p_start), &consumed, pw,
certcb, certcbarg))
goto bailout;
if (lenndef)
len += consumed;
}
else if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_data)
&& !memcmp (p, oid_data, DIM(oid_data)))
&& !memcmp (p, oid_data, DIM(oid_data)))
{
if (result)
log_info ("already got an data object, skipping next one\n");
{
log_info ("already got an data object, skipping next one\n");
p += ti.length;
n -= ti.length;
}
else
{
size_t consumed = 0;
p += DIM(oid_data);
n -= DIM(oid_data);
len -= DIM(oid_data);
result = parse_bag_data (p, n, (p-buffer), pw);
if (!lenndef)
len -= DIM(oid_data);
result = parse_bag_data (p, n, (p - p_start), &consumed, pw);
if (!result)
goto bailout;
if (lenndef)
len += consumed;
}
}
else
log_info ( "unknown bag type - skipped\n");
{
log_info ("unknown bag type - skipped\n");
p += ti.length;
n -= ti.length;
}
if (len < 0 || len > n)
goto bailout;
p += len;
n -= len;
if (lenndef)
{
/* Need to skip the Null Tag. */
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!(ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed))
goto bailout;
}
}
gcry_free (cram_buffer);
return result;
bailout:
log_error ("error at \"%s\", offset %u\n", where, (p - buffer));
log_error ("error at \"%s\", offset %u\n", where, (p - p_start));
/* fixme: need to release RESULT. */
gcry_free (cram_buffer);
return NULL;
}
@ -1586,7 +1774,8 @@ p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen,
/* Encrypt it. */
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
crypt_block (buffer, buflen, salt, 2048, pw, GCRY_CIPHER_RFC2268_40, 1);
crypt_block (buffer, buflen, salt, 8, 2048, pw,
GCRY_CIPHER_RFC2268_40, 1);
/* Encode the encrypted stuff into a bag. */
seqlist[seqlistidx].buffer = build_cert_bag (buffer, buflen, salt, &n);
@ -1607,7 +1796,7 @@ p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen,
/* Encrypt it. */
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
crypt_block (buffer, buflen, salt, 2048, pw, GCRY_CIPHER_3DES, 1);
crypt_block (buffer, buflen, salt, 8, 2048, pw, GCRY_CIPHER_3DES, 1);
/* Encode the encrypted stuff into a bag. */
seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt, &n);

View File

@ -14,6 +14,7 @@ GnuPG comes with a couple of smaller tools:
* gpgsm-gencert.sh:: Generate an X.509 certificate request.
* gpg-preset-passphrase:: Put a passphrase into the cache.
* gpg-connect-agent:: Communicate with a running agent.
* gpgparsemail:: Parse a mail message into an annotated format
* symcryptrun:: Call a simple symmetric encryption tool.
@end menu
@ -773,6 +774,17 @@ be used to directly connect to any Assuan style socket server.
@end table
@c
@c GPGPARSEMAIL
@c
@node gpgparsemail
@section Parse a mail message into an annotated format
The @command{gpgparsemail} is a utility currentlu only useful for
debugging. Run it with @code{--help} for usage information.
@c
@c SYMCRYPTRUN
@c

View File

@ -1,5 +1,7 @@
2005-12-14 Werner Koch <wk@g10code.com>
* Makefile.am (bin_PROGRAMS): Build gpgparsemail.
* gpgparsemail.c (pkcs7_begin): New.
(parse_message, message_cb): Add support of direct pkcs signatures.

View File

@ -18,7 +18,6 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
EXTRA_DIST = Manifest watchgnupg.c \
rfc822parse.c rfc822parse.h gpgparsemail.c \
addgnupghome gpgsm-gencert.sh
AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/common
@ -36,7 +35,7 @@ else
symcryptrun =
endif
bin_PROGRAMS = gpgconf gpg-connect-agent gpgkey2ssh ${symcryptrun}
bin_PROGRAMS = gpgconf gpg-connect-agent gpgkey2ssh ${symcryptrun} gpgparsemail
if !HAVE_W32_SYSTEM
bin_PROGRAMS += watchgnupg
endif
@ -46,6 +45,9 @@ gpgconf_SOURCES = gpgconf.c gpgconf.h gpgconf-comp.c no-libgcrypt.c
gpgconf_LDADD = ../jnlib/libjnlib.a \
../common/libcommon.a ../gl/libgnu.a @LIBINTL@
gpgparsemail_SOURCES = gpgparsemail.c rfc822parse.c rfc822parse.h
gpgparsemail_LDADD =
symcryptrun_SOURCES = symcryptrun.c
symcryptrun_LDADD = $(LIBUTIL_LIBS) ../jnlib/libjnlib.a \
../common/libcommon.a ../gl/libgnu.a \

View File

@ -21,8 +21,8 @@
/* This utility prints an RFC8222, possible MIME structured, message
in an annotated format with the first column having an indicator
for the content of the line.. Several options are available to
scrutinize the message. S/MIME and OpenPGP suuport is included. */
for the content of the line. Several options are available to
scrutinize the message. S/MIME and OpenPGP support is included. */
#include <stdio.h>
@ -708,6 +708,8 @@ main (int argc, char **argv)
" --debug enable additional debug output\n"
" --help display this help and exit\n\n"
"With no FILE, or when FILE is -, read standard input.\n\n"
"WARNING: This tool is under development.\n"
" The semantics may change without notice\n\n"
"Report bugs to <bug-gnupg@gnu.org>.");
exit (0);
}

View File

@ -155,7 +155,7 @@ capitalize_header_name (unsigned char *name)
*name = *name - 'A' + 'a';
}
#ifndef HAVE_STPCPY
static char *
stpcpy (char *a,const char *b)
{
@ -165,6 +165,7 @@ stpcpy (char *a,const char *b)
return (char*)a;
}
#endif
/* If a callback has been registerd, call it for the event of type
@ -474,7 +475,7 @@ insert_body (rfc822parse_t msg, const unsigned char *line, size_t length)
msg->boundary = NULL; /* No current boundary anymore. */
set_current_part_to_parent (msg);
/* Fixme: The next should acctually be sent right before the
/* Fixme: The next should actually be send right before the
next boundary, so that we can mark the epilogue. */
if (!rc)
rc = do_callback (msg, RFC822PARSE_LEVEL_UP);
@ -523,7 +524,8 @@ rfc822parse_finish (rfc822parse_t msg)
* available.
*
* If VALUEOFF is not NULL it will receive the offset of the first non
* space character in th value of the line.
* space character in the value part of the line (i.e. after the first
* colon).
*/
char *
rfc822parse_get_field (rfc822parse_t msg, const char *name, int which,
@ -758,7 +760,8 @@ parse_field (HDR_LINE hdr)
static const char specials[] = "<>@.,;:\\[]\"()";
static const char specials2[] = "<>@.,;:";
static const char tspecials[] = "/?=<>@,;:\\[]\"()";
static const char tspecials2[] = "/?=<>@.,;:";
static const char tspecials2[] = "/?=<>@.,;:"; /* FIXME: really
include '.'?*/
static struct
{
const unsigned char *name;