@@ -1795,6 +1795,73 @@ int test_wolfSSL_MatchDomainName_idn(void)
17951795 return EXPECT_RESULT ();
17961796}
17971797
1798+ /* Regression test mirroring curl test 311 ("HTTPS wrong subjectAltName but
1799+ * right CN"). The leaf Subject CN is the wanted host ("localhost") while its
1800+ * only dNSName SAN carries an embedded NUL ("localhost\0h"). Such a SAN is an
1801+ * invalid presented identifier (RFC 6125 Sec. 6.3 / RFC 9525 Sec. 6.3), not a
1802+ * malformed certificate, so the certificate must parse. Verification must then
1803+ * report DOMAIN_NAME_MISMATCH: the SAN presence suppresses Subject CN fallback
1804+ * and the NUL name can never match the NUL-free reference host, so the
1805+ * otherwise-correct CN must not rescue it. Before the parser stored rather than
1806+ * rejected the entry, verification aborted earlier with ASN_PARSE_E. */
1807+ int test_wolfSSL_X509_check_host_embedded_nul_san (void )
1808+ {
1809+ EXPECT_DECLS ;
1810+ #if !defined(NO_FILESYSTEM ) && !defined(NO_CERTS ) && !defined(NO_RSA ) && \
1811+ defined(OPENSSL_EXTRA ) && defined(WOLFSSL_CERT_GEN ) && \
1812+ defined(WOLFSSL_CERT_EXT ) && defined(WOLFSSL_ALT_NAMES ) && \
1813+ !defined(NO_SHA256 ) && !defined(NO_ASN )
1814+ WOLFSSL_EVP_PKEY * priv = NULL ;
1815+ WOLFSSL_X509 * leaf = NULL ;
1816+ const char * server_cert = "./certs/test/server-goodcn.pem" ;
1817+ const char host [] = "localhost" ;
1818+ /* dNSName "localhost" + embedded NUL + 'h', length 11. */
1819+ static const byte nulSan [] = {
1820+ 'l' , 'o' , 'c' , 'a' , 'l' , 'h' , 'o' , 's' , 't' , '\0' , 'h'
1821+ };
1822+ byte * keyPt = NULL ;
1823+ const byte * der = NULL ;
1824+ int derSz = 0 ;
1825+ DecodedCert dCert ;
1826+ int parseRet = -1 ;
1827+
1828+ keyPt = (byte * )server_key_der_2048 ;
1829+ ExpectNotNull (priv = wolfSSL_d2i_PrivateKey (EVP_PKEY_RSA , NULL ,
1830+ (const unsigned char * * )& keyPt , sizeof_server_key_der_2048 ));
1831+
1832+ /* server-goodcn.pem has CN=localhost and no SAN; add one dNSName SAN that
1833+ * carries an embedded NUL, then re-sign so the SAN is serialized. */
1834+ ExpectNotNull (leaf = wolfSSL_X509_load_certificate_file (server_cert ,
1835+ WOLFSSL_FILETYPE_PEM ));
1836+ ExpectIntEQ (wolfSSL_X509_add_altname_ex (leaf , (const char * )nulSan ,
1837+ sizeof (nulSan ), ASN_DNS_TYPE ), WOLFSSL_SUCCESS );
1838+ ExpectIntGT (wolfSSL_X509_sign (leaf , priv , EVP_sha256 ()), 0 );
1839+
1840+ /* Regression pin: the signed certificate must parse despite the embedded
1841+ * NUL (it aborted with ASN_PARSE_E before the parser stored the entry). */
1842+ ExpectNotNull (der = wolfSSL_X509_get_der (leaf , & derSz ));
1843+ ExpectIntGT (derSz , 0 );
1844+ if ((der != NULL ) && (derSz > 0 )) {
1845+ wc_InitDecodedCert (& dCert , der , (word32 )derSz , NULL );
1846+ parseRet = wc_ParseCert (& dCert , CERT_TYPE , NO_VERIFY , NULL );
1847+ ExpectIntEQ (parseRet , 0 );
1848+ wc_FreeDecodedCert (& dCert );
1849+ }
1850+
1851+ /* Security pin: the host must still be rejected. With parsing now
1852+ * succeeding, the only remaining failure path in check_host for a
1853+ * non-IP host is the hostname comparison, so a failure here means
1854+ * DOMAIN_NAME_MISMATCH: the SAN presence suppressed CN fallback and the
1855+ * NUL name did not match. The correct CN must not rescue the wrong SAN. */
1856+ ExpectIntEQ (wolfSSL_X509_check_host (leaf , host , XSTRLEN (host ), 0 , NULL ),
1857+ WC_NO_ERR_TRACE (WOLFSSL_FAILURE ));
1858+
1859+ wolfSSL_X509_free (leaf );
1860+ wolfSSL_EVP_PKEY_free (priv );
1861+ #endif
1862+ return EXPECT_RESULT ();
1863+ }
1864+
17981865int test_wolfSSL_X509_max_altnames (void )
17991866{
18001867 EXPECT_DECLS ;
0 commit comments