diff --git a/ChangeLog.d/inet_pton.txt b/ChangeLog.d/inet_pton.txt new file mode 100644 index 0000000000..526cd9be5f --- /dev/null +++ b/ChangeLog.d/inet_pton.txt @@ -0,0 +1,4 @@ +Security + * Fix a limited buffer underflow in x509_inet_pton_ipv6(). In rare cases + (e.g. on platforms with memory protection when the overread crosses page + boundary) this could lead to DoS. Found and reported by Haruto Kimura. diff --git a/library/x509_crt.c b/library/x509_crt.c index 59c3204467..028ae8bf14 100644 --- a/library/x509_crt.c +++ b/library/x509_crt.c @@ -2719,22 +2719,25 @@ static int x509_inet_pton_ipv6(const char *src, void *dst) if (*p == '\0') { break; } else if (*p == '.') { - /* Don't accept IPv4 too early or late */ - if ((nonzero_groups == 0 && zero_group_start == -1) || + /* Don't accept IPv4 too early or late: + * - The first 6 nonzero groups must be 16 bit pieces of address delimited by ':' + * - This might be fully or partially represented with compressed syntax (a zero + * group "::") + */ + if ((nonzero_groups < 6 && zero_group_start == -1) || nonzero_groups >= 7) { break; } - /* Walk back to prior ':', then parse as IPv4-mapped */ - int steps = 4; + /* Walk back to prior ':', then parse as IPv4-mapped. + * At this point nonzero_groups == 6 or zero_group_start >= 0. Either way we have a + * ':' before the current position and still inside the buffer. Thus it is safe to + * search back for that ':' without any further checks. + */ do { p--; - steps--; - } while (*p != ':' && steps > 0); + } while (*p != ':'); - if (*p != ':') { - break; - } p++; nonzero_groups--; if (x509_inet_pton_ipv4((const char *) p, diff --git a/tests/scripts/components-configuration-x509.sh b/tests/scripts/components-configuration-x509.sh index 8010a2a2e6..66e9b16da8 100644 --- a/tests/scripts/components-configuration-x509.sh +++ b/tests/scripts/components-configuration-x509.sh @@ -28,8 +28,9 @@ component_test_sw_inet_pton () { # MBEDTLS_TEST_HOOKS required for x509_crt_parse_cn_inet_pton scripts/config.py set MBEDTLS_TEST_HOOKS - $MAKE_COMMAND CFLAGS="-DMBEDTLS_TEST_SW_INET_PTON" + CC=$ASAN_CC CFLAGS="-DMBEDTLS_TEST_SW_INET_PTON" cmake -D CMAKE_BUILD_TYPE:String=Asan . + make msg "test: default plus MBEDTLS_TEST_SW_INET_PTON" - $MAKE_COMMAND test + make test } diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data index 0ca27a9d68..4fc5054b49 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data @@ -1170,6 +1170,9 @@ x509_crt_parse_cn_inet_pton:"\:\:ffff\:1111.2.3.4":"":0 X509 CRT parse CN: IPv6 invalid address IPv4-mapped #3 x509_crt_parse_cn_inet_pton:"\:\:1.2.3.4\:ffff":"":0 +X509 CRT parse CN: IPv6 invalid address IPv4-mapped #4 +x509_crt_parse_cn_inet_pton:"1.2.3.4\:":"":0 + X509 CRT verification with ca callback: failure depends_on:MBEDTLS_PEM_PARSE_C:PSA_WANT_ALG_SHA_1:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK x509_verify_ca_cb_failure:"../framework/data_files/server1.crt":"../framework/data_files/test-ca.crt":"NULL":MBEDTLS_ERR_X509_FATAL_ERROR diff --git a/tests/suites/test_suite_x509parse.function b/tests/suites/test_suite_x509parse.function index e892ab9a9e..25d229d6f1 100644 --- a/tests/suites/test_suite_x509parse.function +++ b/tests/suites/test_suite_x509parse.function @@ -488,12 +488,22 @@ exit: void x509_crt_parse_cn_inet_pton(const char *cn, data_t *exp, int ref_ret) { uint32_t addr[4]; - size_t addrlen = mbedtls_x509_crt_parse_cn_inet_pton(cn, addr); + + char *cn_local = NULL; + size_t cn_local_len = strlen(cn) + 1; + TEST_CALLOC(cn_local, cn_local_len); + memcpy(cn_local, cn, cn_local_len); + + size_t addrlen = mbedtls_x509_crt_parse_cn_inet_pton(cn_local, addr); TEST_EQUAL(addrlen, (size_t) ref_ret); if (addrlen) { TEST_MEMORY_COMPARE(exp->x, exp->len, addr, addrlen); } + +exit: + mbedtls_free(cn_local); + } /* END_CASE */