From 62dd8a556c01cc289d4fb3f19352a78ede90dac2 Mon Sep 17 00:00:00 2001 From: v1rtu3x <149050962+v1rtu3x@users.noreply.github.com> Date: Fri, 16 Oct 2026 13:53:40 +0300 Subject: [PATCH] x509: Accept basicConstraints with omitted cA RFC 5280 defines cA as BOOLEAN DEFAULT FALSE, so a basicConstraints extension containing only pathLenConstraint (SEQUENCE { INTEGER n }) is syntactically valid and must be interpreted as cA=FALSE. The fix for CVE-2026-49300 rejected this encoding outright, breaking compatibility with certificates emitted by devices such as Eaton/CODESYS PLCs. Tolerate MBEDTLS_ERR_ASN1_UNEXPECTED_TAG from the cA BOOLEAN parse, keeping cA FALSE and letting the existing INTEGER parser validate pathLenConstraint. The INTEGER value is never used to determine cA, so the CVE fix is preserved. Fixes #11006 Signed-off-by: v1rtu3x <149050962+v1rtu3x@users.noreply.github.com> --- .../x509-basic-constraints-integer-first.txt | 5 +++++ library/x509_crt.c | 12 +++++----- tests/suites/test_suite_x509parse.data | 20 +++++++++++++++-- tests/suites/test_suite_x509parse.function | 22 +++++++++++++++++++ 4 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 ChangeLog.d/x509-basic-constraints-integer-first.txt diff --git a/ChangeLog.d/x509-basic-constraints-integer-first.txt b/ChangeLog.d/x509-basic-constraints-integer-first.txt new file mode 100644 index 0000000000..409a626aa0 --- /dev/null +++ b/ChangeLog.d/x509-basic-constraints-integer-first.txt @@ -0,0 +1,5 @@ +Bugfix + * Accept a basicConstraints extension that omits cA and contains only + pathLenConstraint, interpreting it as cA=FALSE as specified by RFC 5280. + This restores compatibility with certificates emitted by some devices + without accepting them as CA certificates. Fixes #11006. diff --git a/library/x509_crt.c b/library/x509_crt.c index a6fcd1b4e6..e3549a1c2b 100644 --- a/library/x509_crt.c +++ b/library/x509_crt.c @@ -518,17 +518,19 @@ static int x509_get_basic_constraints(unsigned char **p, } if ((ret = mbedtls_asn1_get_bool(p, end, ca_istrue)) != 0) { - /* 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. + /* cA is DEFAULT FALSE. If it is absent, the first element must + * be pathLenConstraint. Keep cA false and let the INTEGER parser + * below validate the element. * * 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. + * according to RFC 5280. */ - return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + 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 diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data index 6a8e30f524..be519191a3 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data @@ -2062,11 +2062,27 @@ mbedtls_x509_crt_parse_file:"../framework/data_files/parse_input/server5.basic-c X509 CRT ASN1: basicConstraints: no cA but pathLenConstraint (bad signature) depends_on:PSA_HAVE_ALG_ECDSA_VERIFY:PSA_WANT_ECC_SECP_R1_256:PSA_WANT_ALG_SHA_256 -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 +mbedtls_x509_crt_parse_file:"../framework/data_files/parse_input/server5.basic-constraints-integer-first.badsign.crt":0:1 + +X509 CRT ASN1: basicConstraints: no cA, pathLen only: not a CA +depends_on:PSA_HAVE_ALG_ECDSA_VERIFY:PSA_WANT_ECC_SECP_R1_256:PSA_WANT_ALG_SHA_256 +mbedtls_x509_get_ca_istrue:"../framework/data_files/parse_input/server5.basic-constraints-integer-first.badsign.crt":0 X509 CRT ASN1: basicConstraints: no cA but pathLenConstraint (good self signature) depends_on:PSA_HAVE_ALG_ECDSA_VERIFY:PSA_WANT_ECC_SECP_R1_256:PSA_WANT_ALG_SHA_256 -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 +mbedtls_x509_crt_parse_file:"../framework/data_files/parse_input/server5.basic-constraints-integer-first.selfsigned.crt":0:1 + +X509 CRT ASN1: basicConstraints: no cA but zero pathLenConstraint +depends_on:PSA_HAVE_ALG_ECDSA_VERIFY:PSA_WANT_ECC_SECP_R1_256:PSA_WANT_ALG_SHA_256 +x509_crt_parse_basic_constraints:"308201573081fea003020102020c53a2cb4b124ead837da894b2300a06082a8648ce3d040302300e310c300a06035504030c03457665301e170d3236303531323039353330345a170d3336303531323039353330345a300e310c300a06035504030c034576653059301306072a8648ce3d020106082a8648ce3d0301070342000437cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f768225962924ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edffa3423040300f0603551d130101ff04053003020100300e0603551d0f0101ff040403020780301d0603551d0e041604145061a58fd407d9d782010ce5657f8c6346a713be300a06082a8648ce3d0403020348003045022100d67d572af0aa540cd0f9d86d43788c178ca9501d8c0f4f6690c773f4b82c4b9a022011620feee62aca3944b8321636849030014ef80604550031312bb707b7f2bf04":0:0:0 + +X509 CRT ASN1: basicConstraints: invalid cA BOOLEAN length +depends_on:PSA_HAVE_ALG_ECDSA_VERIFY:PSA_WANT_ECC_SECP_R1_256:PSA_WANT_ALG_SHA_256 +x509_crt_parse_basic_constraints:"308201573081fea003020102020c53a2cb4b124ead837da894b2300a06082a8648ce3d040302300e310c300a06035504030c03457665301e170d3236303531323039353330345a170d3336303531323039353330345a300e310c300a06035504030c034576653059301306072a8648ce3d020106082a8648ce3d0301070342000437cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f768225962924ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edffa3423040300f0603551d130101ff040530030100ff300e0603551d0f0101ff040403020780301d0603551d0e041604145061a58fd407d9d782010ce5657f8c6346a713be300a06082a8648ce3d0403020348003045022100d67d572af0aa540cd0f9d86d43788c178ca9501d8c0f4f6690c773f4b82c4b9a022011620feee62aca3944b8321636849030014ef80604550031312bb707b7f2bf04":MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, MBEDTLS_ERR_ASN1_INVALID_LENGTH):0:0 + +X509 CRT ASN1: basicConstraints: neither cA nor pathLenConstraint +depends_on:PSA_HAVE_ALG_ECDSA_VERIFY:PSA_WANT_ECC_SECP_R1_256:PSA_WANT_ALG_SHA_256 +x509_crt_parse_basic_constraints:"308201573081fea003020102020c53a2cb4b124ead837da894b2300a06082a8648ce3d040302300e310c300a06035504030c03457665301e170d3236303531323039353330345a170d3336303531323039353330345a300e310c300a06035504030c034576653059301306072a8648ce3d020106082a8648ce3d0301070342000437cc56d976091e5a723ec7592dff206eee7cf9069174d0ad14b5f768225962924ee500d82311ffea2fd2345d5d16bd8a88c26b770d55cd8a2a0efa01c8b4edffa3423040300f0603551d130101ff04053003050100300e0603551d0f0101ff040403020780301d0603551d0e041604145061a58fd407d9d782010ce5657f8c6346a713be300a06082a8648ce3d0403020348003045022100d67d572af0aa540cd0f9d86d43788c178ca9501d8c0f4f6690c773f4b82c4b9a022011620feee62aca3944b8321636849030014ef80604550031312bb707b7f2bf04":MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, MBEDTLS_ERR_ASN1_UNEXPECTED_TAG):0:0 X509 CRT ASN1 (TBS, inv extBasicConstraint, no pathlen length) depends_on:PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY:PSA_WANT_ALG_SHA_256 diff --git a/tests/suites/test_suite_x509parse.function b/tests/suites/test_suite_x509parse.function index 64115a3fe2..d59fc2b2e2 100644 --- a/tests/suites/test_suite_x509parse.function +++ b/tests/suites/test_suite_x509parse.function @@ -1127,6 +1127,28 @@ exit: } /* END_CASE */ +/* BEGIN_CASE depends_on:MBEDTLS_X509_CRT_PARSE_C */ +void x509_crt_parse_basic_constraints(data_t *buf, int result, + int ca_istrue, int path_len_constraint) +{ + mbedtls_x509_crt crt; + + mbedtls_x509_crt_init(&crt); + USE_PSA_INIT(); + + TEST_EQUAL(mbedtls_x509_crt_parse_der(&crt, buf->x, buf->len), result); + if (result == 0) { + TEST_EQUAL(mbedtls_x509_crt_get_ca_istrue(&crt), ca_istrue); + /* max_pathlen is 1 higher than pathLenConstraint. */ + TEST_EQUAL(crt.max_pathlen, path_len_constraint + 1); + } + +exit: + mbedtls_x509_crt_free(&crt); + USE_PSA_DONE(); +} +/* END_CASE */ + /* BEGIN_CASE depends_on:MBEDTLS_X509_CRT_PARSE_C */ void x509parse_crt(data_t *buf, char *result_str, int result) {