diff --git a/doc/dox_comments/header_files-ja/ssl.h b/doc/dox_comments/header_files-ja/ssl.h index dc99476109..d38641d937 100644 --- a/doc/dox_comments/header_files-ja/ssl.h +++ b/doc/dox_comments/header_files-ja/ssl.h @@ -5435,6 +5435,22 @@ int wolfSSL_want_read(WOLFSSL* ssl); */ int wolfSSL_check_domain_name(WOLFSSL* ssl, const char* dn); +/*! + \ingroup Setup + + \brief wolfSSL_connect() または wolfSSL_accept() の前にこの関数を呼ぶと、 + ピア証明書の SAN iPAddress エントリに対する IP アドレス検証を追加します。 + + \return SSL_SUCCESS 成功。 + \return SSL_FAILURE パラメータ不正またはメモリ確保失敗。 + + \param ssl wolfSSL_new() で作成された WOLFSSL 構造体へのポインタ。 + \param ipaddr 検証する NULL 終端 ASCII IP アドレス文字列。 + + \sa wolfSSL_check_domain_name +*/ +int wolfSSL_check_ip_address(WOLFSSL* ssl, const char* ipaddr); + /*! \ingroup TLS diff --git a/doc/dox_comments/header_files/ssl.h b/doc/dox_comments/header_files/ssl.h index 547f0077fa..aaa55480e9 100644 --- a/doc/dox_comments/header_files/ssl.h +++ b/doc/dox_comments/header_files/ssl.h @@ -6432,6 +6432,37 @@ int wolfSSL_want_write(WOLFSSL* ssl); */ int wolfSSL_check_domain_name(WOLFSSL* ssl, const char* dn); +/*! + \ingroup Setup + + \brief Calling this function before wolfSSL_connect() or wolfSSL_accept() + adds an IP-address identity check against the peer certificate SAN + iPAddress entries. + + \return SSL_SUCCESS upon success. + \return SSL_FAILURE if parameters are invalid or memory allocation fails. + + \param ssl a pointer to a WOLFSSL structure, created using wolfSSL_new(). + \param ipaddr NULL-terminated ASCII IP address string to verify against the + peer certificate. + + _Example_ + \code + int ret = 0; + WOLFSSL* ssl; + const char* ip = "127.0.0.1"; + ... + + ret = wolfSSL_check_ip_address(ssl, ip); + if (ret != SSL_SUCCESS) { + // failed to enable IP check + } + \endcode + + \sa wolfSSL_check_domain_name +*/ +int wolfSSL_check_ip_address(WOLFSSL* ssl, const char* ipaddr); + /*! \ingroup TLS diff --git a/src/internal.c b/src/internal.c index 81df695b7a..3c3c20f76f 100644 --- a/src/internal.c +++ b/src/internal.c @@ -8627,6 +8627,7 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl) FreeHandshakeHashes(ssl); #endif XFREE(ssl->buffers.domainName.buffer, ssl->heap, DYNAMIC_TYPE_DOMAIN); + XFREE(ssl->buffers.ipasc.buffer, ssl->heap, DYNAMIC_TYPE_DOMAIN); /* clear keys struct after session */ ForceZero(&ssl->keys, sizeof(Keys)); @@ -16792,7 +16793,7 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, (char*)ssl->buffers.domainName.buffer, (ssl->buffers.domainName.buffer == NULL ? 0 : (word32)XSTRLEN(ssl->buffers.domainName.buffer)), - NULL, 0) != 1) { + NULL, 0, 0) != 1) { WOLFSSL_MSG("DomainName match failed"); /* try to get peer key still */ ret = DOMAIN_NAME_MISMATCH; @@ -16802,6 +16803,17 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, #endif /* WOLFSSL_ALL_NO_CN_IN_SAN */ } +#ifndef OPENSSL_EXTRA + if (!ssl->options.verifyNone && ssl->buffers.ipasc.buffer) { + if (CheckIPAddr(args->dCert, + (const char*)ssl->buffers.ipasc.buffer) != 0) { + WOLFSSL_MSG("IPAddr match on alt names failed"); + ret = IPADDR_MISMATCH; + WOLFSSL_ERROR_VERBOSE(ret); + } + } +#endif + /* decode peer key */ switch (args->dCert->keyOID) { #ifndef NO_RSA diff --git a/src/ssl.c b/src/ssl.c index c0d8081967..e92c8a0f41 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -7462,6 +7462,48 @@ int wolfSSL_check_domain_name(WOLFSSL* ssl, const char* dn) } } +/* call before SSL_connect, if verifying will add IP SAN check to + date check and signature check */ +WOLFSSL_ABI +int wolfSSL_check_ip_address(WOLFSSL* ssl, const char* ipaddr) +{ + WOLFSSL_ENTER("wolfSSL_check_ip_address"); + + if (ssl == NULL || ipaddr == NULL) { + WOLFSSL_MSG("Bad function argument: NULL"); + return WOLFSSL_FAILURE; + } + + if (ssl->buffers.ipasc.buffer != NULL) { + XFREE(ssl->buffers.ipasc.buffer, ssl->heap, DYNAMIC_TYPE_DOMAIN); + ssl->buffers.ipasc.buffer = NULL; + ssl->buffers.ipasc.length = 0; + } + + ssl->buffers.ipasc.length = (word32)XSTRLEN(ipaddr); + ssl->buffers.ipasc.buffer = (byte*)XMALLOC(ssl->buffers.ipasc.length + 1, + ssl->heap, DYNAMIC_TYPE_DOMAIN); + if (ssl->buffers.ipasc.buffer == NULL) { + ssl->error = MEMORY_ERROR; + return WOLFSSL_FAILURE; + } + + XMEMCPY(ssl->buffers.ipasc.buffer, ipaddr, ssl->buffers.ipasc.length); + ssl->buffers.ipasc.buffer[ssl->buffers.ipasc.length] = '\0'; + +#ifdef OPENSSL_EXTRA + if (ssl->param == NULL) { + return WOLFSSL_FAILURE; + } + if (wolfSSL_X509_VERIFY_PARAM_set1_ip_asc(ssl->param, ipaddr) != + WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } +#endif + + return WOLFSSL_SUCCESS; +} + #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) const char *wolfSSL_get0_peername(WOLFSSL *ssl) { if (ssl == NULL) { diff --git a/tests/api.c b/tests/api.c index 12a26d1e93..18f75e0f85 100644 --- a/tests/api.c +++ b/tests/api.c @@ -15288,6 +15288,8 @@ static int test_wolfSSL_check_domain_basic_client_ssl(WOLFSSL* ssl) return EXPECT_RESULT(); } +/* Verify wolfSSL_check_domain_name() controls DNS-name matching during + * handshake with expected fail/pass outcomes. */ static int test_wolfSSL_check_domain_basic(void) { EXPECT_DECLS; @@ -15320,6 +15322,102 @@ static int test_wolfSSL_check_domain_basic(void) } #endif /* HAVE_SSL_MEMIO_TESTS_DEPENDENCIES */ +#if defined(OPENSSL_EXTRA) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ + (defined(WOLFSSL_IP_ALT_NAME) || defined(OPENSSL_ALL)) && \ + !defined(OPENSSL_COMPATIBLE_DEFAULTS) && !defined(NO_SHA256) +static const char* ipaddr = NULL; +static int test_wolfSSL_check_ip_param_client_ssl(WOLFSSL* ssl) +{ + EXPECT_DECLS; + X509_VERIFY_PARAM* param = NULL; + + ExpectNotNull(param = SSL_get0_param(ssl)); + ExpectIntEQ(X509_VERIFY_PARAM_set1_ip_asc(param, ipaddr), WOLFSSL_SUCCESS); + + return EXPECT_RESULT(); +} + +/* Verify the OpenSSL-compat verify-param path: + * SSL_get0_param() + X509_VERIFY_PARAM_set1_ip_asc() controls IP SAN matching + * during handshake. */ +static int test_wolfSSL_check_ip_param_basic(void) +{ + EXPECT_DECLS; + test_ssl_cbf func_cb_client; + test_ssl_cbf func_cb_server; + + XMEMSET(&func_cb_client, 0, sizeof(func_cb_client)); + XMEMSET(&func_cb_server, 0, sizeof(func_cb_server)); + + func_cb_client.ssl_ready = &test_wolfSSL_check_ip_param_client_ssl; + + ipaddr = "127.0.0.2"; + /* Expect to fail: cert SAN IP is 127.0.0.1 */ + ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&func_cb_client, + &func_cb_server, NULL), -1001); + + ipaddr = "127.0.0.1"; + /* Expect to succeed */ + ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&func_cb_client, + &func_cb_server, NULL), TEST_SUCCESS); + + return EXPECT_RESULT(); +} +#else +static int test_wolfSSL_check_ip_param_basic(void) +{ + EXPECT_DECLS; + return EXPECT_RESULT(); +} +#endif + +#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(OPENSSL_COMPATIBLE_DEFAULTS) && !defined(NO_SHA256) && \ + defined(WOLFSSL_IP_ALT_NAME) +static const char* ipaddr_api = NULL; +static int test_wolfSSL_check_ip_address_basic_client_ssl(WOLFSSL* ssl) +{ + EXPECT_DECLS; + + ExpectIntEQ(wolfSSL_check_ip_address(ssl, ipaddr_api), WOLFSSL_SUCCESS); + + return EXPECT_RESULT(); +} + +/* Verify wolfSSL convenience API path: + * wolfSSL_check_ip_address() enables IP SAN matching during handshake, + * including the non-OPENSSL_EXTRA storage/verification flow. */ +static int test_wolfSSL_check_ip_address_basic(void) +{ + EXPECT_DECLS; + test_ssl_cbf func_cb_client; + test_ssl_cbf func_cb_server; + + XMEMSET(&func_cb_client, 0, sizeof(func_cb_client)); + XMEMSET(&func_cb_server, 0, sizeof(func_cb_server)); + + func_cb_client.ssl_ready = &test_wolfSSL_check_ip_address_basic_client_ssl; + + ipaddr_api = "127.0.0.2"; + /* Expect to fail: cert SAN IP is 127.0.0.1 */ + ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&func_cb_client, + &func_cb_server, NULL), -1001); + + ipaddr_api = "127.0.0.1"; + /* Expect to succeed */ + ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&func_cb_client, + &func_cb_server, NULL), TEST_SUCCESS); + + return EXPECT_RESULT(); +} +#else +static int test_wolfSSL_check_ip_address_basic(void) +{ + EXPECT_DECLS; + return EXPECT_RESULT(); +} +#endif + static int test_wolfSSL_BUF(void) { EXPECT_DECLS; @@ -33192,6 +33290,8 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_check_domain), TEST_DECL(test_wolfSSL_check_domain_basic), + TEST_DECL(test_wolfSSL_check_ip_param_basic), + TEST_DECL(test_wolfSSL_check_ip_address_basic), TEST_DECL(test_wolfSSL_cert_cb), TEST_DECL(test_wolfSSL_cert_cb_dyn_ciphers), TEST_DECL(test_wolfSSL_ciphersuite_auth), diff --git a/wolfssl/internal.h b/wolfssl/internal.h index aad1ee0aef..8794bf4193 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -4876,6 +4876,7 @@ typedef struct Buffers { ThreadCrypt encrypt[WOLFSSL_THREADED_CRYPT_CNT]; #endif buffer domainName; /* for client check */ + buffer ipasc; /* for client IP SAN check */ buffer clearOutputBuffer; buffer sig; /* signature data */ buffer digest; /* digest data */ diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 8d56767dc4..709470800b 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -3356,6 +3356,9 @@ WOLFSSL_API int wolfSSL_get_chain_cert_pem(WOLFSSL_X509_CHAIN* chain, int idx, /* call before SSL_connect, if verifying will add name check to date check and signature check */ WOLFSSL_ABI WOLFSSL_API int wolfSSL_check_domain_name(WOLFSSL* ssl, const char* dn); +/* call before SSL_connect, if verifying will add IP address check to + date check and signature check */ +WOLFSSL_ABI WOLFSSL_API int wolfSSL_check_ip_address(WOLFSSL* ssl, const char* ipaddr); /* need to call once to load library (session cache) */