Honour chklen when matching an IP in X509_check_host

wolfSSL_X509_check_host takes an explicit length and its own validation
accepts a buffer with no NUL terminator, since only an embedded NUL is
rejected and a trailing one is merely stripped when present. The iPAddress
check then called CheckIPAddr, which drops the length and measures the
buffer with XSTRLEN, reading past the end of a caller supplied buffer that
is length delimited rather than terminated. This ran on every call, not
only when checking an IP address, and is compiled in whenever
WOLFSSL_IP_ALT_NAME is defined, which OPENSSL_ALL and WOLFSSL_QT enable.

Call CheckHostName directly with the caller's length and the IP flag set.
That is what CheckIPAddr does internally, minus the length being recomputed.
Behaviour is unchanged for NUL terminated input, because the normalization
above already leaves chklen equal to the string length in that case. It
also fixes a matching bug, since a length delimited IP address sitting in a
longer buffer no longer fails to match an iPAddress entry.

Add a regression test covering an interior slice of a longer buffer and a
buffer sized exactly to the name with no terminator.

Fixes F-7248.
pull/11018/head
Tobias Frauenschläger 2026-07-28 14:22:45 +02:00
parent 2788d9dac1
commit 96fb284398
3 changed files with 40 additions and 1 deletions

View File

@ -15737,7 +15737,10 @@ int wolfSSL_X509_check_host(WOLFSSL_X509 *x, const char *chk, size_t chklen,
}
#ifdef WOLFSSL_IP_ALT_NAME
ret = CheckIPAddr(dCert, (char *)chk);
/* chk is length delimited and may not be NUL terminated, so check it
* against the iPAddress entries directly rather than through the
* NUL terminated CheckIPAddr helper. */
ret = CheckHostName(dCert, (char *)chk, chklen, 0, 1);
if (ret == 0) {
goto out;
}

View File

@ -435,6 +435,40 @@ int test_wolfSSL_X509_check_host(void)
return EXPECT_RESULT();
}
int test_wolfSSL_X509_check_host_len(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && !defined(NO_FILESYSTEM) \
&& !defined(NO_SHA) && !defined(NO_RSA) && defined(WOLFSSL_IP_ALT_NAME)
/* chk is length delimited and need not be NUL terminated, so nothing
* past chk[chklen - 1] may be read. */
X509* x509 = NULL;
const char sliced[] = "127.0.0.1extra";
const size_t ipLen = 9; /* length of "127.0.0.1" */
char* exact = NULL;
/* cliCertFile has subjectAltName set to 'example.com', '127.0.0.1' */
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(cliCertFile,
SSL_FILETYPE_PEM));
/* An interior slice of a longer buffer must match the iPAddress SAN. */
ExpectIntEQ(X509_check_host(x509, sliced, ipLen, 0, NULL),
WOLFSSL_SUCCESS);
/* Same name in a buffer sized exactly to it, with no terminator. */
ExpectNotNull(exact = (char*)XMALLOC(ipLen, NULL, DYNAMIC_TYPE_TMP_BUFFER));
if (exact != NULL) {
XMEMCPY(exact, "127.0.0.1", ipLen);
ExpectIntEQ(X509_check_host(x509, exact, ipLen, 0, NULL),
WOLFSSL_SUCCESS);
}
XFREE(exact, NULL, DYNAMIC_TYPE_TMP_BUFFER);
X509_free(x509);
#endif
return EXPECT_RESULT();
}
int test_wolfSSL_X509_check_email(void)
{
EXPECT_DECLS;

View File

@ -33,6 +33,7 @@ int test_wolfSSL_i2d_X509_NAME_canon(void);
int test_wolfSSL_X509_subject_name_hash(void);
int test_wolfSSL_X509_issuer_name_hash(void);
int test_wolfSSL_X509_check_host(void);
int test_wolfSSL_X509_check_host_len(void);
int test_wolfSSL_X509_check_email(void);
int test_wolfSSL_X509(void);
int test_wolfSSL_X509_get0_tbs_sigalg(void);
@ -68,6 +69,7 @@ int test_wolfSSL_X509_cmp(void);
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_subject_name_hash), \
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_issuer_name_hash), \
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_check_host), \
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_check_host_len), \
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_check_email), \
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509), \
TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_get0_tbs_sigalg), \