diff options
-rw-r--r-- | common/tlv-parser.c | 509 | ||||
-rw-r--r-- | common/tlv.h | 22 | ||||
-rw-r--r-- | sm/minip12.c | 269 |
3 files changed, 450 insertions, 350 deletions
diff --git a/common/tlv-parser.c b/common/tlv-parser.c index 2cafac3e9..b52cc7a21 100644 --- a/common/tlv-parser.c +++ b/common/tlv-parser.c @@ -1,5 +1,5 @@ /* tlv-parser.c - Parse BER encoded objects - * Copyright (C) 2023 g10 Code GmbH + * Copyright (C) 2023, 2024 g10 Code GmbH * * This file is part of GnuPG. * @@ -32,24 +32,26 @@ #define TLV_MAX_DEPTH 25 -struct bufferlist_s -{ - struct bufferlist_s *next; - char *buffer; -}; - /* An object to control the ASN.1 parsing. */ struct tlv_parser_s { /* The original buffer with the entire pkcs#12 object and its length. */ - const unsigned char *origbuffer; + unsigned char *origbuffer; size_t origbufsize; + /* The original offset for debugging. */ + size_t origoff; + + /* Here we keep a copy of the former TLV. This is returned by + * tlv_parser_release. */ + tlv_parser_t lasttlv; + /* The current buffer we are working on and its length. */ - const unsigned char *buffer; + unsigned char *buffer; size_t bufsize; + size_t crammed; /* 0 or actual length of crammed octet strings. */ int in_ndef; /* Flag indicating that we are in a NDEF. */ int pending; /* The last tlv_next has not yet been processed. */ @@ -58,22 +60,18 @@ struct tlv_parser_s const char *lastfunc;/* Name of last called function. */ int verbosity; /* Arg from tlv_parser_new. */ - struct bufferlist_s *bufferlist; /* To keep track of malloced buffers. */ - unsigned int stacklen; /* Used size of the stack. */ struct { - const unsigned char *buffer; /* Saved value of BUFFER. */ + unsigned char *buffer; /* Saved value of BUFFER. */ size_t bufsize; /* Saved value of BUFSIZE. */ size_t length; /* Length of the container (ti.length). */ + size_t crammed; /* Saved CRAMMED value. */ int in_ndef; /* Saved IN_NDEF flag (ti.ndef). */ } stack[TLV_MAX_DEPTH]; }; -static unsigned char *cram_octet_string (const unsigned char *input, - size_t length, size_t *r_newlength); -static int need_octet_string_cramming (const unsigned char *input, - size_t length); +static size_t cram_octet_string (tlv_parser_t tlv, int testmode); @@ -87,12 +85,10 @@ _tlv_parser_dump_tag (const char *text, int lno, tlv_parser_t tlv) ti = &tlv->ti; - log_debug ("p12_parse:%s:%d: @%04zu class=%d tag=%lu len=%zu nhdr=%zu %s%s\n", - text, lno, - (size_t)(tlv->buffer - tlv->origbuffer) - ti->nhdr, - ti->class, ti->tag, ti->length, ti->nhdr, - ti->is_constructed?" cons":"", - ti->ndef?" ndef":""); + log_debug ("%s:%d: %zu@%04zu class=%d tag=%lu %c len=%zu%s nhdr=%zu\n", + text, lno, tlv->origoff, tlv_parser_offset (tlv) - ti->nhdr, + ti->class, ti->tag, ti->is_constructed?'c':'p', + ti->length,ti->ndef?" ndef":"", ti->nhdr); } @@ -103,16 +99,36 @@ _tlv_parser_dump_state (const char *text, const char *text2, if (!tlv || tlv->verbosity < 2) return; - log_debug ("p12_parse:%s%s%s:%d: @%04zu lvl=%u %s\n", + log_debug ("p12_parse:%s%s%s:%d: %zu@%04zu lvl=%u %s\n", text, text2? "/":"", text2? text2:"", - lno, - (size_t)(tlv->buffer - tlv->origbuffer), + lno, tlv->origoff, tlv_parser_offset (tlv), tlv->stacklen, tlv->in_ndef? " in-ndef":""); } +static void +dump_to_file (const void *s, size_t n, const char *name) +{ +#if 0 + FILE *fp; + char fname[100]; + static int fcount; + + snprintf (fname, sizeof fname, "tmp-%03d-%s", ++fcount, name); + log_debug ("dumping %zu bytes to '%s'\n", n, fname); + fp = fopen (fname, "wb"); + if (!fp || fwrite (s, n, 1, fp) != 1) + exit (2); + fclose (fp); +#else + (void)s; + (void)n; + (void)name; +#endif +} + /* 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 @@ -135,7 +151,12 @@ parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti) ti->tag = tag; if (ti->length > *size) - return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); /* data larger than buffer. */ + { + /* data larger than buffer. */ + log_debug ("%s: ti->length=%zu for a buffer of size=%zu\n", + __func__, ti->length, *size); + return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); + } return 0; } @@ -150,95 +171,63 @@ tlv_parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti) /* Create a new TLV object. */ tlv_parser_t -tlv_parser_new (const unsigned char *buffer, size_t bufsize, int verbosity) +_tlv_parser_new (const unsigned char *buffer, size_t bufsize, + int verbosity, tlv_parser_t lasttlv, int lno) { tlv_parser_t tlv; + + if (verbosity > 1) + log_debug ("%s:%d: %zu@%zu (%p,%zu)\n", __func__, lno, + lasttlv?lasttlv->origoff:0, tlv_parser_offset (lasttlv), + buffer, bufsize); + tlv = xtrycalloc (1, sizeof *tlv); if (tlv) { - tlv->origbuffer = buffer; + char *mybuf = xtrymalloc ( bufsize + 1); + if (!mybuf) + { + xfree (tlv); + return NULL; + } + memcpy (mybuf, buffer, bufsize); + mybuf[bufsize] = 0; + tlv->origbuffer = mybuf; tlv->origbufsize = bufsize; - tlv->buffer = buffer; + tlv->origoff = tlv_parser_offset (lasttlv); + tlv->buffer = mybuf; tlv->bufsize = bufsize; + tlv->crammed = 0; tlv->verbosity = verbosity; + tlv->lasttlv = lasttlv; + dump_to_file (mybuf, bufsize, "context"); } return tlv; } -/* This function can be used to store a malloced buffer into the TLV - * object. Ownership of BUFFER is thus transferred to TLV. This - * buffer will then only be released by tlv_release. */ -static gpg_error_t -register_buffer (tlv_parser_t tlv, char *buffer) +/* Free the TLV object and returns the last TLV object stored in this + * TLV. */ +tlv_parser_t +_tlv_parser_release (tlv_parser_t tlv, int lno) { - struct bufferlist_s *item; - - item = xtrycalloc (1, sizeof *item); - if (!item) - return gpg_error_from_syserror (); - item->buffer = buffer; - item->next = tlv->bufferlist; - tlv->bufferlist = item; - return 0; -} + tlv_parser_t result; - -void -tlv_parser_release (tlv_parser_t tlv) -{ if (!tlv) - return; - while (tlv->bufferlist) + return NULL; + result = tlv->lasttlv; + if (tlv->verbosity > 1) { - struct bufferlist_s *save = tlv->bufferlist->next; - xfree (tlv->bufferlist->buffer); - xfree (tlv->bufferlist); - tlv->bufferlist = save; + if (result) + log_debug ("%s:%d: done; returning last TLV %zu@%zu (%p,%zu)\n", + __func__, lno, result->origoff, + tlv_parser_offset (result), result->buffer, result->bufsize); + else + log_debug ("%s:%d: done\n", __func__, lno); } + xfree (tlv->origbuffer); xfree (tlv); -} - - -/* Helper for the tlv_peek functions. */ -static gpg_error_t -_tlv_peek (tlv_parser_t tlv, struct tag_info *ti) -{ - const unsigned char *p; - size_t n; - - /* Note that we want to peek ahead of any current container but of - * course not beyond our entire buffer. */ - p = tlv->buffer; - if ((p - tlv->origbuffer) > tlv->origbufsize) - return gpg_error (GPG_ERR_BUG); - n = tlv->origbufsize - (p - tlv->origbuffer); - return parse_tag (&p, &n, ti); -} - - -/* Look for the next tag and return true if it matches CLASS and TAG. - * Otherwise return false. No state is changed. */ -int -_tlv_parser_peek (tlv_parser_t tlv, int class, int tag) -{ - struct tag_info ti; - - return (!_tlv_peek (tlv, &ti) - && ti.class == class && ti.tag == tag); -} - - -/* Look for the next tag and return true if it is the Null tag. - * Otherwise return false. No state is changed. */ -int -_tlv_parser_peek_null (tlv_parser_t tlv) -{ - struct tag_info ti; - - return (!_tlv_peek (tlv, &ti) - && ti.class == CLASS_UNIVERSAL && ti.tag == TAG_NULL - && !ti.is_constructed && !ti.length); + return result; } @@ -246,6 +235,7 @@ _tlv_parser_peek_null (tlv_parser_t tlv) static gpg_error_t _tlv_push (tlv_parser_t tlv) { + /* Right now our pointer is at the value of the current container. * We push that info onto the stack. */ if (tlv->stacklen >= TLV_MAX_DEPTH) @@ -254,6 +244,7 @@ _tlv_push (tlv_parser_t tlv) tlv->stack[tlv->stacklen].bufsize = tlv->bufsize; tlv->stack[tlv->stacklen].in_ndef = tlv->in_ndef; tlv->stack[tlv->stacklen].length = tlv->ti.length; + tlv->stack[tlv->stacklen].crammed = tlv->crammed; tlv->stacklen++; tlv->in_ndef = tlv->ti.ndef; @@ -278,7 +269,7 @@ _tlv_push (tlv_parser_t tlv) static gpg_error_t _tlv_pop (tlv_parser_t tlv) { - size_t lastlen; + size_t length; /* We reached the end of a container, either due to the size limit * or due to an end tag. Now we pop the last container so that we @@ -288,6 +279,8 @@ _tlv_pop (tlv_parser_t tlv) tlv->stacklen--; tlv->in_ndef = tlv->stack[tlv->stacklen].in_ndef; + length = tlv->ti.length = tlv->stack[tlv->stacklen].length; + tlv->crammed = tlv->stack[tlv->stacklen].crammed; if (tlv->in_ndef) { /* We keep buffer but adjust bufsize to the end of the origbuffer. */ @@ -297,19 +290,19 @@ _tlv_pop (tlv_parser_t tlv) } else { - lastlen = tlv->stack[tlv->stacklen].length; tlv->buffer = tlv->stack[tlv->stacklen].buffer; tlv->bufsize = tlv->stack[tlv->stacklen].bufsize; - if (lastlen > tlv->bufsize) + if (length > tlv->bufsize) { - log_debug ("%s: container length larger than buffer (%zu/%zu)\n", - __func__, lastlen, tlv->bufsize); + if (tlv->verbosity > 1) + log_debug ("%s: container larger than buffer (%zu/%zu)\n", + __func__, length, tlv->bufsize); return gpg_error (GPG_ERR_INV_BER); } - tlv->buffer += lastlen; - tlv->bufsize -= lastlen; - } + tlv->buffer += length; + tlv->bufsize -= length; + } _tlv_parser_dump_state (__func__, NULL, 0, tlv); return 0; } @@ -318,9 +311,13 @@ _tlv_pop (tlv_parser_t tlv) /* Parse the next tag and value. Also detect the end of a * container. The caller should use the tlv_next macro. */ gpg_error_t -_tlv_parser_next (tlv_parser_t tlv, int lno) +_tlv_parser_next (tlv_parser_t tlv, unsigned int flag, int lno) { gpg_error_t err; + const unsigned char *buffer; + size_t save_bufsize; + const unsigned char *save_buffer; + int i; tlv->lasterr = 0; tlv->lastfunc = __func__; @@ -329,34 +326,68 @@ _tlv_parser_next (tlv_parser_t tlv, int lno) { tlv->pending = 0; if (tlv->verbosity > 1) - log_debug ("%s: skipped\n", __func__); + log_debug ("%s:%d: skipped\n", __func__, lno); return 0; } if (tlv->verbosity > 1) - log_debug ("%s: called\n", __func__); + log_debug ("%s:%d: called (%p,%zu)\n", __func__, lno, + tlv->buffer, tlv->bufsize); /* If we are at the end of an ndef container pop the stack. */ if (!tlv->in_ndef && !tlv->bufsize) { + if (tlv->verbosity > 1) + for (i=0; i < tlv->stacklen; i++) + log_debug ("%s: stack[%d] (%p,@%zu,%zu) len=%zu (%zu) %s\n", + __func__, i, + tlv->stack[i].buffer, + tlv->stack[i].buffer - tlv->origbuffer, + tlv->stack[i].bufsize, + tlv->stack[i].length, + tlv->stack[i].crammed, + tlv->stack[i].in_ndef? " ndef":""); do err = _tlv_pop (tlv); while (!err && !tlv->in_ndef && !tlv->bufsize); + if (err) return (tlv->lasterr = err); if (tlv->verbosity > 1) - log_debug ("%s: container(s) closed due to size\n", __func__); + log_debug ("%s: container(s) closed due to size (lvl=%d)\n", + __func__, tlv->stacklen); } again: /* Get the next tag. */ - err = parse_tag (&tlv->buffer, &tlv->bufsize, &tlv->ti); + save_buffer = buffer = tlv->buffer; + save_bufsize = tlv->bufsize; + err = parse_tag (&buffer, &tlv->bufsize, &tlv->ti); + tlv->buffer = (unsigned char *)buffer; if (err) { if (tlv->verbosity > 1) - log_debug ("%s: reading tag returned err=%d\n", __func__, err); + { + log_debug ("%s: reading tag returned err=%d\n", __func__, err); + log_printhex (save_buffer, save_bufsize > 40? 40: save_bufsize, + "%s: data was\n", __func__); + dump_to_file (tlv->origbuffer, save_buffer - tlv->origbuffer, + "parseerr"); + } return err; } + if ( ( (tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OCTET_STRING) + || ((flag & TLV_PARSER_FLAG_T5793) + && tlv->ti.class == CLASS_CONTEXT && tlv->ti.tag == 0)) + && tlv->ti.is_constructed && cram_octet_string (tlv, 1)) + { + if (tlv->verbosity > 1) + log_debug ("%s: cramming %s\n", __func__, + tlv->ti.tag? "constructed octet strings":"for Mozilla bug"); + if (!cram_octet_string (tlv, 0)) + return (tlv->lasterr = gpg_error (GPG_ERR_BAD_BER)); + } + /* If there is an end tag in an ndef container pop the stack. Also * pop other containers which are fully consumed. */ if (tlv->in_ndef && (tlv->ti.class == CLASS_UNIVERSAL @@ -368,7 +399,8 @@ _tlv_parser_next (tlv_parser_t tlv, int lno) if (err) return (tlv->lasterr = err); if (tlv->verbosity > 1) - log_debug ("%s: container(s) closed due to end tag\n", __func__); + log_debug ("%s: container(s) closed due to end tag (lvl=%d)\n", + __func__, tlv->stacklen); goto again; } @@ -488,12 +520,13 @@ gpg_error_t tlv_expect_object (tlv_parser_t tlv, int class, int tag, unsigned char const **r_data, size_t *r_datalen) { - gpg_error_t err; const unsigned char *p; size_t n; int needpush = 0; tlv->lastfunc = __func__; + /* Note that the parser has already crammed the octet strings for a + * [0] to workaround the Mozilla bug. */ if (!(tlv->ti.class == class && tlv->ti.tag == tag)) return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); p = tlv->buffer; @@ -506,89 +539,65 @@ tlv_expect_object (tlv_parser_t tlv, int class, int tag, else if (!tlv->ti.length) return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); - if (class == CLASS_CONTEXT && tag == 0 && tlv->ti.is_constructed - && need_octet_string_cramming (p, n)) - { - char *newbuffer; + if (tlv->verbosity > 1) + log_debug ("%s: %zu@%zu %c len=%zu (%zu) bufsize=%zu of %zu\n", + __func__, + tlv->origoff, tlv_parser_offset (tlv), + tlv->ti.is_constructed? 'c':'p', + n, tlv->crammed, + tlv->bufsize, tlv->origbufsize); + + if (r_data) + *r_data = p; + if (r_datalen) + *r_datalen = tlv->crammed? tlv->crammed : n; - newbuffer = cram_octet_string (p, n, r_datalen); - if (!newbuffer) - return (tlv->lasterr = gpg_error (GPG_ERR_BAD_BER)); - err = register_buffer (tlv, newbuffer); - if (err) - { - xfree (newbuffer); - return (tlv->lasterr = err); - } - *r_data = newbuffer; - } - else - { - *r_data = p; - *r_datalen = n; - } if (needpush) return _tlv_push (tlv); - if (!(tlv->bufsize >= tlv->ti.length)) + if (!(tlv->bufsize >= n)) return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); - tlv->buffer += tlv->ti.length; - tlv->bufsize -= tlv->ti.length; + tlv->buffer += n; + tlv->bufsize -= n; + tlv->crammed = 0; return 0; } /* Expect that the current tag is an object string and store its value * at (R_DATA,R_DATALEN). Then skip over its value to the next tag. - * Note that the stored value are not allocated but point into TLV. - * If ENCAPSULATES is set the octet string is used as a new - * container. R_DATA and R_DATALEN are optional. */ + * Note that the stored value are not allocated but point into TLV. */ gpg_error_t -tlv_expect_octet_string (tlv_parser_t tlv, int encapsulates, +tlv_expect_octet_string (tlv_parser_t tlv, unsigned char const **r_data, size_t *r_datalen) { - gpg_error_t err; - const unsigned char *p; size_t n; tlv->lastfunc = __func__; - if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OCTET_STRING - && (!tlv->ti.is_constructed || encapsulates))) + /* The parser has already crammed constructed octet strings. */ + if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OCTET_STRING)) return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - p = tlv->buffer; - if (!(n=tlv->ti.length) && !tlv->ti.ndef) + if (!(n=tlv->ti.length) || tlv->ti.ndef ) return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); - if (encapsulates && tlv->ti.is_constructed - && need_octet_string_cramming (p, n)) - { - char *newbuffer; - - newbuffer = cram_octet_string (p, n, r_datalen); - if (!newbuffer) - return (tlv->lasterr = gpg_error (GPG_ERR_BAD_BER)); - err = register_buffer (tlv, newbuffer); - if (err) - { - xfree (newbuffer); - return (tlv->lasterr = err); - } - *r_data = newbuffer; - } - else - { - if (r_data) - *r_data = p; - if (r_datalen) - *r_datalen = tlv->ti.length; - } - if (encapsulates) - return _tlv_push (tlv); + if (tlv->verbosity > 1) + log_debug ("%s: %zu@%zu %c len=%zu (%zu) bufsize=%zu of %zu\n", + __func__, + tlv->origoff, tlv_parser_offset (tlv), + tlv->ti.is_constructed? 'c':'p', + n, tlv->crammed, + tlv->bufsize, tlv->origbufsize); + + if (r_data) + *r_data = tlv->buffer; + if (r_datalen) + *r_datalen = tlv->crammed? tlv->crammed : tlv->ti.length; if (!(tlv->bufsize >= tlv->ti.length)) return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); tlv->buffer += tlv->ti.length; tlv->bufsize -= tlv->ti.length; + tlv->crammed = 0; return 0; } @@ -692,10 +701,26 @@ tlv_expect_object_id (tlv_parser_t tlv, } -/* Given an ASN.1 chunk of a structure like: +/* Expect a NULL tag. */ +gpg_error_t +tlv_expect_null (tlv_parser_t tlv) +{ + tlv->lastfunc = __func__; + if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_NULL + && !tlv->ti.is_constructed && !tlv->ti.length)) + return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); + return 0; +} + + +/* Given a BER encoded constructed octet string like the example below + * from Mozilla Firefox 1.0.4 (which actually exports certs as single + * byte chunks of octet strings) in the buffer described by TLV. + * Although the example uses an ndef for the length of the constructed + * octet string, a fixed length is also allowed. * - * 24 NDEF: OCTET STRING -- This is not passed to us - * 04 1: OCTET STRING -- INPUT point s to here + * 24 NDEF: OCTET STRING + * 04 1: OCTET STRING -- TLV->buffer points to here * : 30 * 04 1: OCTET STRING * : 80 @@ -705,84 +730,98 @@ tlv_expect_object_id (tlv_parser_t tlv, * : } -- This denotes a Null tag and are the last * -- two bytes in INPUT. * - * The example is from Mozilla Firefox 1.0.4 which actually exports - * certs as single byte chunks of octet strings. + * Turn it into a primitive octet string of this form: + * + * 24 2: OCTET STRING + * : 30 80 + * + * and fill it up with FE to the original length. Unless TESTMODE is + * true the TLV object including the the member and the data is + * adjusted accordingly; however the intiial tag is not changed (in + * the example the "24 NDEF") because this is not needed anymore. * - * Create a new buffer with the content of that octet string. INPUT - * is the original buffer with a LENGTH. Returns - * NULL on error or a new malloced buffer with its actual used length - * stored at R_NEWLENGTH. */ -static unsigned char * -cram_octet_string (const unsigned char *input, size_t length, - size_t *r_newlength) + * On error 0 is returned and in this case the buffer might have + * already been modified and thus the caller should better stop + * parsing - unless TESTMODE was used. */ +static size_t +cram_octet_string (tlv_parser_t tlv, int testmode) { - const unsigned char *s = input; - size_t n = length; - unsigned char *output, *d; + gpg_error_t err; + size_t totallen; /* Length of the non-crammed octet strings. */ + size_t crammedlen; /* Length of the crammed octet strings. */ + const unsigned char *s, *save_s; + unsigned char *d; + size_t n, save_n; struct tag_info ti; - /* Allocate output buf. We know that it won't be longer than the - input buffer. */ - d = output = xtrymalloc (length); - if (!output) - goto bailout; - + if (tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OCTET_STRING) + ; /* Okay. */ + else if (tlv->ti.class == CLASS_CONTEXT && tlv->ti.tag == 0) + ; /* Workaround for Mozilla bug; see T5793 */ + else + return 0; /* Oops - we should not have been called. */ + if (!tlv->ti.is_constructed) + return 0; /* Oops - Not a constructed octet string. */ + if (!tlv->ti.ndef && tlv->ti.length < 4) + return 0; /* Fixed length but too short. */ + + /* Let S point to the first octet string chunk. */ + s = tlv->buffer; + n = tlv->ti.ndef? tlv->bufsize : tlv->ti.length; + + d = (unsigned char *)s; + totallen = crammedlen = 0; while (n) { - if (parse_tag (&s, &n, &ti)) - goto bailout; + save_s = s; + save_n = n; + if ((err=parse_tag (&s, &n, &ti))) + { + if (tlv->verbosity > 1) + { + log_debug ("%s: parse_tag(n=%zu) failed : %s\n", + __func__, save_n, gpg_strerror (err)); + log_printhex (save_s, save_n > 40? 40:save_n, "%s: data was", + __func__); + } + return 0; + } + if (tlv->verbosity > 1) + log_debug ("%s:%s ti.ndef=%d ti.tag=%lu ti.length=%zu (n %zu->%zu)\n", + __func__, testmode?"test:":"", + ti.ndef, ti.tag, ti.length, save_n, n); if (ti.class == CLASS_UNIVERSAL && ti.tag == TAG_OCTET_STRING && !ti.ndef && !ti.is_constructed) { - memcpy (d, s, ti.length); - s += ti.length; - d += ti.length; + if (!testmode) + memmove (d, s, ti.length); + d += ti.length; /* Update destination */ + totallen += ti.length + ti.nhdr; + crammedlen += ti.length; + s += ti.length; /* Skip to next tag. */ n -= ti.length; } else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) - break; /* Ready */ + { + totallen += ti.nhdr; + break; /* EOC - Ready */ + } else - goto bailout; + return 0; /* Invalid data. */ } - - - *r_newlength = d - output; - return output; - - bailout: - xfree (output); - return NULL; -} - - -/* Return true if (INPUT,LENGTH) is a structure which should be passed - * to cram_octet_string. This is basically the same loop as in - * cram_octet_string but without any actual copying. */ -static int -need_octet_string_cramming (const unsigned char *input, size_t length) -{ - const unsigned char *s = input; - size_t n = length; - struct tag_info ti; - - if (!length) - return 0; - - while (n) + if (!testmode) { - if (parse_tag (&s, &n, &ti)) - return 0; - if (ti.class == CLASS_UNIVERSAL && ti.tag == TAG_OCTET_STRING - && !ti.ndef && !ti.is_constructed) + memset (d, '\xfe', totallen - crammedlen); + tlv->ti.length = totallen; + tlv->ti.is_constructed = 0; + tlv->ti.ndef = 0; + tlv->crammed = crammedlen; + if (tlv->verbosity > 1) { - s += ti.length; - n -= ti.length; + log_debug ("%s: crammed length is %zu\n", __func__, crammedlen); + log_debug ("%s: total length is %zu\n", __func__, totallen); } - else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) - break; /* Ready */ - else - return 0; + dump_to_file (tlv->buffer, totallen, "crammed"); } - - return 1; + return totallen; } diff --git a/common/tlv.h b/common/tlv.h index 3136195a5..cfd615003 100644 --- a/common/tlv.h +++ b/common/tlv.h @@ -150,18 +150,17 @@ size_t get_tlv_length (int class, int tag, int constructed, size_t length); /*-- tlv-parser.c --*/ +#define TLV_PARSER_FLAG_T5793 1 /* Enable workaround for Mozilla bug. */ -tlv_parser_t tlv_parser_new (const unsigned char *buffer, size_t bufsize, - int verbosity); -void tlv_parser_release (tlv_parser_t tlv); +tlv_parser_t _tlv_parser_new (const unsigned char *buffer, size_t bufsize, + int verbosity, tlv_parser_t lasttlv, int lno); +tlv_parser_t _tlv_parser_release (tlv_parser_t tlv, int lno); void _tlv_parser_dump_tag (const char *text, int lno, tlv_parser_t tlv); void _tlv_parser_dump_state (const char *text, const char *text2, int lno, tlv_parser_t tlv); -int _tlv_parser_peek (tlv_parser_t tlv, int class, int tag); -int _tlv_parser_peek_null (tlv_parser_t tlv); -gpg_error_t _tlv_parser_next (tlv_parser_t tlv, int lno); +gpg_error_t _tlv_parser_next (tlv_parser_t tlv, unsigned int flags, int lno); unsigned int tlv_parser_level (tlv_parser_t tlv); size_t tlv_parser_offset (tlv_parser_t tlv); @@ -178,7 +177,7 @@ gpg_error_t tlv_expect_set (tlv_parser_t tlv); gpg_error_t tlv_expect_object (tlv_parser_t tlv, int class, int tag, unsigned char const **r_data, size_t *r_datalen); -gpg_error_t tlv_expect_octet_string (tlv_parser_t tlv, int encapsulates, +gpg_error_t tlv_expect_octet_string (tlv_parser_t tlv, unsigned char const **r_data, size_t *r_datalen); gpg_error_t tlv_expect_integer (tlv_parser_t tlv, int *r_value); @@ -189,19 +188,20 @@ gpg_error_t tlv_expect_mpinteger (tlv_parser_t tlv, int ignore_zero, gpg_error_t tlv_expect_object_id (tlv_parser_t tlv, unsigned char const **r_oid, size_t *r_oidlen); +gpg_error_t tlv_expect_null (tlv_parser_t tlv); /* Easier to use wrapper around parse_ber_header. */ gpg_error_t tlv_parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti); /* Convenience macro and macros to include the line number. */ +#define tlv_parser_new(a,b,c,d) _tlv_parser_new ((a),(b),(c),(d), __LINE__) +#define tlv_parser_release(a) _tlv_parser_release ((a), __LINE__) #define tlv_parser_dump_tag(a,b) _tlv_parser_dump_tag ((a),__LINE__,(b)) #define tlv_parser_dump_state(a,b,c) \ _tlv_parser_dump_state ((a),(b),__LINE__,(c)) -#define tlv_peek(a,b,c) _tlv_parser_peek ((a),(b),(c)) -#define tlv_peek_null(a) _tlv_parser_peek_null ((a)) -#define tlv_next(a) _tlv_parser_next ((a), __LINE__) - +#define tlv_next(a) _tlv_parser_next ((a),0, __LINE__) +#define tlv_next_with_flag(a,b) _tlv_parser_next ((a),(b), __LINE__) #endif /* SCD_TLV_H */ diff --git a/sm/minip12.c b/sm/minip12.c index c95240b23..028be916a 100644 --- a/sm/minip12.c +++ b/sm/minip12.c @@ -1,7 +1,7 @@ /* minip12.c - A minimal pkcs-12 implementation. * Copyright (C) 2002, 2003, 2004, 2006, 2011 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch - * Copyright (C) 2022, 2023 g10 Code GmbH + * Copyright (C) 2022-2024 g10 Code GmbH * * This file is part of GnuPG. * @@ -50,9 +50,6 @@ #define DIM(v) (sizeof(v)/sizeof((v)[0])) #endif -/* Enable the next macro to dump stuff for debugging. */ -#undef ENABLE_DER_STRUCT_DUMPING - static unsigned char const oid_data[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; @@ -189,6 +186,25 @@ p12_set_verbosity (int verbose, int debug) } +static void +dump_to_file (const void *s, size_t n, const char *name) +{ + FILE *fp; + char fname[100]; + static int fcount; + + if (opt_verbose > 1 && getenv ("GPGSM_DUMP_P12_DATA")) + { + snprintf (fname, sizeof fname, "tmp-p12-%03d-%s", ++fcount, name); + log_debug ("dumping %zu bytes to '%s'\n", n, fname); + fp = fopen (fname, "wb"); + if (!fp || fwrite (s, n, 1, fp) != 1) + log_assert (!"fopen or fwrite failed"); + fclose (fp); + } +} + + static int digest_algo_from_oid (unsigned char const *oid, size_t oidlen) { @@ -608,6 +624,7 @@ decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length, memcpy (plaintext, ciphertext, length); crypt_block (plaintext, length, salt, saltlen, iter, iv, ivlen, convertedpw? convertedpw:pw, cipher_algo, digest_algo, 0); + dump_to_file (plaintext, length, "raw-decrypt"); if (check_fnc (plaintext, length)) { /* Strip the pkcs#7 padding. */ @@ -646,15 +663,7 @@ bag_decrypted_data_p (const void *plaintext, size_t length) const unsigned char *p = plaintext; size_t n = length; -#ifdef ENABLE_DER_STRUCT_DUMPING - { - # warning debug code is enabled - FILE *fp = fopen ("tmp-minip12-plain-data.der", "wb"); - if (!fp || fwrite (p, n, 1, fp) != 1) - exit (2); - fclose (fp); - } -#endif /*ENABLE_DER_STRUCT_DUMPING*/ + dump_to_file (p, n, "plain-data.der"); if (tlv_parse_tag (&p, &n, &ti)) return 0; @@ -686,7 +695,8 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) int is_pbes2 = 0; int is_aes256 = 0; int keyelem_count; - int renewed_tlv = 0; + tlv_parser_t tmptlv; + int newtlv = 0; int loopcount; unsigned int startlevel, startlevel2; int digest_algo = GCRY_MD_SHA1; @@ -791,7 +801,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (tlv_next (tlv)) goto bailout; - if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) + if (tlv_expect_octet_string (tlv, &data, &datalen)) goto bailout; parmlen -= tlv_parser_tag_length (tlv, 1); if (datalen < 8 || datalen > sizeof salt) @@ -835,12 +845,13 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (opt_verbose > 1) log_debug ("kdf digest algo = %d\n", digest_algo); - if (tlv_peek_null (tlv)) - { - /* Read the optional Null tag. */ - if (tlv_next (tlv)) - goto bailout; - } + /* Read the optional Null tag. */ + if (tlv_next (tlv)) + goto bailout; + else if (!tlv_expect_null (tlv)) + ; /* NULL tag needs no skip. */ + else + tlv_parser_set_pending (tlv); } else digest_algo = GCRY_MD_SHA1; @@ -870,7 +881,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (tlv_next (tlv)) goto bailout; - if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) + if (tlv_expect_octet_string (tlv, &data, &datalen)) goto bailout; if (datalen != sizeof iv) { @@ -889,7 +900,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (tlv_next (tlv)) goto bailout; - if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) + if (tlv_expect_octet_string (tlv, &data, &datalen)) goto bailout; if (datalen < 8 || datalen > 20) { @@ -913,11 +924,12 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) } where = "rc2or3desoraes-ciphertext"; - if (tlv_next (tlv)) + if (tlv_next_with_flag (tlv, TLV_PARSER_FLAG_T5793)) goto bailout; if (tlv_expect_object (tlv, CLASS_CONTEXT, 0, &data, &datalen)) goto bailout; + dump_to_file (data, datalen, "raw-ciphertext"); if (opt_verbose) log_info ("%zu bytes of %s encrypted text\n", datalen, @@ -945,13 +957,14 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) /* We do not need the TLV anymore and allocated a new one. */ where = "bag.encryptedData.decrypted-text"; - tlv = tlv_parser_new (plain, datalen, opt_verbose); - if (!tlv) + tmptlv = tlv_parser_new (plain, datalen, opt_verbose, tlv); + if (!tmptlv) { err = gpg_error_from_syserror (); goto bailout; } - renewed_tlv = 1; + tlv = tmptlv; + newtlv = 1; if (tlv_next (tlv)) { @@ -1056,7 +1069,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) /* We ignore the next octet string. */ if (tlv_next (tlv)) goto bailout; - if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) + if (tlv_expect_octet_string (tlv, &data, &datalen)) goto bailout; if (tlv_next (tlv)) @@ -1144,7 +1157,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (tlv_next (tlv)) goto bailout; - if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) + if (tlv_expect_octet_string (tlv, &data, &datalen)) goto bailout; /* Return the certificate. */ @@ -1154,17 +1167,19 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) /* Skip the optional SET with the pkcs12 cert attributes. */ where = "bag.attribute_set"; - if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_SET)) + err = tlv_next (tlv); + if (err && gpg_err_code (err) == GPG_ERR_EOF) + err = 0; + else if (err) + goto bailout; + else if (!tlv_expect_set (tlv)) { - if (tlv_next (tlv)) - goto bailout; - err = tlv_expect_set (tlv); - if (err) - goto bailout; tlv_parser_skip (tlv); if (opt_verbose) log_info ("skipping %s\n", where); } + else + tlv_parser_set_pending (tlv); } if (!err) tlv_parser_set_pending (tlv); @@ -1177,7 +1192,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) err = 0; leave: - if (renewed_tlv) + if (newtlv) tlv_parser_release (tlv); gcry_free (plain); if (ctx->badpass) @@ -1211,15 +1226,7 @@ bag_data_p (const void *plaintext, size_t length) const unsigned char *p = plaintext; size_t n = length; -#ifdef ENABLE_DER_STRUCT_DUMPING - { -# warning debug code is enabled - FILE *fp = fopen ("tmp-minip12-plain-key.der", "wb"); - if (!fp || fwrite (p, n, 1, fp) != 1) - exit (2); - fclose (fp); - } -#endif /*ENABLE_DER_STRUCT_DUMPING*/ + dump_to_file (p, n, "plain-key.der"); if (tlv_parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) return 0; @@ -1245,8 +1252,8 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) size_t saltlen; char iv[16]; unsigned int iter; - tlv_parser_t saved_tlv = NULL; - int renewed_tlv = 0; /* True if the TLV must be released. */ + tlv_parser_t tmptlv = NULL; + int newtlv = 0; /* Counter which is true if the TLV must be released. */ unsigned char *plain = NULL; int is_pbes2 = 0; int is_aes256 = 0; @@ -1322,7 +1329,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (tlv_next (tlv)) goto bailout; - if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) + if (tlv_expect_octet_string (tlv, &data, &datalen)) goto bailout; parmlen -= tlv_parser_tag_length (tlv, 1); if (datalen < 8 || datalen > sizeof salt) @@ -1366,12 +1373,13 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (opt_verbose > 1) log_debug ("kdf digest algo = %d\n", digest_algo); - if (tlv_peek_null (tlv)) - { - /* Read the optional Null tag. */ - if (tlv_next (tlv)) - goto bailout; - } + /* Read the optional Null tag. */ + if (tlv_next (tlv)) + goto bailout; + else if (!tlv_expect_null (tlv)) + ; /* NULL tag needs no skip. */ + else + tlv_parser_set_pending (tlv); } else digest_algo = GCRY_MD_SHA1; @@ -1400,7 +1408,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (tlv_next (tlv)) goto bailout; - if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) + if (tlv_expect_octet_string (tlv, &data, &datalen)) goto bailout; if (datalen != sizeof iv) goto bailout; /* Bad IV. */ @@ -1416,8 +1424,11 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (tlv_next (tlv)) goto bailout; - if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) + if (tlv_expect_octet_string (tlv, &data, &datalen)) goto bailout; + if (opt_verbose > 1) + log_printhex (data, datalen, "%s: salt", __func__); + if (datalen < 8 || datalen > 20) { log_info ("bad length of salt (%zu) for 3DES\n", datalen); @@ -1442,7 +1453,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) where = "shrouded_key_bag.3desoraes-ciphertext"; if (tlv_next (tlv)) goto bailout; - if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) + if (tlv_expect_octet_string (tlv, &data, &datalen)) goto bailout; if (opt_verbose) @@ -1472,14 +1483,14 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) /* We do not need the TLV anymore and allocated a new one. */ where = "shrouded_key_bag.decrypted-text"; - saved_tlv = tlv; - tlv = tlv_parser_new (plain, datalen, opt_verbose); - if (!tlv) + tmptlv = tlv_parser_new (plain, datalen, opt_verbose, tlv); + if (!tmptlv) { err = gpg_error_from_syserror (); goto bailout; } - renewed_tlv = 1; + tlv = tmptlv; + newtlv++; if (opt_verbose > 1) log_debug ("new parser context\n"); @@ -1526,12 +1537,13 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (opt_verbose > 1) log_debug ("RSA parameters\n"); - if (tlv_peek_null (tlv)) - { - /* Read the optional Null tag. */ - if (tlv_next (tlv)) - goto bailout; - } + /* Read the optional Null tag. */ + if (tlv_next (tlv)) + goto bailout; + else if (!tlv_expect_null (tlv)) + ; /* NULL tag needs no skip. */ + else + tlv_parser_set_pending (tlv); } else if (oidlen == DIM(oid_pcPublicKey) && !memcmp (oid, oid_pcPublicKey, oidlen)) @@ -1561,9 +1573,21 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) /* An octet string to encapsulate the key elements. */ if (tlv_next (tlv)) goto bailout; - if (tlv_expect_octet_string (tlv, 1, &data, &datalen)) + if (tlv_expect_octet_string (tlv, &data, &datalen)) goto bailout; + tmptlv = tlv_parser_new (data, datalen, opt_verbose, tlv); + if (!tmptlv) + { + err = gpg_error_from_syserror (); + goto bailout; + } + tlv = tmptlv; + newtlv++; + data = NULL; + if (opt_verbose > 1) + log_debug ("new parser context\n"); + if (tlv_next (tlv)) goto bailout; if (tlv_expect_sequence (tlv)) @@ -1572,7 +1596,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (ctx->privatekey2) { err = gpg_error (GPG_ERR_DUP_VALUE); - log_error ("two private kesy have already been received\n"); + log_error ("two private keys have already been received\n"); goto bailout; } privatekey = gcry_calloc (10, sizeof *privatekey); @@ -1607,7 +1631,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (tlv_next (tlv)) goto bailout; - if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) + if (tlv_expect_octet_string (tlv, &data, &datalen)) goto bailout; if (opt_verbose > 1) log_printhex (data, datalen, "ecc q="); @@ -1660,30 +1684,33 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (opt_verbose > 1) log_debug ("restoring parser context\n"); - tlv_parser_release (tlv); - renewed_tlv = 0; - tlv = saved_tlv; + tlv = tlv_parser_release (tlv); + log_assert (tlv); + newtlv--; where = "shrouded_key_bag.attribute_set"; /* Check for an optional set of attributes. */ - if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_SET)) + err = tlv_next (tlv); + if (err && gpg_err_code (err) == GPG_ERR_EOF) + err = 0; + else if (err) + goto bailout; + else if (!tlv_expect_set (tlv)) { - if (tlv_next (tlv)) - goto bailout; - err = tlv_expect_set (tlv); - if (err) - goto bailout; tlv_parser_skip (tlv); if (opt_verbose) log_info ("skipping %s\n", where); } + else + tlv_parser_set_pending (tlv); leave: gcry_free (plain); - if (renewed_tlv) + while (newtlv) { - tlv_parser_release (tlv); + tlv = tlv_parser_release (tlv); + newtlv--; if (opt_verbose > 1) log_debug ("parser context released\n"); } @@ -1758,7 +1785,7 @@ parse_cert_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (tlv_next (tlv)) goto bailout; - if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) + if (tlv_expect_octet_string (tlv, &data, &datalen)) goto bailout; /* Return the certificate from the octet string. */ @@ -1771,18 +1798,19 @@ parse_cert_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) */ where = "certbag.attribute_set"; /* Check for an optional set of attributes. */ - if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_SET)) + err = tlv_next (tlv); + if (err && gpg_err_code (err) == GPG_ERR_EOF) + err = 0; + else if (err) + goto bailout; + else if (!tlv_expect_set (tlv)) { - if (tlv_next (tlv)) - goto bailout; - err = tlv_expect_set (tlv); - if (err) - goto bailout; tlv_parser_skip (tlv); if (opt_verbose) log_info ("skipping %s\n", where); } - + else + tlv_parser_set_pending (tlv); leave: return err; @@ -1809,6 +1837,10 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) const unsigned char *oid; size_t oidlen; unsigned int startlevel; + const unsigned char *data; + size_t datalen; + tlv_parser_t tmptlv; + int newtlv = 0; /* True if the TLV must be released. */ if (opt_verbose) log_info ("processing bag data\n"); @@ -1825,17 +1857,20 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) if (tlv_next (tlv)) goto bailout; - if (tlv_expect_octet_string (tlv, 1, NULL, NULL)) + if (tlv_expect_octet_string (tlv, &data, &datalen)) goto bailout; - if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_OCTET_STRING)) + tmptlv = tlv_parser_new (data, datalen, opt_verbose, tlv); + if (!tmptlv) { - if (tlv_next (tlv)) - goto bailout; - err = tlv_expect_octet_string (tlv, 1, NULL, NULL); - if (err) - goto bailout; + err = gpg_error_from_syserror (); + goto bailout; } + tlv = tmptlv; + newtlv = 1; + data = NULL; + if (opt_verbose > 1) + log_debug ("new parser context for embedded octet string\n"); /* Expect: * SEQUENCE @@ -1894,6 +1929,12 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) err = 0; leave: + if (newtlv) + { + tlv_parser_release (tlv); + if (opt_verbose > 1) + log_debug ("parser context released\n"); + } return err; bailout: @@ -1930,6 +1971,10 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, int intval; unsigned int startlevel; int i; + const unsigned char *data; + size_t datalen; + tlv_parser_t tmptlv; + int newtlv = 0; /* True if the TLV must be released. */ *r_badpass = 0; @@ -1937,7 +1982,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, ctx.certcbarg = certcbarg; ctx.password = pw; - tlv = tlv_parser_new (buffer, length, opt_verbose); + tlv = tlv_parser_new (buffer, length, opt_verbose, 0); if (!tlv) { err = gpg_error_from_syserror (); @@ -1979,17 +2024,21 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, if ((err = tlv_next (tlv))) goto bailout; - if ((err = tlv_expect_octet_string (tlv, 1, NULL, NULL))) + if ((err = tlv_expect_octet_string (tlv, &data, &datalen))) goto bailout; - if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_OCTET_STRING)) + tmptlv = tlv_parser_new (data, datalen, opt_verbose, NULL); + if (!tmptlv) { - if ((err = tlv_next (tlv))) - goto bailout; - err = tlv_expect_octet_string (tlv, 1, NULL, NULL); - if (err) - goto bailout; + err = gpg_error_from_syserror (); + goto bailout; } + tlv = tmptlv; + newtlv = 1; + data = NULL; + + if (opt_verbose > 1) + log_debug ("new parser context for embedded octet string\n"); where = "bags"; if ((err = tlv_next (tlv))) @@ -2046,6 +2095,12 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, goto bailout; err = 0; + if (newtlv) + { + tlv = tlv_parser_release (tlv); + if (opt_verbose > 1) + log_debug ("parser context released\n"); + } tlv_parser_release (tlv); if (r_curve) *r_curve = ctx.curve; @@ -2064,6 +2119,12 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, return ctx.privatekey; bailout: + if (newtlv) + { + tlv = tlv_parser_release (tlv); + if (opt_verbose > 1) + log_debug ("parser context released\n"); + } *r_badpass = ctx.badpass; log_error ("%s(%s): @%04zu lvl=%u %s: %s - %s\n", __func__, where, |