diff options
author | Werner Koch <wk@gnupg.org> | 2024-08-06 17:51:01 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2024-08-06 17:51:01 +0200 |
commit | 690fd61a0cf2b4b51ee64811656692eb644d2918 (patch) | |
tree | 58df8ab6e4cd9906444bff77efda6be059d1561a /sm | |
parent | sm: Add a debug helper command to t-minip12.c (diff) | |
download | gnupg2-690fd61a0cf2b4b51ee64811656692eb644d2918.tar.xz gnupg2-690fd61a0cf2b4b51ee64811656692eb644d2918.zip |
sm: More improvements for PKCS#12 parsing for latest IVBB changes.
* common/tlv.h (TLV_PARSER_FLAG_T5793): New.
(tlv_parser_new): New macro. Rename function with an underscore.
(tlv_next_with_flag): New.
* common/tlv-parser.c (struct tlv_parser_s): Remove const from buffer.
Add fields crammed, lasttlv, and origoff. Remove bufferlist ands ist
definition.
(dump_to_file): New but disabled debug helper.
(parse_tag): Print more info on error.
(_tlv_parser_new): Add args lasttlv and LNO. Take a copy of the data.
(_tlv_parser_release): Free the copy of the buffer and return the
recorded TLV object from tlv_parser_new.
(_tlv_peek, tlv_parser_peek, _tlv_parser_peek_null): Remove.
(_tlv_push): Record crammed length.
(_tlv_pop): Restore crammed length.
(_tlv_parser_next): Add arg flags. More debug output. Handle cramming
here. Take care of cramming here.
(tlv_expect_object): Simplify to adjust for changes in _tlv_parser_next.
(tlv_expect_octet_string): Remove arg encapsulates. Adjust for
changes in _tlv_parser_next. Change all allers.
(tlv_expect_null): New.
(cram_octet_string): Rewrite.
(need_octet_string_cramming): Remove.
* sm/minip12.c (dump_to_file): New. Enablein debug mode and if a
envvar ist set. Replace all explict but disabled dumping to call this
function.
(parse_bag_encrypted_data): Replace tlv_peek_null and a peeking for an
optional SET by non-peeking code.
(parse_cert_bag): Ditto.
(parse_shrouded_key_bag): Replace tlv_peek_null by non-peeking code.
(parse_bag_encrypted_data): Use the new TLV_PARSER_FLAG_T5793 to
enable the Mozilla workaround.
(parse_bag_encrypted_data): Replace the 'renewed_tlv' code by the new
tlv_parser_release semantics.
(parse_shrouded_key_bag): Ditto.
(parse_shrouded_key_bag): Create a new context instead of using the
former encapsulated mechanism for tlv_expect_octet_string.
(parse_bag_data): Ditto.
(p12_parse): Ditto.
--
GnuPG-bug-id: 7213
Fixing this took way too long; I should have earlier explained the
code to a co-hacker to find the problem myself in my code by this.
Diffstat (limited to 'sm')
-rw-r--r-- | sm/minip12.c | 269 |
1 files changed, 165 insertions, 104 deletions
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, |