From c1f78634ec3927ddcfdc4687bc6e408c658a0ece Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 5 Oct 2023 10:02:59 +0200 Subject: sm: Improve the octet string cramming for pkcs#12 * sm/minip12.c (need_octet_string_cramming): New. (tlv_expect_object, tlv_expect_octet_string): Run the test before cramming. * sm/minip12.c (ENABLE_DER_STRUCT_DUMPING): New but undefined macro for debug purposes. (bag_decrypted_data_p, bag_data_p): Use macro to allow dumping. -- This bug was exhibited by importing a gpgsm exported EC certificate. We use an extra test instead of retrying to allow retruning an error from malloc failure. And well, for easier reading of the code. GnuPG-bug-id: 6536 --- sm/minip12.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 17 deletions(-) (limited to 'sm/minip12.c') diff --git a/sm/minip12.c b/sm/minip12.c index 265243f3e..ed80534b6 100644 --- a/sm/minip12.c +++ b/sm/minip12.c @@ -50,6 +50,8 @@ #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] = { @@ -111,6 +113,8 @@ static unsigned char const data_mactemplate[51] = { #define DATA_MACTEMPLATE_MAC_OFF 17 #define DATA_MACTEMPLATE_SALT_OFF 39 +/* Note that the BMP String in this template reads: + * "GnuPG exported certificate ffffffff" */ static unsigned char const data_attrtemplate[106] = { 0x31, 0x7c, 0x30, 0x55, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x14, 0x31, @@ -210,6 +214,8 @@ static int opt_verbose; 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); @@ -560,7 +566,7 @@ tlv_expect_sequence (struct tlv_ctx_s *tlv) return _tlv_push (tlv); } -/* Variant of tlv_expect_sequence to be used for the ouyter sequence +/* Variant of tlv_expect_sequence to be used for the outer sequence * of an object which might have padding after the ASN.1 data. */ static gpg_error_t tlv_expect_top_sequence (struct tlv_ctx_s *tlv) @@ -618,7 +624,8 @@ tlv_expect_object (struct tlv_ctx_s *tlv, int class, int tag, if (!tlv->ti.length) return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); - if (class == CLASS_CONTEXT && tag == 0 && tlv->ti.is_constructed) + if (class == CLASS_CONTEXT && tag == 0 && tlv->ti.is_constructed + && need_octet_string_cramming (p, tlv->ti.length)) { char *newbuffer; @@ -665,7 +672,8 @@ tlv_expect_octet_string (struct tlv_ctx_s *tlv, int encapsulates, if (!(n=tlv->ti.length)) return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); - if (encapsulates && tlv->ti.is_constructed) + if (encapsulates && tlv->ti.is_constructed + && need_octet_string_cramming (p, n)) { char *newbuffer; @@ -859,6 +867,39 @@ cram_octet_string (const unsigned char *input, size_t length, } +/* 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 (parse_tag (&s, &n, &ti)) + return 0; + if (ti.class == CLASS_UNIVERSAL && ti.tag == TAG_OCTET_STRING + && !ti.ndef && !ti.is_constructed) + { + s += ti.length; + n -= ti.length; + } + else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) + break; /* Ready */ + else + return 0; + } + + return 1; +} + + static int string_to_key (int id, char *salt, size_t saltlen, int iter, const char *pw, int req_keylen, unsigned char *keybuf) @@ -1173,13 +1214,15 @@ bag_decrypted_data_p (const void *plaintext, size_t length) const unsigned char *p = plaintext; size_t n = length; - /* { */ - /* # 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); */ - /* } */ +#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*/ if (parse_tag (&p, &n, &ti)) return 0; @@ -1696,13 +1739,15 @@ bag_data_p (const void *plaintext, size_t length) const unsigned char *p = plaintext; size_t n = length; -/* { */ -/* # 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); */ -/* } */ +#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*/ if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) return 0; -- cgit v1.2.3 From 24b3a5a5794db4bb69b38a1df099d5e59cccf2b3 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 6 Oct 2023 10:57:12 +0200 Subject: sm: Support more HMAC algos in the pkcs#12 parser. * sm/minip12.c (oid_hmacWithSHA1): New. Also for the SHA-2 algos. (digest_algo_from_oid): New. (set_key_iv_pbes2): Add arg digest_algo. (crypt_block): Ditto. (decrypt_block): Ditto. (parse_bag_encrypted_data): Parse the optional prf part and get the hmac algorithm. (parse_shrouded_key_bag): Ditto. (p12_build): Pass SHA1 for digest_algo. * sm/t-minip12.c (run_one_test): Print failed values in verbose mode. * tests/cms/samplekeys/nistp256-openssl-self-signed.p12: New. * tests/cms/samplekeys/Description-p12: Add this one. * tests/cms/Makefile.am (EXTRA_DIST): Ditto. -- This supports the modern algorithms, i.e. using SHA256 for the KDF which is the default in openssl unless the -legacy option is used. GnuPG-bug-id: 6536 --- sm/minip12.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 118 insertions(+), 13 deletions(-) (limited to 'sm/minip12.c') diff --git a/sm/minip12.c b/sm/minip12.c index ed80534b6..e63d9a95d 100644 --- a/sm/minip12.c +++ b/sm/minip12.c @@ -83,6 +83,17 @@ static unsigned char const oid_aes128_CBC[9] = { static unsigned char const oid_aes256_CBC[9] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2A }; +static unsigned char const oid_hmacWithSHA1[8] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x07 }; +static unsigned char const oid_hmacWithSHA224[8] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x08 }; +static unsigned char const oid_hmacWithSHA256[8] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x09 }; +static unsigned char const oid_hmacWithSHA384[8] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x0A }; +static unsigned char const oid_hmacWithSHA512[8] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x0B }; + static unsigned char const oid_rsaEncryption[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; static unsigned char const oid_pcPublicKey[7] = { @@ -241,6 +252,32 @@ dump_tag_info (const char *text, struct tag_info *ti) } +static int +digest_algo_from_oid (unsigned char const *oid, size_t oidlen) +{ + int algo; + + if (oidlen == DIM(oid_hmacWithSHA1) && + !memcmp (oid, oid_hmacWithSHA1, oidlen)) + algo = GCRY_MD_SHA1; + else if (oidlen == DIM(oid_hmacWithSHA224) && + !memcmp (oid, oid_hmacWithSHA224, oidlen)) + algo = GCRY_MD_SHA224; + else if (oidlen == DIM(oid_hmacWithSHA256) && + !memcmp (oid, oid_hmacWithSHA256, oidlen)) + algo = GCRY_MD_SHA256; + else if (oidlen == DIM(oid_hmacWithSHA384) && + !memcmp (oid, oid_hmacWithSHA384, oidlen)) + algo = GCRY_MD_SHA384; + else if (oidlen == DIM(oid_hmacWithSHA512) && + !memcmp (oid, oid_hmacWithSHA512, oidlen)) + algo = GCRY_MD_SHA512; + else + algo = 0; + return algo; +} + + /* Wrapper around tlv_builder_add_ptr to add an OID. When we * eventually put the whole tlv_builder stuff into Libksba, we can add * such a function there. Right now we don't do this to avoid a @@ -1029,13 +1066,14 @@ set_key_iv (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter, static int set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter, - const void *iv, size_t ivlen, const char *pw, int algo) + const void *iv, size_t ivlen, const char *pw, + int cipher_algo, int digest_algo) { unsigned char *keybuf; size_t keylen; int rc; - keylen = gcry_cipher_get_algo_keylen (algo); + keylen = gcry_cipher_get_algo_keylen (cipher_algo); if (!keylen) return -1; keybuf = gcry_malloc_secure (keylen); @@ -1043,7 +1081,7 @@ set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter, return -1; rc = gcry_kdf_derive (pw, strlen (pw), - GCRY_KDF_PBKDF2, GCRY_MD_SHA1, + GCRY_KDF_PBKDF2, digest_algo, salt, saltlen, iter, keylen, keybuf); if (rc) { @@ -1074,7 +1112,7 @@ set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter, static void crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen, int iter, const void *iv, size_t ivlen, - const char *pw, int cipher_algo, int encrypt) + const char *pw, int cipher_algo, int digest_algo, int encrypt) { gcry_cipher_hd_t chd; int rc; @@ -1088,7 +1126,8 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen, } if ((cipher_algo == GCRY_CIPHER_AES128 || cipher_algo == GCRY_CIPHER_AES256) - ? set_key_iv_pbes2 (chd, salt, saltlen, iter, iv, ivlen, pw, cipher_algo) + ? set_key_iv_pbes2 (chd, salt, saltlen, iter, iv, ivlen, pw, + cipher_algo, digest_algo) : set_key_iv (chd, salt, saltlen, iter, pw, cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24)) { @@ -1125,7 +1164,7 @@ static void decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length, char *salt, size_t saltlen, int iter, const void *iv, size_t ivlen, - const char *pw, int cipher_algo, + const char *pw, int cipher_algo, int digest_algo, int (*check_fnc) (const void *, size_t)) { static const char * const charsets[] = { @@ -1197,7 +1236,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, 0); + convertedpw? convertedpw:pw, cipher_algo, digest_algo, 0); if (check_fnc (plaintext, length)) break; /* Decryption succeeded. */ } @@ -1257,6 +1296,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) int renewed_tlv = 0; int loopcount; unsigned int startlevel; + int digest_algo = GCRY_MD_SHA1; where = "bag.encryptedData"; if (opt_verbose) @@ -1326,6 +1366,8 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) /*FIXME: This code is duplicated in parse_shrouded_key_bag. */ if (is_pbes2) { + size_t parmlen; /* Remaining length of the parameter sequence. */ + where = "pkcs5PBES2-params"; if (tlv_next (tlv)) goto bailout; @@ -1352,11 +1394,13 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) goto bailout; if (tlv_expect_sequence (tlv)) goto bailout; + parmlen = tlv->ti.length; if (tlv_next (tlv)) goto bailout; if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; + parmlen -= tlv->ti.length + tlv->ti.nhdr; if (datalen < 8 || datalen > sizeof salt) { log_info ("bad length of salt (%zu)\n", datalen); @@ -1370,6 +1414,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) goto bailout; if ((err = tlv_expect_integer (tlv, &intval))) goto bailout; + parmlen -= tlv->ti.length + tlv->ti.nhdr; if (!intval) /* Not a valid iteration count. */ { err = gpg_error (GPG_ERR_INV_VALUE); @@ -1377,8 +1422,34 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) } iter = intval; - /* Note: We don't support the optional parameters but assume - that the algorithmIdentifier follows. */ + if (parmlen > 2) /* There is the optional prf. */ + { + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_sequence (tlv)) + goto bailout; + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_object_id (tlv, &oid, &oidlen)) + goto bailout; + digest_algo = digest_algo_from_oid (oid, oidlen); + if (!digest_algo) + { + gpgrt_log_printhex (oid, oidlen, "kdf digest algo:"); + err = gpg_error (GPG_ERR_DIGEST_ALGO); + goto bailout; + } + if (opt_verbose > 1) + log_debug ("kdf digest algo = %d\n", digest_algo); + + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_null (tlv)) + tlv_set_pending (tlv); /* NULL tag missing - ignore this. */ + } + else + digest_algo = GCRY_MD_SHA1; + if (tlv_next (tlv)) goto bailout; if (tlv_expect_sequence (tlv)) @@ -1468,6 +1539,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) iv, is_pbes2?16:0, ctx->password, is_pbes2 ? (is_aes256?GCRY_CIPHER_AES256:GCRY_CIPHER_AES128) : is_3des ? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40, + digest_algo, bag_decrypted_data_p); /* We do not need the TLV anymore and allocated a new one. */ @@ -1778,6 +1850,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) unsigned char *plain = NULL; int is_pbes2 = 0; int is_aes256 = 0; + int digest_algo = GCRY_MD_SHA1; where = "shrouded_key_bag"; if (opt_verbose) @@ -1819,6 +1892,8 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) if (is_pbes2) { + size_t parmlen; /* Remaining length of the parameter sequence. */ + where = "shrouded_key_bag.pkcs5PBES2-params"; if (tlv_next (tlv)) goto bailout; @@ -1842,11 +1917,13 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) goto bailout; if (tlv_expect_sequence (tlv)) goto bailout; + parmlen = tlv->ti.length; if (tlv_next (tlv)) goto bailout; if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; + parmlen -= tlv->ti.length + tlv->ti.nhdr; if (datalen < 8 || datalen > sizeof salt) { log_info ("bad length of salt (%zu) for AES\n", datalen); @@ -1860,6 +1937,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) goto bailout; if ((err = tlv_expect_integer (tlv, &intval))) goto bailout; + parmlen -= tlv->ti.length + tlv->ti.nhdr; if (!intval) /* Not a valid iteration count. */ { err = gpg_error (GPG_ERR_INV_VALUE); @@ -1867,8 +1945,34 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) } iter = intval; - /* Note: We don't support the optional parameters but assume - that the algorithmIdentifier follows. */ + if (parmlen > 2) /* There is the optional prf. */ + { + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_sequence (tlv)) + goto bailout; + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_object_id (tlv, &oid, &oidlen)) + goto bailout; + digest_algo = digest_algo_from_oid (oid, oidlen); + if (!digest_algo) + { + gpgrt_log_printhex (oid, oidlen, "kdf digest algo:"); + err = gpg_error (GPG_ERR_DIGEST_ALGO); + goto bailout; + } + if (opt_verbose > 1) + log_debug ("kdf digest algo = %d\n", digest_algo); + + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_null (tlv)) + tlv_set_pending (tlv); /* NULL tag missing - ignore this. */ + } + else + digest_algo = GCRY_MD_SHA1; + if (tlv_next (tlv)) goto bailout; if (tlv_expect_sequence (tlv)) @@ -1954,6 +2058,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) iv, is_pbes2? 16:0, ctx->password, is_pbes2 ? (is_aes256?GCRY_CIPHER_AES256:GCRY_CIPHER_AES128) : GCRY_CIPHER_3DES, + digest_algo, bag_data_p); @@ -3468,7 +3573,7 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen, /* Encrypt it. */ gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0, pw, - GCRY_CIPHER_RFC2268_40, 1); + GCRY_CIPHER_RFC2268_40, GCRY_MD_SHA1, 1); /* Encode the encrypted stuff into a bag. */ seqlist[seqlistidx].buffer = build_cert_bag (buffer, buflen, salt, &n); @@ -3500,7 +3605,7 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen, /* Encrypt it. */ gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0, - pw, GCRY_CIPHER_3DES, 1); + pw, GCRY_CIPHER_3DES, GCRY_MD_SHA1, 1); /* Encode the encrypted stuff into a bag. */ if (cert && certlen) -- cgit v1.2.3 From 5601f5db9862e23140bccc0e603e42164fe02296 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 10 Oct 2023 11:36:26 +0200 Subject: gpgsm: Improvements for NDEF in the pkcs#12 parser * sm/minip12.c (_tlv_push): Handle NDEF more correctly. (tlv_expect_octet_string): Do not bail out on NDEF. (dump_tag_info): Print some more infos. -- We do not have a complete test case for this. We need to further analyze T6752 to see what Mozilla is doing here. In any case with this patch we get a bit further and don't bail out at the ndef. GnuPG-bug-id: 6536, 6752 --- sm/minip12.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'sm/minip12.c') diff --git a/sm/minip12.c b/sm/minip12.c index e63d9a95d..3705f3770 100644 --- a/sm/minip12.c +++ b/sm/minip12.c @@ -241,14 +241,21 @@ p12_set_verbosity (int verbose, int debug) static void -dump_tag_info (const char *text, struct tag_info *ti) +dump_tag_info (const char *text, struct tlv_ctx_s *tlv) { - if (opt_verbose > 1) - log_debug ("p12_parse(%s): ti.class=%d tag=%lu len=%zu nhdr=%zu %s%s\n", - text, - ti->class, ti->tag, ti->length, ti->nhdr, - ti->is_constructed?" cons":"", - ti->ndef?" ndef":""); + struct tag_info *ti; + + if (opt_verbose < 2) + return; + + ti = &tlv->ti; + log_debug ("p12_parse(%s): ti.class=%-2d tag=%-2lu len=%-4zu nhdr=%zu %s %s" + " (%u:%zu.%zu)\n", + text, + ti->class, ti->tag, ti->length, ti->nhdr, + ti->is_constructed?"cons":" ", + ti->ndef?"ndef":" ", + tlv->stacklen, tlv->bufsize, tlv->offset); } @@ -473,7 +480,13 @@ _tlv_push (struct tlv_ctx_s *tlv) tlv->stack[tlv->stacklen].in_ndef = tlv->in_ndef; tlv->stacklen++; tlv->buffer += tlv->offset; - tlv->bufsize = tlv->ti.length; + if (tlv->ti.ndef) + { + log_assert (tlv->bufsize >= tlv->offset); + tlv->bufsize -= tlv->offset; + } + else + tlv->bufsize = tlv->ti.length; tlv->offset = 0; tlv->in_ndef = tlv->ti.ndef; return 0; @@ -551,7 +564,7 @@ tlv_next (struct tlv_ctx_s *tlv) /* Set offset to the value of the TLV. */ tlv->offset += tlv->bufsize - tlv->offset - n; - dump_tag_info ("tlv_next", &tlv->ti); + dump_tag_info ("tlv_next", tlv); return 0; } @@ -706,7 +719,7 @@ tlv_expect_octet_string (struct tlv_ctx_s *tlv, int encapsulates, && (!tlv->ti.is_constructed || encapsulates))) return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); p = tlv->buffer + tlv->offset; - if (!(n=tlv->ti.length)) + if (!(n=tlv->ti.length) && !tlv->ti.ndef) return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); if (encapsulates && tlv->ti.is_constructed -- cgit v1.2.3 From 7661d2fbc6eb533016df63a86ec3e35bf00cfb1f Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 24 Oct 2023 09:22:13 +0200 Subject: sm: Another partly rewrite of minip12.c * sm/minip12.c (struct tlv_ctx_s): Add origbuffer and origbufsize. Remove pop_count. Rename offset to length. (dump_tag_info, _dump_tag_info): Rewrite. (dump_tlv_ctx, _dump_tlv_ctx): Rewrite. (tlv_new): Init origbuffer. (_tlv_peek): Add arg ti. (tlv_peek): New. (tlv_peek_null): New. (_tlv_push): Rewrite. (_tlv_pop): Rewrite. (tlv_next): New macro. Move old code to ... (_tlv_next): this. Add arg lno. Pop remaining end tags. (tlv_popped): Remove. (tlv_expect_object): Handle ndef. (tlv_expect_octet_string): Ditto. (parse_bag_encrypted_data): Use nesting level to control the inner loop. (parse_shrouded_key_bag): Likewise. (parse_bag_data): Handle surplus octet strings. (p12_parse): Ditto. * sm/minip12.c (decrypt_block): Strip the padding. (tlv_expect_top_sequence): Remove. Replace callers by tlv_expect_sequence. * tests/cms/samplekeys/t6752-ov-user-ff.p12: New sample key. * tests/cms/samplekeys/Description-p12: Add its description -- This patch improves the BER parser by simplifying it. Now tlv_next pops off and thus closes all containers regardless on whether they are length bounded or ndef. tlv_set_pending is now always used to undo the effect of a tlv_next in a loop condition which was terminated by a nesting level change. Instead of using the length as seen in the decrypted container we now remove the padding and let the BER parser do its work. This might have a negative effect on pkcs#12 objects which are not correctly padded but we don't have any example of such broken objects. GnuPG-bug-id: 6752 --- sm/minip12.c | 501 +++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 319 insertions(+), 182 deletions(-) (limited to 'sm/minip12.c') diff --git a/sm/minip12.c b/sm/minip12.c index 3705f3770..98eb4e3b5 100644 --- a/sm/minip12.c +++ b/sm/minip12.c @@ -174,11 +174,14 @@ struct bufferlist_s /* An object to control the ASN.1 parsing. */ struct tlv_ctx_s { + /* The orginal buffer with the entire pkcs#12 object and its length. */ + const unsigned char *origbuffer; + size_t origbufsize; + /* The current buffer we are working on and its length. */ const unsigned char *buffer; size_t bufsize; - size_t offset; /* The current offset into this buffer. */ int in_ndef; /* Flag indicating that we are in a NDEF. */ int pending; /* The last tlv_next has not yet been processed. */ @@ -186,15 +189,14 @@ struct tlv_ctx_s gpg_error_t lasterr; /* Last error from tlv function. */ const char *lastfunc;/* Name of last called function. */ - struct bufferlist_s *bufferlist; /* To keep track of amlloced buffers. */ + struct bufferlist_s *bufferlist; /* To keep track of malloced buffers. */ - unsigned int pop_count;/* Number of pops by tlv_next. */ unsigned int stacklen; /* Used size of the stack. */ struct { const unsigned char *buffer; /* Saved value of BUFFER. */ size_t bufsize; /* Saved value of BUFSIZE. */ - size_t offset; /* Saved value of OFFSET. */ - int in_ndef; /* Saved IN_NDEF flag. */ + size_t length; /* Length of the container (ti.length). */ + int in_ndef; /* Saved IN_NDEF flag (ti.ndef). */ } stack[TLV_MAX_DEPTH]; }; @@ -240,8 +242,9 @@ p12_set_verbosity (int verbose, int debug) } +#define dump_tag_info(a,b) _dump_tag_info ((a),__LINE__,(b)) static void -dump_tag_info (const char *text, struct tlv_ctx_s *tlv) +_dump_tag_info (const char *text, int lno, struct tlv_ctx_s *tlv) { struct tag_info *ti; @@ -249,13 +252,31 @@ dump_tag_info (const char *text, struct tlv_ctx_s *tlv) return; ti = &tlv->ti; - log_debug ("p12_parse(%s): ti.class=%-2d tag=%-2lu len=%-4zu nhdr=%zu %s %s" - " (%u:%zu.%zu)\n", - text, + + 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":" ", - tlv->stacklen, tlv->bufsize, tlv->offset); + ti->is_constructed?" cons":"", + ti->ndef?" ndef":""); +} + + +#define dump_tlv_ctx(a,b,c) _dump_tlv_ctx ((a),(b),__LINE__,(c)) +static void +_dump_tlv_ctx (const char *text, const char *text2, + int lno, struct tlv_ctx_s *tlv) +{ + if (opt_verbose < 2) + return; + + log_debug ("p12_parse:%s%s%s:%d: @%04zu lvl=%u %s\n", + text, + text2? "/":"", text2? text2:"", + lno, + (size_t)(tlv->buffer - tlv->origbuffer), + tlv->stacklen, + tlv->in_ndef? " in-ndef":""); } @@ -413,6 +434,8 @@ tlv_new (const unsigned char *buffer, size_t bufsize) tlv = xtrycalloc (1, sizeof *tlv); if (tlv) { + tlv->origbuffer = buffer; + tlv->origbufsize = bufsize; tlv->buffer = buffer; tlv->bufsize = bufsize; } @@ -454,17 +477,45 @@ tlv_release (struct tlv_ctx_s *tlv) } -/* Helper for tlv_next and tlv_peek. */ +/* Helper for the tlv_peek functions. */ static gpg_error_t -_tlv_peek (struct tlv_ctx_s *tlv, size_t *r_n) +_tlv_peek (struct tlv_ctx_s *tlv, struct tag_info *ti) { const unsigned char *p; + size_t n; - if (tlv->offset > tlv->bufsize) + /* 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); - p = tlv->buffer + tlv->offset; - *r_n = tlv->bufsize - tlv->offset; - return parse_tag (&p, r_n, &tlv->ti); + 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. */ +static int +tlv_peek (struct tlv_ctx_s *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. */ +static int +tlv_peek_null (struct tlv_ctx_s *tlv) +{ + struct tag_info ti; + + return (!_tlv_peek (tlv, &ti) + && ti.class == CLASS_UNIVERSAL && ti.tag == TAG_NULL + && !ti.is_constructed && !ti.length); } @@ -472,23 +523,30 @@ _tlv_peek (struct tlv_ctx_s *tlv, size_t *r_n) static gpg_error_t _tlv_push (struct tlv_ctx_s *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) return (tlv->lasterr = gpg_error (GPG_ERR_TOO_MANY)); tlv->stack[tlv->stacklen].buffer = tlv->buffer; tlv->stack[tlv->stacklen].bufsize = tlv->bufsize; - tlv->stack[tlv->stacklen].offset = tlv->offset; tlv->stack[tlv->stacklen].in_ndef = tlv->in_ndef; + tlv->stack[tlv->stacklen].length = tlv->ti.length; tlv->stacklen++; - tlv->buffer += tlv->offset; - if (tlv->ti.ndef) + + tlv->in_ndef = tlv->ti.ndef; + + /* We set the size of the buffer to the TLV length if it is known or + * else to the size of the remaining entire buffer. */ + if (tlv->in_ndef) { - log_assert (tlv->bufsize >= tlv->offset); - tlv->bufsize -= tlv->offset; + if ((tlv->buffer - tlv->origbuffer) > tlv->origbufsize) + return (tlv->lasterr = gpg_error (GPG_ERR_BUG)); + tlv->bufsize = tlv->origbufsize - (tlv->buffer - tlv->origbuffer); } else tlv->bufsize = tlv->ti.length; - tlv->offset = 0; - tlv->in_ndef = tlv->ti.ndef; + + dump_tlv_ctx (__func__, NULL, tlv); return 0; } @@ -497,74 +555,102 @@ _tlv_push (struct tlv_ctx_s *tlv) static gpg_error_t _tlv_pop (struct tlv_ctx_s *tlv) { - size_t saveoff; + size_t lastlen; + /* 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 + * are positioned at the value of the last container. */ if (!tlv->stacklen) return gpg_error (GPG_ERR_EOF); - saveoff = tlv->offset; - tlv->stacklen--; - tlv->buffer = tlv->stack[tlv->stacklen].buffer; - tlv->bufsize = tlv->stack[tlv->stacklen].bufsize; - tlv->offset = tlv->stack[tlv->stacklen].offset; tlv->in_ndef = tlv->stack[tlv->stacklen].in_ndef; + if (tlv->in_ndef) + { + /* We keep buffer but adjust bufsize to the end of the origbuffer. */ + if ((tlv->buffer - tlv->origbuffer) > tlv->origbufsize) + return (tlv->lasterr = gpg_error (GPG_ERR_BUG)); + tlv->bufsize = tlv->origbufsize - (tlv->buffer - tlv->origbuffer); + } + 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) + { + log_debug ("%s: container length larger than buffer (%zu/%zu)\n", + __func__, lastlen, tlv->bufsize); + return gpg_error (GPG_ERR_INV_BER); + } + tlv->buffer += lastlen; + tlv->bufsize -= lastlen; + } - /* Move offset of the container to the end of the container. */ - tlv->offset += saveoff; - if (tlv->offset > tlv->bufsize) - return gpg_error (GPG_ERR_INV_BER); - - tlv->pop_count++; + dump_tlv_ctx (__func__, NULL, tlv); return 0; } -/* Parse the next tag and value. Also detect the end of a container; - * tlv_popped() can be used to detect this. */ +/* Parse the next tag and value. Also detect the end of a + * container. */ +#define tlv_next(a) _tlv_next ((a), __LINE__) static gpg_error_t -tlv_next (struct tlv_ctx_s *tlv) +_tlv_next (struct tlv_ctx_s *tlv, int lno) { gpg_error_t err; - size_t n; - tlv->pop_count = 0; tlv->lasterr = 0; tlv->lastfunc = __func__; + if (tlv->pending) { tlv->pending = 0; + if (opt_verbose > 1) + log_debug ("%s: tlv_next skipped\n", __func__); return 0; } - if (!tlv->in_ndef && tlv->offset == tlv->bufsize) + if (opt_verbose > 1) + log_debug ("%s: tlv_next called\n", __func__); + /* If we are at the end of an ndef container pop the stack. */ + if (!tlv->in_ndef && !tlv->bufsize) { - /* We are at the end of a container. Pop the stack. */ do err = _tlv_pop (tlv); - while (!err && !tlv->in_ndef && tlv->offset == tlv->bufsize); + while (!err && !tlv->in_ndef && !tlv->bufsize); if (err) return (tlv->lasterr = err); + if (opt_verbose > 1) + log_debug ("%s: container(s) closed due to size\n", __func__); } - err = _tlv_peek (tlv, &n); + again: + /* Get the next tag. */ + err = parse_tag (&tlv->buffer, &tlv->bufsize, &tlv->ti); if (err) - return err; + { + if (opt_verbose > 1) + log_debug ("%s: reading tag returned err=%d\n", __func__, err); + return err; + } + + /* 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 && !tlv->ti.tag && !tlv->ti.is_constructed)) { - /* End tag while in ndef container. Skip the tag, and pop. */ - tlv->offset += n - (tlv->bufsize - tlv->offset); - err = _tlv_pop (tlv); - /* FIXME: We need to peek whether there is another end tag and - * pop again. We can't modify the TLV object, though. */ + do + err = _tlv_pop (tlv); + while (!err && !tlv->in_ndef && !tlv->bufsize); if (err) return (tlv->lasterr = err); + if (opt_verbose > 1) + log_debug ("%s: container(s) closed due to end tag\n", __func__); + goto again; } - /* Set offset to the value of the TLV. */ - tlv->offset += tlv->bufsize - tlv->offset - n; - dump_tag_info ("tlv_next", tlv); + _dump_tag_info (__func__, lno, tlv); return 0; } @@ -577,15 +663,6 @@ tlv_level (struct tlv_ctx_s *tlv) } -/* If called right after tlv_next the number of container levels - * popped are returned. */ -static unsigned int -tlv_popped (struct tlv_ctx_s *tlv) -{ - return tlv->pop_count; -} - - /* Set a flag to indicate that the last tlv_next has not yet been * consumed. */ static void @@ -595,12 +672,15 @@ tlv_set_pending (struct tlv_ctx_s *tlv) } -/* Skip over the value of the current tag. */ +/* Skip over the value of the current tag. Does not yet work for ndef + * containers. */ static void tlv_skip (struct tlv_ctx_s *tlv) { tlv->lastfunc = __func__; - tlv->offset += tlv->ti.length; + log_assert (tlv->bufsize >= tlv->ti.length); + tlv->buffer += tlv->ti.length; + tlv->bufsize -= tlv->ti.length; } @@ -616,19 +696,6 @@ tlv_expect_sequence (struct tlv_ctx_s *tlv) return _tlv_push (tlv); } -/* Variant of tlv_expect_sequence to be used for the outer sequence - * of an object which might have padding after the ASN.1 data. */ -static gpg_error_t -tlv_expect_top_sequence (struct tlv_ctx_s *tlv) -{ - tlv->lastfunc = __func__; - if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_SEQUENCE - && tlv->ti.is_constructed)) - return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - tlv->bufsize = tlv->ti.nhdr + tlv->ti.length; - return _tlv_push (tlv); -} - /* Expect that the current tag is a context tag and setup the context * for processing. The tag of the context is returned at R_TAG. */ @@ -666,20 +733,28 @@ tlv_expect_object (struct tlv_ctx_s *tlv, int class, int tag, { gpg_error_t err; const unsigned char *p; + size_t n; + int needpush = 0; tlv->lastfunc = __func__; if (!(tlv->ti.class == class && tlv->ti.tag == tag)) return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - p = tlv->buffer + tlv->offset; - if (!tlv->ti.length) + p = tlv->buffer; + n = tlv->ti.length; + if (!n && tlv->ti.ndef) + { + n = tlv->bufsize; + needpush = 1; + } + 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, tlv->ti.length)) + && need_octet_string_cramming (p, n)) { char *newbuffer; - newbuffer = cram_octet_string (p, tlv->ti.length, r_datalen); + newbuffer = cram_octet_string (p, n, r_datalen); if (!newbuffer) return (tlv->lasterr = gpg_error (GPG_ERR_BAD_BER)); err = tlv_register_buffer (tlv, newbuffer); @@ -693,10 +768,15 @@ tlv_expect_object (struct tlv_ctx_s *tlv, int class, int tag, else { *r_data = p; - *r_datalen = tlv->ti.length; + *r_datalen = n; } + if (needpush) + return _tlv_push (tlv); - tlv->offset += 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; return 0; } @@ -718,7 +798,7 @@ tlv_expect_octet_string (struct tlv_ctx_s *tlv, int encapsulates, if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OCTET_STRING && (!tlv->ti.is_constructed || encapsulates))) return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - p = tlv->buffer + tlv->offset; + p = tlv->buffer; if (!(n=tlv->ti.length) && !tlv->ti.ndef) return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); @@ -748,19 +828,10 @@ tlv_expect_octet_string (struct tlv_ctx_s *tlv, int encapsulates, if (encapsulates) return _tlv_push (tlv); - tlv->offset += tlv->ti.length; - return 0; -} - - -/* Expect a NULL tag. */ -static gpg_error_t -tlv_expect_null (struct tlv_ctx_s *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)); + 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; return 0; } @@ -778,7 +849,7 @@ tlv_expect_integer (struct tlv_ctx_s *tlv, int *r_value) if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_INTEGER && !tlv->ti.is_constructed)) return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - p = tlv->buffer + tlv->offset; + p = tlv->buffer; if (!(n=tlv->ti.length)) return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); @@ -794,7 +865,10 @@ tlv_expect_integer (struct tlv_ctx_s *tlv, int *r_value) return (tlv->lasterr = gpg_error (GPG_ERR_EOVERFLOW)); } *r_value = value; - tlv->offset += 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; return 0; } @@ -816,11 +890,14 @@ tlv_expect_mpinteger (struct tlv_ctx_s *tlv, int ignore_zero, if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_INTEGER && !tlv->ti.is_constructed)) return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - p = tlv->buffer + tlv->offset; + p = tlv->buffer; if (!(n=tlv->ti.length)) return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); - tlv->offset += 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; if (ignore_zero && n == 1 && !*p) return gpg_error (GPG_ERR_FALSE); @@ -842,13 +919,16 @@ tlv_expect_object_id (struct tlv_ctx_s *tlv, if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OBJECT_ID && !tlv->ti.is_constructed)) return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - p = tlv->buffer + tlv->offset; + p = tlv->buffer; if (!(n=tlv->ti.length)) return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); *r_oid = p; *r_oidlen = tlv->ti.length; - tlv->offset += 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; return 0; } @@ -1171,9 +1251,9 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen, and CIPHER_ALGO is the algorithm id to use. CHECK_FNC is a function called with the plaintext and used to check whether the decryption succeeded; i.e. that a correct passphrase has been - given. That function shall return true if the decryption has likely - succeeded. */ -static void + given. The function returns the length of the unpadded plaintext + or 0 on error. */ +static size_t decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length, char *salt, size_t saltlen, int iter, const void *iv, size_t ivlen, @@ -1202,6 +1282,7 @@ decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length, int charsetidx = 0; char *convertedpw = NULL; /* Malloced and converted password or NULL. */ size_t convertedpwsize = 0; /* Allocated length. */ + size_t plainlen = 0; for (charsetidx=0; charsets[charsetidx]; charsetidx++) { @@ -1251,9 +1332,31 @@ decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length, crypt_block (plaintext, length, salt, saltlen, iter, iv, ivlen, convertedpw? convertedpw:pw, cipher_algo, digest_algo, 0); if (check_fnc (plaintext, length)) - break; /* Decryption succeeded. */ + { + /* Strip the pkcs#7 padding. */ + if (length) + { + int n, i; + + n = plaintext[length-1]; + if (n >= length || n > 16) + log_info ("decryption failed; invalid padding size\n"); + else + { + for (i=1; i < n; i++) + if (plaintext[length-i-1] != n) + break; + if (i < n) + log_info ("decryption failed; invalid padding octet\n"); + else + plainlen = length - n; + } + } + break; /* Decryption probably succeeded. */ + } } gcry_free (convertedpw); + return plainlen; } @@ -1308,7 +1411,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) int keyelem_count; int renewed_tlv = 0; int loopcount; - unsigned int startlevel; + unsigned int startlevel, startlevel2; int digest_algo = GCRY_MD_SHA1; where = "bag.encryptedData"; @@ -1455,10 +1558,12 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) if (opt_verbose > 1) log_debug ("kdf digest algo = %d\n", digest_algo); - if (tlv_next (tlv)) - goto bailout; - if (tlv_expect_null (tlv)) - tlv_set_pending (tlv); /* NULL tag missing - ignore this. */ + if (tlv_peek_null (tlv)) + { + /* Read the optional Null tag. */ + if (tlv_next (tlv)) + goto bailout; + } } else digest_algo = GCRY_MD_SHA1; @@ -1548,12 +1653,17 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) log_error ("error allocating decryption buffer\n"); goto bailout; } - decrypt_block (data, plain, datalen, salt, saltlen, iter, + datalen = decrypt_block (data, plain, datalen, salt, saltlen, iter, iv, is_pbes2?16:0, ctx->password, is_pbes2 ? (is_aes256?GCRY_CIPHER_AES256:GCRY_CIPHER_AES128) : is_3des ? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40, digest_algo, bag_decrypted_data_p); + if (!datalen) + { + err = gpg_error (GPG_ERR_DECRYPT_FAILED); + goto bailout; + } /* We do not need the TLV anymore and allocated a new one. */ where = "bag.encryptedData.decrypted-text"; @@ -1570,7 +1680,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) ctx->badpass = 1; goto bailout; } - if (tlv_expect_top_sequence (tlv)) + if (tlv_expect_sequence (tlv)) { ctx->badpass = 1; goto bailout; @@ -1692,7 +1802,8 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) where = "reading.keybag.key-parameters"; keyelem_count = 0; - while (!(err = tlv_next (tlv)) && !tlv_popped (tlv)) + startlevel2 = tlv_level (tlv); + while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel2) { if (keyelem_count >= 9) { @@ -1714,7 +1825,9 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) log_debug ("RSA key parameter %d found\n", keyelem_count); keyelem_count++; } - if (err && gpg_err_code (err) != GPG_ERR_EOF) + if (!err) + tlv_set_pending (tlv); + else if (err && gpg_err_code (err) != GPG_ERR_EOF) goto bailout; err = 0; } @@ -1763,24 +1876,21 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) /* Skip the optional SET with the pkcs12 cert attributes. */ where = "bag.attribute_set"; - err = tlv_next (tlv); - if (gpg_err_code (err) == GPG_ERR_EOF) - break; - if (err) - goto bailout; - err = tlv_expect_set (tlv); - if (!err) - { /* This is the optional set of attributes. Skip it. */ + if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_SET)) + { + if (tlv_next (tlv)) + goto bailout; + err = tlv_expect_set (tlv); + if (err) + goto bailout; tlv_skip (tlv); if (opt_verbose) - log_info ("skipping bag.attribute_set\n"); + log_info ("skipping %s\n", where); } - else if (gpg_err_code (err) == GPG_ERR_INV_OBJ) - tlv_set_pending (tlv); /* The next tlv_next will be skipped. */ - else - goto bailout; } - if (err && gpg_err_code (err) != GPG_ERR_EOF) + if (!err) + tlv_set_pending (tlv); + else if (err && gpg_err_code (err) != GPG_ERR_EOF) { if (!loopcount) /* The first while(tlv_next) failed. */ ctx->badpass = 1; @@ -1804,10 +1914,9 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) bailout: if (!err) err = gpg_error (GPG_ERR_GENERAL); - log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n", + log_error ("%s(%s): lvl=%u (%s): %s - %s\n", __func__, where, tlv? tlv->stacklen : 0, - tlv? tlv->offset : 0, tlv? tlv->lastfunc : "", tlv ? gpg_strerror (tlv->lasterr) : "init failed", gpg_strerror (err)); @@ -1978,10 +2087,12 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) if (opt_verbose > 1) log_debug ("kdf digest algo = %d\n", digest_algo); - if (tlv_next (tlv)) - goto bailout; - if (tlv_expect_null (tlv)) - tlv_set_pending (tlv); /* NULL tag missing - ignore this. */ + if (tlv_peek_null (tlv)) + { + /* Read the optional Null tag. */ + if (tlv_next (tlv)) + goto bailout; + } } else digest_algo = GCRY_MD_SHA1; @@ -2067,13 +2178,17 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) log_error ("error allocating decryption buffer\n"); goto bailout; } - decrypt_block (data, plain, datalen, salt, saltlen, iter, + datalen = decrypt_block (data, plain, datalen, salt, saltlen, iter, iv, is_pbes2? 16:0, ctx->password, is_pbes2 ? (is_aes256?GCRY_CIPHER_AES256:GCRY_CIPHER_AES128) : GCRY_CIPHER_3DES, digest_algo, bag_data_p); - + if (!datalen) + { + err = gpg_error (GPG_ERR_DECRYPT_FAILED); + goto bailout; + } /* We do not need the TLV anymore and allocated a new one. */ where = "shrouded_key_bag.decrypted-text"; @@ -2093,7 +2208,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) ctx->badpass = 1; goto bailout; } - if (tlv_expect_top_sequence (tlv)) + if (tlv_expect_sequence (tlv)) { ctx->badpass = 1; goto bailout; @@ -2130,10 +2245,13 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) { if (opt_verbose > 1) log_debug ("RSA parameters\n"); - if (tlv_next (tlv)) - goto bailout; - if (tlv_expect_null (tlv)) - tlv_set_pending (tlv); /* NULL tag missing - ignore this. */ + + if (tlv_peek_null (tlv)) + { + /* Read the optional Null tag. */ + if (tlv_next (tlv)) + goto bailout; + } } else if (oidlen == DIM(oid_pcPublicKey) && !memcmp (oid, oid_pcPublicKey, oidlen)) @@ -2218,8 +2336,9 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) { int keyelem_count = 0; int firstparam = 1; + unsigned int startlevel = tlv_level (tlv); - while (!(err = tlv_next (tlv)) && !tlv_popped (tlv)) + while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel) { if (keyelem_count >= 9) { @@ -2245,7 +2364,9 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) } firstparam = 0; } - if (err && gpg_err_code (err) != GPG_ERR_EOF) + if (!err) + tlv_set_pending (tlv); + else if (err && gpg_err_code (err) != GPG_ERR_EOF) goto bailout; err = 0; } @@ -2257,22 +2378,18 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) tlv = saved_tlv; where = "shrouded_key_bag.attribute_set"; - err = tlv_next (tlv); - if (gpg_err_code (err) == GPG_ERR_EOF) - goto leave; - if (err) - goto bailout; - err = tlv_expect_set (tlv); - if (!err) - { /* This is the optional set of attributes. Skip it. */ + /* Check for an optional set of attributes. */ + if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_SET)) + { + if (tlv_next (tlv)) + goto bailout; + err = tlv_expect_set (tlv); + if (err) + goto bailout; tlv_skip (tlv); if (opt_verbose) log_info ("skipping %s\n", where); } - else if (gpg_err_code (err) == GPG_ERR_INV_OBJ) - tlv_set_pending (tlv); /* The next tlv_next will be skipped. */ - else /* Other error. */ - goto bailout; leave: @@ -2288,10 +2405,9 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) bailout: if (!err) err = gpg_error (GPG_ERR_GENERAL); - log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n", + log_error ("%s(%s): lvl=%d (%s): %s - %s\n", __func__, where, tlv? tlv->stacklen : 0, - tlv? tlv->offset : 0, tlv? tlv->lastfunc : "", tlv ? gpg_strerror (tlv->lasterr) : "init failed", gpg_strerror (err)); @@ -2367,28 +2483,27 @@ parse_cert_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) * SEQUENCE -- we actually ignore this. */ where = "certbag.attribute_set"; - if (tlv_next (tlv)) - goto bailout; - err = tlv_expect_set (tlv); - if (!err) - { /* This is the optional set of attributes. Skip it. */ + /* Check for an optional set of attributes. */ + if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_SET)) + { + if (tlv_next (tlv)) + goto bailout; + err = tlv_expect_set (tlv); + if (err) + goto bailout; tlv_skip (tlv); if (opt_verbose) - log_info ("skipping certbag.attribute_set\n"); + log_info ("skipping %s\n", where); } - else if (gpg_err_code (err) == GPG_ERR_INV_OBJ) - tlv_set_pending (tlv); /* The next tlv_next will be skipped. */ - else - goto bailout; + leave: return err; bailout: - log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n", + log_error ("%s(%s): lvl=%u (%s): %s - %s\n", __func__, where, tlv? tlv->stacklen : 0, - tlv? tlv->offset : 0, tlv? tlv->lastfunc : "", tlv ? gpg_strerror (tlv->lasterr) : "init failed", gpg_strerror (err)); @@ -2426,6 +2541,14 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) if (tlv_expect_octet_string (tlv, 1, NULL, NULL)) goto bailout; + if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_OCTET_STRING)) + { + if (tlv_next (tlv)) + goto bailout; + err = tlv_expect_octet_string (tlv, 1, NULL, NULL); + if (err) + goto bailout; + } /* Expect: * SEQUENCE @@ -2437,6 +2560,7 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) goto bailout; startlevel = tlv_level (tlv); + dump_tlv_ctx ("data.outerseqs", "beginloop", tlv); while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel) { /* Expect: @@ -2475,11 +2599,12 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) log_info ("unknown inner data type - skipped\n"); } } - if (err && gpg_err_code (err) != GPG_ERR_EOF) + dump_tlv_ctx ("data.outerseqs", "endloop", tlv); + if (!err) + tlv_set_pending (tlv); + else if (err && gpg_err_code (err) != GPG_ERR_EOF) goto bailout; err = 0; - if (tlv_popped (tlv)) - tlv_set_pending (tlv); leave: return err; @@ -2487,10 +2612,9 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) bailout: if (!err) err = gpg_error (GPG_ERR_GENERAL); - log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n", + log_error ("%s(%s): lvl=%d (%s): %s - %s\n", __func__, where, tlv? tlv->stacklen : 0, - tlv? tlv->offset : 0, tlv? tlv->lastfunc : "", tlv ? gpg_strerror (tlv->lasterr) : "init failed", gpg_strerror (err)); @@ -2567,6 +2691,14 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, if (tlv_expect_octet_string (tlv, 1, NULL, NULL)) goto bailout; + if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_OCTET_STRING)) + { + if (tlv_next (tlv)) + goto bailout; + err = tlv_expect_octet_string (tlv, 1, NULL, NULL); + if (err) + goto bailout; + } where = "bags"; if (tlv_next (tlv)) @@ -2575,9 +2707,11 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, goto bailout; startlevel = tlv_level (tlv); + dump_tlv_ctx ("bags", "beginloop", tlv); while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel) { where = "bag-sequence"; + dump_tlv_ctx (where, NULL, tlv); if (tlv_expect_sequence (tlv)) goto bailout; @@ -2614,7 +2748,10 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, log_info ("unknown outer bag type - skipped\n"); } } - if (err && gpg_err_code (err) != GPG_ERR_EOF) + dump_tlv_ctx ("bags", "endloop", tlv); + if (!err) + tlv_set_pending (tlv); + else if (err && gpg_err_code (err) != GPG_ERR_EOF) goto bailout; err = 0; @@ -2628,12 +2765,12 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, bailout: *r_badpass = ctx.badpass; - log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n", + log_error ("%s(%s): @%04zu lvl=%u %s: %s - %s\n", __func__, where, + tlv? (size_t)(tlv->buffer - tlv->origbuffer):0, tlv? tlv->stacklen : 0, - tlv? tlv->offset : 0, tlv? tlv->lastfunc : "", - tlv ? gpg_strerror (tlv->lasterr) : "init failed", + tlv? gpg_strerror (tlv->lasterr) : "init failed", gpg_strerror (err)); if (ctx.privatekey) { -- cgit v1.2.3 From 4448bc44f0baf913efdc23ac527f5b0fb4a93351 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 24 Oct 2023 13:25:10 +0200 Subject: common: Provide API to parse BER/TLV encodings. * sm/minip12.c: Factor parsing code out to ... * common/tlv-parser.c: new. Extend function names and provide a few extra functions. * common/Makefile.am (common_sources): Add new file. * sm/minip12.c: Adjust to use the new parser API. --- sm/minip12.c | 860 +++++------------------------------------------------------ 1 file changed, 67 insertions(+), 793 deletions(-) (limited to 'sm/minip12.c') diff --git a/sm/minip12.c b/sm/minip12.c index 98eb4e3b5..ae81d821b 100644 --- a/sm/minip12.c +++ b/sm/minip12.c @@ -150,57 +150,6 @@ struct buffer_s }; -struct tag_info -{ - int class; - int is_constructed; - unsigned long tag; - size_t length; /* length part of the TLV */ - size_t nhdr; - int ndef; /* It is an indefinite length */ -}; - - -#define TLV_MAX_DEPTH 20 - - -struct bufferlist_s -{ - struct bufferlist_s *next; - char *buffer; -}; - - -/* An object to control the ASN.1 parsing. */ -struct tlv_ctx_s -{ - /* The orginal buffer with the entire pkcs#12 object and its length. */ - const unsigned char *origbuffer; - size_t origbufsize; - - /* The current buffer we are working on and its length. */ - const unsigned char *buffer; - size_t bufsize; - - int in_ndef; /* Flag indicating that we are in a NDEF. */ - int pending; /* The last tlv_next has not yet been processed. */ - - struct tag_info ti; /* The current tag. */ - gpg_error_t lasterr; /* Last error from tlv function. */ - const char *lastfunc;/* Name of last called function. */ - - 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. */ - size_t bufsize; /* Saved value of BUFSIZE. */ - size_t length; /* Length of the container (ti.length). */ - int in_ndef; /* Saved IN_NDEF flag (ti.ndef). */ - } stack[TLV_MAX_DEPTH]; -}; - - /* Parser communication object. */ struct p12_parse_ctx_s { @@ -225,11 +174,6 @@ struct p12_parse_ctx_s static int opt_verbose; -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); - @@ -242,44 +186,6 @@ p12_set_verbosity (int verbose, int debug) } -#define dump_tag_info(a,b) _dump_tag_info ((a),__LINE__,(b)) -static void -_dump_tag_info (const char *text, int lno, struct tlv_ctx_s *tlv) -{ - struct tag_info *ti; - - if (opt_verbose < 2) - return; - - 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":""); -} - - -#define dump_tlv_ctx(a,b,c) _dump_tlv_ctx ((a),(b),__LINE__,(c)) -static void -_dump_tlv_ctx (const char *text, const char *text2, - int lno, struct tlv_ctx_s *tlv) -{ - if (opt_verbose < 2) - return; - - log_debug ("p12_parse:%s%s%s:%d: @%04zu lvl=%u %s\n", - text, - text2? "/":"", text2? text2:"", - lno, - (size_t)(tlv->buffer - tlv->origbuffer), - tlv->stacklen, - tlv->in_ndef? " in-ndef":""); -} - - static int digest_algo_from_oid (unsigned char const *oid, size_t oidlen) { @@ -398,638 +304,6 @@ builder_add_mpi (tlv_builder_t tb, int class, int tag, gcry_mpi_t mpi, } - -/* 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. 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) -{ - gpg_error_t err; - int tag; - - err = parse_ber_header (buffer, size, - &ti->class, &tag, - &ti->is_constructed, &ti->ndef, - &ti->length, &ti->nhdr); - if (err) - return err; - if (tag < 0) - return gpg_error (GPG_ERR_EOVERFLOW); - ti->tag = tag; - - if (ti->length > *size) - return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); /* data larger than buffer. */ - - return 0; -} - - -/* Create a new TLV object. */ -static struct tlv_ctx_s * -tlv_new (const unsigned char *buffer, size_t bufsize) -{ - struct tlv_ctx_s *tlv; - tlv = xtrycalloc (1, sizeof *tlv); - if (tlv) - { - tlv->origbuffer = buffer; - tlv->origbufsize = bufsize; - tlv->buffer = buffer; - tlv->bufsize = bufsize; - } - 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 -tlv_register_buffer (struct tlv_ctx_s *tlv, char *buffer) -{ - 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; -} - - -static void -tlv_release (struct tlv_ctx_s *tlv) -{ - if (!tlv) - return; - while (tlv->bufferlist) - { - struct bufferlist_s *save = tlv->bufferlist->next; - xfree (tlv->bufferlist->buffer); - xfree (tlv->bufferlist); - tlv->bufferlist = save; - } - xfree (tlv); -} - - -/* Helper for the tlv_peek functions. */ -static gpg_error_t -_tlv_peek (struct tlv_ctx_s *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. */ -static int -tlv_peek (struct tlv_ctx_s *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. */ -static int -tlv_peek_null (struct tlv_ctx_s *tlv) -{ - struct tag_info ti; - - return (!_tlv_peek (tlv, &ti) - && ti.class == CLASS_UNIVERSAL && ti.tag == TAG_NULL - && !ti.is_constructed && !ti.length); -} - - -/* Helper for tlv_expect_sequence and tlv_expect_context_tag. */ -static gpg_error_t -_tlv_push (struct tlv_ctx_s *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) - return (tlv->lasterr = gpg_error (GPG_ERR_TOO_MANY)); - tlv->stack[tlv->stacklen].buffer = tlv->buffer; - 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->stacklen++; - - tlv->in_ndef = tlv->ti.ndef; - - /* We set the size of the buffer to the TLV length if it is known or - * else to the size of the remaining entire buffer. */ - if (tlv->in_ndef) - { - if ((tlv->buffer - tlv->origbuffer) > tlv->origbufsize) - return (tlv->lasterr = gpg_error (GPG_ERR_BUG)); - tlv->bufsize = tlv->origbufsize - (tlv->buffer - tlv->origbuffer); - } - else - tlv->bufsize = tlv->ti.length; - - dump_tlv_ctx (__func__, NULL, tlv); - return 0; -} - - -/* Helper for tlv_next. */ -static gpg_error_t -_tlv_pop (struct tlv_ctx_s *tlv) -{ - size_t lastlen; - - /* 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 - * are positioned at the value of the last container. */ - if (!tlv->stacklen) - return gpg_error (GPG_ERR_EOF); - - tlv->stacklen--; - tlv->in_ndef = tlv->stack[tlv->stacklen].in_ndef; - if (tlv->in_ndef) - { - /* We keep buffer but adjust bufsize to the end of the origbuffer. */ - if ((tlv->buffer - tlv->origbuffer) > tlv->origbufsize) - return (tlv->lasterr = gpg_error (GPG_ERR_BUG)); - tlv->bufsize = tlv->origbufsize - (tlv->buffer - tlv->origbuffer); - } - 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) - { - log_debug ("%s: container length larger than buffer (%zu/%zu)\n", - __func__, lastlen, tlv->bufsize); - return gpg_error (GPG_ERR_INV_BER); - } - tlv->buffer += lastlen; - tlv->bufsize -= lastlen; - } - - dump_tlv_ctx (__func__, NULL, tlv); - return 0; -} - - -/* Parse the next tag and value. Also detect the end of a - * container. */ -#define tlv_next(a) _tlv_next ((a), __LINE__) -static gpg_error_t -_tlv_next (struct tlv_ctx_s *tlv, int lno) -{ - gpg_error_t err; - - tlv->lasterr = 0; - tlv->lastfunc = __func__; - - if (tlv->pending) - { - tlv->pending = 0; - if (opt_verbose > 1) - log_debug ("%s: tlv_next skipped\n", __func__); - return 0; - } - - if (opt_verbose > 1) - log_debug ("%s: tlv_next called\n", __func__); - /* If we are at the end of an ndef container pop the stack. */ - if (!tlv->in_ndef && !tlv->bufsize) - { - do - err = _tlv_pop (tlv); - while (!err && !tlv->in_ndef && !tlv->bufsize); - if (err) - return (tlv->lasterr = err); - if (opt_verbose > 1) - log_debug ("%s: container(s) closed due to size\n", __func__); - } - - again: - /* Get the next tag. */ - err = parse_tag (&tlv->buffer, &tlv->bufsize, &tlv->ti); - if (err) - { - if (opt_verbose > 1) - log_debug ("%s: reading tag returned err=%d\n", __func__, err); - return err; - } - - /* 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 - && !tlv->ti.tag && !tlv->ti.is_constructed)) - { - do - err = _tlv_pop (tlv); - while (!err && !tlv->in_ndef && !tlv->bufsize); - if (err) - return (tlv->lasterr = err); - if (opt_verbose > 1) - log_debug ("%s: container(s) closed due to end tag\n", __func__); - goto again; - } - - _dump_tag_info (__func__, lno, tlv); - return 0; -} - - -/* Return the current neting level of the TLV object. */ -static unsigned int -tlv_level (struct tlv_ctx_s *tlv) -{ - return tlv->stacklen; -} - - -/* Set a flag to indicate that the last tlv_next has not yet been - * consumed. */ -static void -tlv_set_pending (struct tlv_ctx_s *tlv) -{ - tlv->pending = 1; -} - - -/* Skip over the value of the current tag. Does not yet work for ndef - * containers. */ -static void -tlv_skip (struct tlv_ctx_s *tlv) -{ - tlv->lastfunc = __func__; - log_assert (tlv->bufsize >= tlv->ti.length); - tlv->buffer += tlv->ti.length; - tlv->bufsize -= tlv->ti.length; -} - - -/* Expect that the current tag is a sequence and setup the context for - * processing. */ -static gpg_error_t -tlv_expect_sequence (struct tlv_ctx_s *tlv) -{ - tlv->lastfunc = __func__; - if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_SEQUENCE - && tlv->ti.is_constructed)) - return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - return _tlv_push (tlv); -} - - -/* Expect that the current tag is a context tag and setup the context - * for processing. The tag of the context is returned at R_TAG. */ -static gpg_error_t -tlv_expect_context_tag (struct tlv_ctx_s *tlv, int *r_tag) -{ - tlv->lastfunc = __func__; - if (!(tlv->ti.class == CLASS_CONTEXT && tlv->ti.is_constructed)) - return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - *r_tag = tlv->ti.tag; - return _tlv_push (tlv); -} - - -/* Expect that the current tag is a SET and setup the context for - * processing. */ -static gpg_error_t -tlv_expect_set (struct tlv_ctx_s *tlv) -{ - tlv->lastfunc = __func__; - if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_SET - && tlv->ti.is_constructed)) - return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - return _tlv_push (tlv); -} - - -/* Expect an object of CLASS with TAG and store its value at - * (R_DATA,R_DATALEN). Then skip over its value to the next tag. - * Note that the stored value is not allocated but points into - * TLV. */ -static gpg_error_t -tlv_expect_object (struct tlv_ctx_s *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__; - if (!(tlv->ti.class == class && tlv->ti.tag == tag)) - return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - p = tlv->buffer; - n = tlv->ti.length; - if (!n && tlv->ti.ndef) - { - n = tlv->bufsize; - needpush = 1; - } - 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; - - newbuffer = cram_octet_string (p, n, r_datalen); - if (!newbuffer) - return (tlv->lasterr = gpg_error (GPG_ERR_BAD_BER)); - err = tlv_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)) - return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); - tlv->buffer += tlv->ti.length; - tlv->bufsize -= tlv->ti.length; - 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. */ -static gpg_error_t -tlv_expect_octet_string (struct tlv_ctx_s *tlv, int encapsulates, - 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))) - return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - p = tlv->buffer; - 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 = tlv_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->bufsize >= tlv->ti.length)) - return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); - tlv->buffer += tlv->ti.length; - tlv->bufsize -= tlv->ti.length; - return 0; -} - - -/* Expect that the current tag is an integer and return its value at - * R_VALUE. Then skip over its value to the next tag. */ -static gpg_error_t -tlv_expect_integer (struct tlv_ctx_s *tlv, int *r_value) -{ - const unsigned char *p; - size_t n; - int value; - - tlv->lastfunc = __func__; - if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_INTEGER - && !tlv->ti.is_constructed)) - return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - p = tlv->buffer; - if (!(n=tlv->ti.length)) - return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); - - /* We currently support only positive values. */ - if ((*p & 0x80)) - return (tlv->lasterr = gpg_error (GPG_ERR_ERANGE)); - - for (value = 0; n; n--) - { - value <<= 8; - value |= (*p++) & 0xff; - if (value < 0) - return (tlv->lasterr = gpg_error (GPG_ERR_EOVERFLOW)); - } - *r_value = value; - 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; - return 0; -} - - -/* Variant of tlv_expect_integer which returns an MPI. If IGNORE_ZERO - * is set a value of 0 is ignored and R_VALUE not changed and the - * function returns GPG_ERR_FALSE. No check for negative encoded - * integers is doe because the old code here worked the same and we - * can't foreclose invalid encoded PKCS#12 stuff - after all it is - * PKCS#12 see https://www.cs.auckland.ac.nz/~pgut001/pubs/pfx.html */ -static gpg_error_t -tlv_expect_mpinteger (struct tlv_ctx_s *tlv, int ignore_zero, - gcry_mpi_t *r_value) -{ - const unsigned char *p; - size_t n; - - tlv->lastfunc = __func__; - if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_INTEGER - && !tlv->ti.is_constructed)) - return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - p = tlv->buffer; - if (!(n=tlv->ti.length)) - return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); - - 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; - if (ignore_zero && n == 1 && !*p) - return gpg_error (GPG_ERR_FALSE); - - return gcry_mpi_scan (r_value, GCRYMPI_FMT_USG, p, n, NULL); -} - - -/* Expect that the current tag is an object id and store its value at - * (R_OID,R_OIDLEN). Then skip over its value to the next tag. Note - * that the stored value is not allocated but points into TLV. */ -static gpg_error_t -tlv_expect_object_id (struct tlv_ctx_s *tlv, - unsigned char const **r_oid, size_t *r_oidlen) -{ - const unsigned char *p; - size_t n; - - tlv->lastfunc = __func__; - if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OBJECT_ID - && !tlv->ti.is_constructed)) - return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); - p = tlv->buffer; - if (!(n=tlv->ti.length)) - return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); - - *r_oid = p; - *r_oidlen = 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; - return 0; -} - - - -/* 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. - * - * The example is from Mozilla Firefox 1.0.4 which actually exports - * certs as single byte chunks of octet strings. - * - * 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) -{ - 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 (length); - if (!output) - goto bailout; - - while (n) - { - if (parse_tag (&s, &n, &ti)) - goto bailout; - 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; - n -= ti.length; - } - else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) - break; /* Ready */ - else - goto bailout; - } - - - *r_newlength = d - output; - return output; - - bailout: - gcry_free (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 (parse_tag (&s, &n, &ti)) - return 0; - if (ti.class == CLASS_UNIVERSAL && ti.tag == TAG_OCTET_STRING - && !ti.ndef && !ti.is_constructed) - { - s += ti.length; - n -= ti.length; - } - else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) - break; /* Ready */ - else - return 0; - } - - return 1; -} - - static int string_to_key (int id, char *salt, size_t saltlen, int iter, const char *pw, int req_keylen, unsigned char *keybuf) @@ -1379,11 +653,11 @@ bag_decrypted_data_p (const void *plaintext, size_t length) } #endif /*ENABLE_DER_STRUCT_DUMPING*/ - if (parse_tag (&p, &n, &ti)) + if (tlv_parse_tag (&p, &n, &ti)) return 0; if (ti.class || ti.tag != TAG_SEQUENCE) return 0; - if (parse_tag (&p, &n, &ti)) + if (tlv_parse_tag (&p, &n, &ti)) return 0; return 1; @@ -1391,7 +665,7 @@ bag_decrypted_data_p (const void *plaintext, size_t length) static int -parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) +parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) { gpg_error_t err = 0; const char *where; @@ -1510,13 +784,13 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) goto bailout; if (tlv_expect_sequence (tlv)) goto bailout; - parmlen = tlv->ti.length; + parmlen = tlv_parser_tag_length (tlv, 0); if (tlv_next (tlv)) goto bailout; if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; - parmlen -= tlv->ti.length + tlv->ti.nhdr; + parmlen -= tlv_parser_tag_length (tlv, 1); if (datalen < 8 || datalen > sizeof salt) { log_info ("bad length of salt (%zu)\n", datalen); @@ -1530,7 +804,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) goto bailout; if ((err = tlv_expect_integer (tlv, &intval))) goto bailout; - parmlen -= tlv->ti.length + tlv->ti.nhdr; + parmlen -= tlv_parser_tag_length (tlv, 1); if (!intval) /* Not a valid iteration count. */ { err = gpg_error (GPG_ERR_INV_VALUE); @@ -1667,7 +941,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) /* We do not need the TLV anymore and allocated a new one. */ where = "bag.encryptedData.decrypted-text"; - tlv = tlv_new (plain, datalen); + tlv = tlv_parser_new (plain, datalen, opt_verbose); if (!tlv) { err = gpg_error_from_syserror (); @@ -1688,8 +962,8 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) /* Loop over all certificates inside the bag. */ loopcount = 0; - startlevel = tlv_level (tlv); - while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel) + startlevel = tlv_parser_level (tlv); + while (!(err = tlv_next (tlv)) && tlv_parser_level (tlv) == startlevel) { int iscrlbag = 0; int iskeybag = 0; @@ -1802,8 +1076,8 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) where = "reading.keybag.key-parameters"; keyelem_count = 0; - startlevel2 = tlv_level (tlv); - while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel2) + startlevel2 = tlv_parser_level (tlv); + while (!(err = tlv_next (tlv)) && tlv_parser_level (tlv) == startlevel2) { if (keyelem_count >= 9) { @@ -1826,7 +1100,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) keyelem_count++; } if (!err) - tlv_set_pending (tlv); + tlv_parser_set_pending (tlv); else if (err && gpg_err_code (err) != GPG_ERR_EOF) goto bailout; err = 0; @@ -1883,13 +1157,13 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) err = tlv_expect_set (tlv); if (err) goto bailout; - tlv_skip (tlv); + tlv_parser_skip (tlv); if (opt_verbose) log_info ("skipping %s\n", where); } } if (!err) - tlv_set_pending (tlv); + tlv_parser_set_pending (tlv); else if (err && gpg_err_code (err) != GPG_ERR_EOF) { if (!loopcount) /* The first while(tlv_next) failed. */ @@ -1900,7 +1174,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) leave: if (renewed_tlv) - tlv_release (tlv); + tlv_parser_release (tlv); gcry_free (plain); if (ctx->badpass) { @@ -1916,9 +1190,9 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) err = gpg_error (GPG_ERR_GENERAL); log_error ("%s(%s): lvl=%u (%s): %s - %s\n", __func__, where, - tlv? tlv->stacklen : 0, - tlv? tlv->lastfunc : "", - tlv ? gpg_strerror (tlv->lasterr) : "init failed", + tlv_parser_level (tlv), + tlv_parser_lastfunc (tlv), + tlv_parser_lasterrstr (tlv), gpg_strerror (err)); goto leave; } @@ -1943,9 +1217,9 @@ bag_data_p (const void *plaintext, size_t length) } #endif /*ENABLE_DER_STRUCT_DUMPING*/ - if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) return 0; - if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER + if (tlv_parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER || ti.length != 1 || *p) return 0; @@ -1954,7 +1228,7 @@ bag_data_p (const void *plaintext, size_t length) static gpg_error_t -parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) +parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) { gpg_error_t err = 0; const char *where; @@ -1967,7 +1241,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) size_t saltlen; char iv[16]; unsigned int iter; - struct tlv_ctx_s *saved_tlv = NULL; + tlv_parser_t saved_tlv = NULL; int renewed_tlv = 0; /* True if the TLV must be released. */ unsigned char *plain = NULL; int is_pbes2 = 0; @@ -2039,13 +1313,13 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) goto bailout; if (tlv_expect_sequence (tlv)) goto bailout; - parmlen = tlv->ti.length; + parmlen = tlv_parser_tag_length (tlv, 0); if (tlv_next (tlv)) goto bailout; if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; - parmlen -= tlv->ti.length + tlv->ti.nhdr; + parmlen -= tlv_parser_tag_length (tlv, 1); if (datalen < 8 || datalen > sizeof salt) { log_info ("bad length of salt (%zu) for AES\n", datalen); @@ -2059,7 +1333,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) goto bailout; if ((err = tlv_expect_integer (tlv, &intval))) goto bailout; - parmlen -= tlv->ti.length + tlv->ti.nhdr; + parmlen -= tlv_parser_tag_length (tlv, 1); if (!intval) /* Not a valid iteration count. */ { err = gpg_error (GPG_ERR_INV_VALUE); @@ -2193,7 +1467,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) /* We do not need the TLV anymore and allocated a new one. */ where = "shrouded_key_bag.decrypted-text"; saved_tlv = tlv; - tlv = tlv_new (plain, datalen); + tlv = tlv_parser_new (plain, datalen, opt_verbose); if (!tlv) { err = gpg_error_from_syserror (); @@ -2336,9 +1610,9 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) { int keyelem_count = 0; int firstparam = 1; - unsigned int startlevel = tlv_level (tlv); + unsigned int startlevel = tlv_parser_level (tlv); - while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel) + while (!(err = tlv_next (tlv)) && tlv_parser_level (tlv) == startlevel) { if (keyelem_count >= 9) { @@ -2365,7 +1639,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) firstparam = 0; } if (!err) - tlv_set_pending (tlv); + tlv_parser_set_pending (tlv); else if (err && gpg_err_code (err) != GPG_ERR_EOF) goto bailout; err = 0; @@ -2373,7 +1647,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) if (opt_verbose > 1) log_debug ("restoring parser context\n"); - tlv_release (tlv); + tlv_parser_release (tlv); renewed_tlv = 0; tlv = saved_tlv; @@ -2386,7 +1660,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) err = tlv_expect_set (tlv); if (err) goto bailout; - tlv_skip (tlv); + tlv_parser_skip (tlv); if (opt_verbose) log_info ("skipping %s\n", where); } @@ -2396,7 +1670,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) gcry_free (plain); if (renewed_tlv) { - tlv_release (tlv); + tlv_parser_release (tlv); if (opt_verbose > 1) log_debug ("parser context released\n"); } @@ -2405,18 +1679,18 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) bailout: if (!err) err = gpg_error (GPG_ERR_GENERAL); - log_error ("%s(%s): lvl=%d (%s): %s - %s\n", + log_error ("%s(%s): lvl=%u (%s): %s - %s\n", __func__, where, - tlv? tlv->stacklen : 0, - tlv? tlv->lastfunc : "", - tlv ? gpg_strerror (tlv->lasterr) : "init failed", + tlv_parser_level (tlv), + tlv_parser_lastfunc (tlv), + tlv_parser_lasterrstr (tlv), gpg_strerror (err)); goto leave; } static gpg_error_t -parse_cert_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) +parse_cert_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) { gpg_error_t err = 0; const char *where; @@ -2491,7 +1765,7 @@ parse_cert_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) err = tlv_expect_set (tlv); if (err) goto bailout; - tlv_skip (tlv); + tlv_parser_skip (tlv); if (opt_verbose) log_info ("skipping %s\n", where); } @@ -2503,9 +1777,9 @@ parse_cert_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) bailout: log_error ("%s(%s): lvl=%u (%s): %s - %s\n", __func__, where, - tlv? tlv->stacklen : 0, - tlv? tlv->lastfunc : "", - tlv ? gpg_strerror (tlv->lasterr) : "init failed", + tlv_parser_level (tlv), + tlv_parser_lastfunc (tlv), + tlv_parser_lasterrstr (tlv), gpg_strerror (err)); if (!err) err = gpg_error (GPG_ERR_GENERAL); @@ -2514,7 +1788,7 @@ parse_cert_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) static gpg_error_t -parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) +parse_bag_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv) { gpg_error_t err = 0; const char *where; @@ -2559,9 +1833,9 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) if (tlv_expect_sequence (tlv)) goto bailout; - startlevel = tlv_level (tlv); - dump_tlv_ctx ("data.outerseqs", "beginloop", tlv); - while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel) + startlevel = tlv_parser_level (tlv); + tlv_parser_dump_state ("data.outerseqs", "beginloop", tlv); + while (!(err = tlv_next (tlv)) && tlv_parser_level (tlv) == startlevel) { /* Expect: * SEQUENCE @@ -2595,13 +1869,13 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) } else { - tlv_skip (tlv); + tlv_parser_skip (tlv); log_info ("unknown inner data type - skipped\n"); } } - dump_tlv_ctx ("data.outerseqs", "endloop", tlv); + tlv_parser_dump_state ("data.outerseqs", "endloop", tlv); if (!err) - tlv_set_pending (tlv); + tlv_parser_set_pending (tlv); else if (err && gpg_err_code (err) != GPG_ERR_EOF) goto bailout; err = 0; @@ -2612,11 +1886,11 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) bailout: if (!err) err = gpg_error (GPG_ERR_GENERAL); - log_error ("%s(%s): lvl=%d (%s): %s - %s\n", + log_error ("%s(%s): lvl=%u (%s): %s - %s\n", __func__, where, - tlv? tlv->stacklen : 0, - tlv? tlv->lastfunc : "", - tlv ? gpg_strerror (tlv->lasterr) : "init failed", + tlv_parser_level (tlv), + tlv_parser_lastfunc (tlv), + tlv_parser_lasterrstr (tlv), gpg_strerror (err)); goto leave; } @@ -2636,7 +1910,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, { gpg_error_t err; const char *where = ""; - struct tlv_ctx_s *tlv; + tlv_parser_t tlv; struct p12_parse_ctx_s ctx = { NULL }; const unsigned char *oid; size_t oidlen; @@ -2649,7 +1923,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, ctx.certcbarg = certcbarg; ctx.password = pw; - tlv = tlv_new (buffer, length); + tlv = tlv_parser_new (buffer, length, opt_verbose); if (!tlv) { err = gpg_error_from_syserror (); @@ -2706,12 +1980,12 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, if (tlv_expect_sequence (tlv)) goto bailout; - startlevel = tlv_level (tlv); - dump_tlv_ctx ("bags", "beginloop", tlv); - while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel) + startlevel = tlv_parser_level (tlv); + tlv_parser_dump_state ("bags", "beginloop", tlv); + while (!(err = tlv_next (tlv)) && tlv_parser_level (tlv) == startlevel) { where = "bag-sequence"; - dump_tlv_ctx (where, NULL, tlv); + tlv_parser_dump_state (where, NULL, tlv); if (tlv_expect_sequence (tlv)) goto bailout; @@ -2744,18 +2018,18 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, } else { - tlv_skip (tlv); + tlv_parser_skip (tlv); log_info ("unknown outer bag type - skipped\n"); } } - dump_tlv_ctx ("bags", "endloop", tlv); + tlv_parser_dump_state ("bags", "endloop", tlv); if (!err) - tlv_set_pending (tlv); + tlv_parser_set_pending (tlv); else if (err && gpg_err_code (err) != GPG_ERR_EOF) goto bailout; err = 0; - tlv_release (tlv); + tlv_parser_release (tlv); if (r_curve) *r_curve = ctx.curve; else @@ -2767,10 +2041,10 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, *r_badpass = ctx.badpass; log_error ("%s(%s): @%04zu lvl=%u %s: %s - %s\n", __func__, where, - tlv? (size_t)(tlv->buffer - tlv->origbuffer):0, - tlv? tlv->stacklen : 0, - tlv? tlv->lastfunc : "", - tlv? gpg_strerror (tlv->lasterr) : "init failed", + tlv_parser_offset (tlv), + tlv_parser_level (tlv), + tlv_parser_lastfunc (tlv), + tlv_parser_lasterrstr (tlv), gpg_strerror (err)); if (ctx.privatekey) { @@ -2781,7 +2055,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, gcry_free (ctx.privatekey); ctx.privatekey = NULL; } - tlv_release (tlv); + tlv_parser_release (tlv); gcry_free (ctx.curve); if (r_curve) *r_curve = NULL; -- cgit v1.2.3