From 412b8d91e065149402575c05bd84021a149ad09b Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 7 May 2026 18:40:28 +0200 Subject: [PATCH 01/10] Update framework with test certificate with basicConstraints overflow Signed-off-by: Gilles Peskine --- framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework b/framework index 80a0ea93f0..e0f6275eff 160000 --- a/framework +++ b/framework @@ -1 +1 @@ -Subproject commit 80a0ea93f0215bcd9030734904b4b54fb8306f07 +Subproject commit e0f6275eff2e45e295fdd6b6877de4cbdaddae83 From 07f45b87681c1a0680c260089d3e6349b25fdd08 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 6 May 2026 22:29:22 +0200 Subject: [PATCH 02/10] Fix lax basicConstraints parsing When parsing the basicConstraints extension, reject junk after the SEQUENCE inside the extension (which is probably benign), and reject a SEQUENCE that extends beyond the extension (could be very dangerous). Add a non-regression test where a certificate that is technically malformed, but accepted as a leaf certificate by OpenSSL and other X.509 implementations, to be accepted as a CA certificate by Mbed TLS. Signed-off-by: Gilles Peskine --- ChangeLog.d/x509-crt-parse-basicConstraints.txt | 7 +++++++ library/x509_crt.c | 7 +++++++ tests/suites/test_suite_x509parse.data | 8 ++++++++ 3 files changed, 22 insertions(+) create mode 100644 ChangeLog.d/x509-crt-parse-basicConstraints.txt diff --git a/ChangeLog.d/x509-crt-parse-basicConstraints.txt b/ChangeLog.d/x509-crt-parse-basicConstraints.txt new file mode 100644 index 0000000000..c5b25f114b --- /dev/null +++ b/ChangeLog.d/x509-crt-parse-basicConstraints.txt @@ -0,0 +1,7 @@ +Security + * Fix a bug in the X.509 certificate parser that caused some inputs + with a malformed basicConstraints extension to be accepted. This + could have dangerous results, in particular causing some certificates + to be parsed as CA certificates when other X.509 implementations would + parse them as end-entity certificates. Reported by Mohammad Seet + (mhdsait101). CVE-2026-TODO diff --git a/library/x509_crt.c b/library/x509_crt.c index d3aa954894..c2fb22726b 100644 --- a/library/x509_crt.c +++ b/library/x509_crt.c @@ -516,6 +516,13 @@ static int x509_get_basic_constraints(unsigned char **p, return 0; } + if ((size_t) (end - *p) != len) { + /* Reject junk after the SEQUENCE inside the extension (which is + * probably benign), and reject a SEQUENCE that extends beyond + * the extension (could be very dangerous). */ + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + } + if ((ret = mbedtls_asn1_get_bool(p, end, ca_istrue)) != 0) { if (ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { ret = mbedtls_asn1_get_int(p, end, ca_istrue); diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data index 2b65f66653..b5470a5067 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data @@ -2060,6 +2060,14 @@ X509 CRT ASN1 (TBSCertificate v3, inv CertificatePolicies, policy qualifier leng depends_on:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA256 x509parse_crt:"3081b030819aa0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092a864886f70d010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a314301230100603551d200409300730050601003001300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA +X509 CRT ASN1: basicConstraints overflow would make CA=1 (bad signature) +depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_MD_CAN_SHA256 +mbedtls_x509_crt_parse_file:"../framework/data_files/parse_input/server5.basic-constraints-sequence-overflow.badsign.crt":MBEDTLS_ERR_X509_INVALID_EXTENSIONS:0 + +X509 CRT ASN1: basicConstraints overflow would make CA=1 (good self signature) +depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_MD_CAN_SHA256 +mbedtls_x509_crt_parse_file:"../framework/data_files/parse_input/server5.basic-constraints-sequence-overflow.selfsigned.crt":MBEDTLS_ERR_X509_INVALID_EXTENSIONS:0 + X509 CRT ASN1 (TBS, inv extBasicConstraint, no pathlen length) depends_on:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA256 x509parse_crt:"3081b030819aa0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a314301230100603551d130101010406300402010102300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA From 102d53ea9e611adeaaf57d816622c702f7d0cd01 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 7 May 2026 20:29:11 +0200 Subject: [PATCH 03/10] Tighten CSR extensionRequest bounds Don't allow parts of a compound (SEQUENCE or SET) to go beyond the containing compound. The inline CSR regression test is derived from the existing "X509 CSR ASN.1 (OK)" DER test vector. In the extensionRequest attribute, the original well-formed fragment is: a029 3027 06092a864886f70d01090e 311a 3018 ... where 31 1a is the SET containing the Extensions sequence and 30 18 is the contained Extensions sequence. The malformed test changes only the SET length byte, from 1a to 19: a029 3027 06092a864886f70d01090e 3119 3018 ... The attribute SEQUENCE length and the inner Extensions sequence length are left unchanged. This makes the SET one byte too short for its containing attribute, so the parser must reject it with MBEDTLS_ERR_ASN1_LENGTH_MISMATCH instead of parsing the trailing byte as data outside the SET. There is no known security impact, just some risk reduction. Signed-off-by: Gilles Peskine --- .../x509-csr-extension-request-bounds.txt | 3 +++ library/x509_csr.c | 18 +++++++++++++++--- tests/suites/test_suite_x509parse.data | 12 ++++++++---- 3 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 ChangeLog.d/x509-csr-extension-request-bounds.txt diff --git a/ChangeLog.d/x509-csr-extension-request-bounds.txt b/ChangeLog.d/x509-csr-extension-request-bounds.txt new file mode 100644 index 0000000000..dfc706703c --- /dev/null +++ b/ChangeLog.d/x509-csr-extension-request-bounds.txt @@ -0,0 +1,3 @@ +Bugfix + * Reject malformed X.509 CSRs where the extensionRequest attribute + wrappers do not exactly contain the requested extensions. diff --git a/library/x509_csr.c b/library/x509_csr.c index 174ca6ffc3..4fad0738b4 100644 --- a/library/x509_csr.c +++ b/library/x509_csr.c @@ -227,18 +227,30 @@ static int x509_csr_parse_attributes(mbedtls_x509_csr *csr, /* Check that this is an extension-request attribute */ if (MBEDTLS_OID_CMP(MBEDTLS_OID_PKCS9_CSR_EXT_REQ, &attr_oid) == 0) { - if ((ret = mbedtls_asn1_get_tag(p, end, &len, + if ((ret = mbedtls_asn1_get_tag(p, end_attr_data, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET)) != 0) { return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); } - if ((ret = mbedtls_asn1_get_tag(p, end, &len, + const unsigned char *end_set = *p + len; + if (end_set != end_attr_data) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + if ((ret = mbedtls_asn1_get_tag(p, end_set, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); } - if ((ret = x509_csr_parse_extensions(csr, p, *p + len, cb, p_ctx)) != 0) { + const unsigned char *end_exts = *p + len; + if (end_exts != end_set) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + if ((ret = x509_csr_parse_extensions(csr, p, end_exts, cb, p_ctx)) != 0) { return ret; } diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data index b5470a5067..314c175ca9 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data @@ -2951,6 +2951,10 @@ X509 CSR ASN.1 (OK) depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_MD_CAN_SHA1:!MBEDTLS_X509_REMOVE_INFO mbedtls_x509_csr_parse:"308201183081bf0201003034310b3009060355040613024e4c3111300f060355040a1308506f6c617253534c31123010060355040313096c6f63616c686f73743059301306072a8648ce3d020106082a8648ce3d0301070342000437cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f768225962924ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edffa029302706092a864886f70d01090e311a301830090603551d1304023000300b0603551d0f0404030205e0300906072a8648ce3d04010349003046022100b49fd8c8f77abfa871908dfbe684a08a793d0f490a43d86fcf2086e4f24bb0c2022100f829d5ccd3742369299e6294394717c4b723a0f68b44e831b6e6c3bcabf97243":"CSR version \: 1\nsubject name \: C=NL, O=PolarSSL, CN=localhost\nsigned using \: ECDSA with SHA1\nEC key size \: 256 bits\n\nkey usage \: Digital Signature, Non Repudiation, Key Encipherment\n":0 +X509 CSR ASN.1 (extensionRequest SET length too short) +depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_ECP_HAVE_SECP256R1 +mbedtls_x509_csr_parse:"308201183081bf0201003034310b3009060355040613024e4c3111300f060355040a1308506f6c617253534c31123010060355040313096c6f63616c686f73743059301306072a8648ce3d020106082a8648ce3d0301070342000437cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f768225962924ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edffa029302706092a864886f70d01090e3119301830090603551d1304023000300b0603551d0f0404030205e0300906072a8648ce3d04010349003046022100b49fd8c8f77abfa871908dfbe684a08a793d0f490a43d86fcf2086e4f24bb0c2022100f829d5ccd3742369299e6294394717c4b723a0f68b44e831b6e6c3bcabf97243":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS+MBEDTLS_ERR_ASN1_LENGTH_MISMATCH + X509 CSR ASN.1 (Unsupported critical extension, critical=true) depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_MD_CAN_SHA256:!MBEDTLS_X509_REMOVE_INFO mbedtls_x509_csr_parse:"308201233081cb02010030413119301706035504030c1053656c66207369676e65642074657374310b300906035504061302444531173015060355040a0c0e41757468437274444220546573743059301306072a8648ce3d020106082a8648ce3d03010703420004c11ebb9951848a436ca2c8a73382f24bbb6c28a92e401d4889b0c361f377b92a8b0497ff2f5a5f6057ae85f704ab1850bef075914f68ed3aeb15a1ff1ebc0dc6a028302606092a864886f70d01090e311930173015060b2b0601040183890c8622020101ff0403010101300a06082a8648ce3d040302034700304402200c4108fd098525993d3fd5b113f0a1ead8750852baf55a2f8e670a22cabc0ba1022034db93a0fcb993912adcf2ea8cb4b66389af30e264d43c0daea03255e45d2ccc":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS+MBEDTLS_ERR_ASN1_UNEXPECTED_TAG @@ -3086,17 +3090,17 @@ X509 CSR ASN.1 (attributes: invalid len (len > data)) depends_on:MBEDTLS_MD_CAN_SHA256:MBEDTLS_RSA_C mbedtls_x509_csr_parse_file:"../framework/data_files/parse_input/test_csr_v3_all_malformed_attributes_len1.csr.der":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS+MBEDTLS_ERR_ASN1_OUT_OF_DATA -X509 CSR ASN.1 (attributes: invalid len (len < data)) +X509 CSR ASN.1 (attributes: extensionRequest SET extends past attribute) depends_on:MBEDTLS_MD_CAN_SHA256:MBEDTLS_RSA_C -mbedtls_x509_csr_parse_file:"../framework/data_files/parse_input/test_csr_v3_all_malformed_attributes_len2.csr.der":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS+MBEDTLS_ERR_ASN1_LENGTH_MISMATCH +mbedtls_x509_csr_parse_file:"../framework/data_files/parse_input/test_csr_v3_all_malformed_attributes_len2.csr.der":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS+MBEDTLS_ERR_ASN1_OUT_OF_DATA X509 CSR ASN.1 (attributes: extension request invalid len (len > data)) depends_on:MBEDTLS_MD_CAN_SHA256:MBEDTLS_RSA_C mbedtls_x509_csr_parse_file:"../framework/data_files/parse_input/test_csr_v3_all_malformed_attributes_extension_request_sequence_len1.csr.der":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS+MBEDTLS_ERR_ASN1_OUT_OF_DATA -X509 CSR ASN.1 (attributes: extension request invalid len (len < data)) +X509 CSR ASN.1 (attributes: extensionRequest sequence shorter than SET) depends_on:MBEDTLS_MD_CAN_SHA256:MBEDTLS_RSA_C -mbedtls_x509_csr_parse_file:"../framework/data_files/parse_input/test_csr_v3_all_malformed_attributes_extension_request_sequence_len2.csr.der":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS+MBEDTLS_ERR_ASN1_OUT_OF_DATA +mbedtls_x509_csr_parse_file:"../framework/data_files/parse_input/test_csr_v3_all_malformed_attributes_extension_request_sequence_len2.csr.der":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS+MBEDTLS_ERR_ASN1_LENGTH_MISMATCH X509 CSR ASN.1 (extensions: invalid sequence tag) depends_on:MBEDTLS_MD_CAN_SHA256:MBEDTLS_RSA_C From 80807b3af8f13d6b7b74ca6c412d50e96b095627 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 7 May 2026 20:59:00 +0200 Subject: [PATCH 04/10] Remove unreachable CSR extensionRequest check It is now unreachable because: - end_set == end_attr_data is enforced at library/x509_csr.c:236. - end_exts == end_set is enforced at library/x509_csr.c:248. - x509_csr_parse_extensions() only returns success if *p == end_exts, enforced at library/x509_csr.c:188. So after a successful x509_csr_parse_extensions() call, *p == end_exts == end_set == end_attr_data. The condition at line 257 cannot be true. Signed-off-by: Gilles Peskine --- library/x509_csr.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/library/x509_csr.c b/library/x509_csr.c index 4fad0738b4..164bde6941 100644 --- a/library/x509_csr.c +++ b/library/x509_csr.c @@ -253,11 +253,8 @@ static int x509_csr_parse_attributes(mbedtls_x509_csr *csr, if ((ret = x509_csr_parse_extensions(csr, p, end_exts, cb, p_ctx)) != 0) { return ret; } - - if (*p != end_attr_data) { - return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, - MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); - } + /* x509_csr_parse_extensions() guarantees *p == end_exts + * on success */ } *p = end_attr_data; From 4fb9c9e439fd1e7e44697d23d50f00b4642fbe08 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 12 May 2026 17:14:02 +0200 Subject: [PATCH 05/10] basicConstraints pathLenConstraint tests: use standard cA type In test cases for pathLenConstraint parsing in a basicConstraints extension of an X.509 certificate, use the standard type for the leading cA field, namely BOOLEAN. The test data was encoding that field as INTEGER, which was accepted as a deviation from the standard since XySSL 0.9. Signed-off-by: Gilles Peskine --- tests/suites/test_suite_x509parse.data | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data index 314c175ca9..cc6d0f8303 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data @@ -2070,7 +2070,7 @@ mbedtls_x509_crt_parse_file:"../framework/data_files/parse_input/server5.basic-c X509 CRT ASN1 (TBS, inv extBasicConstraint, no pathlen length) depends_on:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA256 -x509parse_crt:"3081b030819aa0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a314301230100603551d130101010406300402010102300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA +x509parse_crt:"3081b030819aa0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a314301230100603551d13010101040630040101ff02300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA X509 CRT ASN1 (inv extBasicConstraint, pathlen is INT_MAX) depends_on:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA256:MBEDTLS_MD_CAN_SHA1 @@ -2082,19 +2082,19 @@ mbedtls_x509_crt_parse_file:"../framework/data_files/parse_input/server1_pathlen X509 CRT ASN1 (TBS, inv extBasicConstraint, pathlen inv length encoding) depends_on:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA256 -x509parse_crt:"3081b130819ba0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a315301330110603551d13010101040730050201010285300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_INVALID_LENGTH +x509parse_crt:"3081b130819ba0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a315301330110603551d13010101040730050101ff0285300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_INVALID_LENGTH X509 CRT ASN1 (TBS, inv extBasicConstraint, pathlen length out of bounds) depends_on:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA256 -x509parse_crt:"3081b130819ba0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a315301330110603551d13010101040730050201010201300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA +x509parse_crt:"3081b130819ba0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a315301330110603551d13010101040730050101ff0201300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA X509 CRT ASN1 (TBS, inv extBasicConstraint, pathlen empty) depends_on:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA256 -x509parse_crt:"3081b130819ba0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a315301330110603551d13010101040730050201010200300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_INVALID_LENGTH +x509parse_crt:"3081b130819ba0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a315301330110603551d13010101040730050101ff0200300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_INVALID_LENGTH X509 CRT ASN1 (TBS, inv extBasicConstraint, pathlen length mismatch) depends_on:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA256 -x509parse_crt:"3081b430819ea0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a318301630140603551d13010101040a30080201010201010500300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH +x509parse_crt:"3081b430819ea0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a318301630140603551d13010101040a30080101ff0201010500300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH X509 CRT ASN1 (TBS, inv v3Ext, ExtKeyUsage bad second tag) depends_on:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA256 From f67b8e5bded0c531b29480b8c5b7285366b1b153 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 12 May 2026 17:06:18 +0200 Subject: [PATCH 06/10] Stop accepting basicConstraints with only pathLenConstraint In an X.509 certificate, a basicConstraints extension where the cA field (BOOLEAN) is omitted but the pathLenConstraint field (INTEGER )is present is syntactically valid, but RFC 5280 states that CAs MUST NOT emit such a certificate. Historically, since XySSL 0.9 in 2008, if a basicConstraints extension started with an INTEGER field, we interpreted that field as a cA value. This was a deliberate change: > Fixed x509_get_ext() to accept some rare certificates (like > www.openssl.org:443) which have an INTEGER instead of a BOOLEAN for > Extension::BasicConstraints::cA. However, 18 years later, this does not seem to be relevant, and the details seem to have been lost. Furthermore, major implementations follow RFC 5280 and interpret `SEQUENCE { INTEGER n }` with cA being set to its default value (FALSE) and pathLenConstraint set to `n`. This means that such a certificate with n != 0 would be interpreted as a CA certificate in Mbed TLS but as a leaf certificate elsewhere, which is dangerous. Since CAs are not supposed to emit such certificates, stop accepting them altogether. Signed-off-by: Gilles Peskine --- library/x509_crt.c | 26 +++++++++++++++----------- tests/suites/test_suite_x509parse.data | 8 ++++++++ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/library/x509_crt.c b/library/x509_crt.c index c2fb22726b..15d7d2e686 100644 --- a/library/x509_crt.c +++ b/library/x509_crt.c @@ -513,6 +513,7 @@ static int x509_get_basic_constraints(unsigned char **p, } if (*p == end) { + /* Empty basicConstraints: valid, not a CA. */ return 0; } @@ -524,18 +525,21 @@ static int x509_get_basic_constraints(unsigned char **p, } if ((ret = mbedtls_asn1_get_bool(p, end, ca_istrue)) != 0) { - if (ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { - ret = mbedtls_asn1_get_int(p, end, ca_istrue); - } - - if (ret != 0) { - return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); - } - - if (*ca_istrue != 0) { - *ca_istrue = 1; - } + /* If the SEQUENCE starts with an INTEGER and not a BOOLEAN, + * according to RFC 5280, it's syntactically valid, but it's + * something that a CA MUST NOT produce. So we reject it. + * + * Note: historically, from XySSL 0.9 to Mbed TLS 3.6.6/4.1.0, + * `SEQUENCE { INTEGER n }` was interpreted as cA=FALSE if n == 0 + * and cA=TRUE if n != 0. This was dangerous since + * `SEQUENCE { INTEGER n }` should be parsed as cA=FALSE + * according to RFC 5280, so we stopped doing it. + */ + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); } + /* `SEQUENCE { BOOLEAN FALSE }` is not DER since default-value fields + * must be omitted in DER. But it seems harmless, and Mbed TLS has + * always accepted it, so we continue to accept it. */ if (*p == end) { return 0; diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data index cc6d0f8303..f8e60298eb 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data @@ -2068,6 +2068,14 @@ X509 CRT ASN1: basicConstraints overflow would make CA=1 (good self signature) depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_MD_CAN_SHA256 mbedtls_x509_crt_parse_file:"../framework/data_files/parse_input/server5.basic-constraints-sequence-overflow.selfsigned.crt":MBEDTLS_ERR_X509_INVALID_EXTENSIONS:0 +X509 CRT ASN1: basicConstraints: no cA but pathLenConstraint (bad signature) +depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_MD_CAN_SHA256 +mbedtls_x509_crt_parse_file:"../framework/data_files/parse_input/server5.basic-constraints-integer-first.badsign.crt":MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, MBEDTLS_ERR_ASN1_UNEXPECTED_TAG):0 + +X509 CRT ASN1: basicConstraints: no cA but pathLenConstraint (good self signature) +depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_ECP_HAVE_SECP256R1:MBEDTLS_MD_CAN_SHA256 +mbedtls_x509_crt_parse_file:"../framework/data_files/parse_input/server5.basic-constraints-integer-first.selfsigned.crt":MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, MBEDTLS_ERR_ASN1_UNEXPECTED_TAG):0 + X509 CRT ASN1 (TBS, inv extBasicConstraint, no pathlen length) depends_on:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA256 x509parse_crt:"3081b030819aa0030201028204deadbeef300d06092a864886f70d01010b0500300c310a30080600130454657374301c170c303930313031303030303030170c303931323331323335393539300c310a30080600130454657374302a300d06092A864886F70D010101050003190030160210ffffffffffffffffffffffffffffffff0202ffffa100a200a314301230100603551d13010101040630040101ff02300d06092a864886f70d01010b0500030200ff":"":MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_OUT_OF_DATA From 8e077171056afd818bbbc49f2c9b4628cfe2ab1f Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 12 May 2026 17:21:48 +0200 Subject: [PATCH 07/10] basicConstraints with leading INTEGER field: document the behavior change Announce the security fix together with the overflow fix, since the two bugs are pretty much indistinguishable from a black-box perspective, despite being due to independent problems in the code. Signed-off-by: Gilles Peskine --- ChangeLog.d/x509-crt-parse-basicConstraints.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ChangeLog.d/x509-crt-parse-basicConstraints.txt b/ChangeLog.d/x509-crt-parse-basicConstraints.txt index c5b25f114b..4fd450e18e 100644 --- a/ChangeLog.d/x509-crt-parse-basicConstraints.txt +++ b/ChangeLog.d/x509-crt-parse-basicConstraints.txt @@ -1,7 +1,15 @@ Security - * Fix a bug in the X.509 certificate parser that caused some inputs + * Fix two bugs in the X.509 certificate parser that caused some inputs with a malformed basicConstraints extension to be accepted. This could have dangerous results, in particular causing some certificates to be parsed as CA certificates when other X.509 implementations would parse them as end-entity certificates. Reported by Mohammad Seet (mhdsait101). CVE-2026-TODO + +Changes + * X.509 certificates with a basicConstraints extension starting with an + INTEGER field are no longer accepted. According to RFC 5280, such + certificates are syntactically valid and the first field is the + pathLenConstraint value, but certificate authorities must not emit such + certificates. Mbed TLS interpreted it as a cA value, which was nonstandard + and dangerous. From 8c398012dde204b284b0bad69a6e59f994b4c3b6 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 3 Jun 2026 18:23:42 +0200 Subject: [PATCH 08/10] Add attribution for the second bug Signed-off-by: Gilles Peskine --- ChangeLog.d/x509-crt-parse-basicConstraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog.d/x509-crt-parse-basicConstraints.txt b/ChangeLog.d/x509-crt-parse-basicConstraints.txt index 4fd450e18e..30ee25a785 100644 --- a/ChangeLog.d/x509-crt-parse-basicConstraints.txt +++ b/ChangeLog.d/x509-crt-parse-basicConstraints.txt @@ -4,7 +4,7 @@ Security could have dangerous results, in particular causing some certificates to be parsed as CA certificates when other X.509 implementations would parse them as end-entity certificates. Reported by Mohammad Seet - (mhdsait101). CVE-2026-TODO + (mhdsait101) and Pablo Ruiz García. CVE-2026-TODO Changes * X.509 certificates with a basicConstraints extension starting with an From 1aaec34173f36c6a340977e1d049a0dd98905916 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 3 Jun 2026 18:24:04 +0200 Subject: [PATCH 09/10] Add CVE-ID Signed-off-by: Gilles Peskine --- ChangeLog.d/x509-crt-parse-basicConstraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog.d/x509-crt-parse-basicConstraints.txt b/ChangeLog.d/x509-crt-parse-basicConstraints.txt index 30ee25a785..a67f992ab5 100644 --- a/ChangeLog.d/x509-crt-parse-basicConstraints.txt +++ b/ChangeLog.d/x509-crt-parse-basicConstraints.txt @@ -4,7 +4,7 @@ Security could have dangerous results, in particular causing some certificates to be parsed as CA certificates when other X.509 implementations would parse them as end-entity certificates. Reported by Mohammad Seet - (mhdsait101) and Pablo Ruiz García. CVE-2026-TODO + (mhdsait101) and Pablo Ruiz García. CVE-2026-49300 Changes * X.509 certificates with a basicConstraints extension starting with an From abb6bcd35400128d7d529405369e7592eac73c16 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 16 Jun 2026 19:34:32 +0200 Subject: [PATCH 10/10] Credit independent report Signed-off-by: Gilles Peskine --- ChangeLog.d/x509-crt-parse-basicConstraints.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog.d/x509-crt-parse-basicConstraints.txt b/ChangeLog.d/x509-crt-parse-basicConstraints.txt index a67f992ab5..251dc8f0fd 100644 --- a/ChangeLog.d/x509-crt-parse-basicConstraints.txt +++ b/ChangeLog.d/x509-crt-parse-basicConstraints.txt @@ -4,7 +4,7 @@ Security could have dangerous results, in particular causing some certificates to be parsed as CA certificates when other X.509 implementations would parse them as end-entity certificates. Reported by Mohammad Seet - (mhdsait101) and Pablo Ruiz García. CVE-2026-49300 + (mhdsait101), Pablo Ruiz García and NVIDIA Project Vanessa. CVE-2026-49300 Changes * X.509 certificates with a basicConstraints extension starting with an