mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-10-24 04:09:07 +03:00
- DecodedCert gains selfIssued for the issuer/subject name compare, and the name-compare readers use it. - ParseCertRelative() sets selfSigned for every self-issued certificate whose own key verifies its signature: it reuses the key that passed the issuer check, or calls the new ConfirmSelfSignature(), which verifies in its own context with no device. - CheckChainCAExtKeyUsage() exempts a CA with selfSigned set, in place of its own issuer key compare. - IsSelfSignedCert() in tls13.c is renamed IsSelfIssuedCert(). - ProcessPeerCerts() reports X509_V_ERR_INVALID_CA when a chain CA is refused with NOT_CA_ERROR. - Comments that called the name compare self-signed now say self-issued. - Tests cover a key rollover CA without keyCertSign in a TLS chain and as a trusted anchor in X509_verify_cert(), and the selfSigned and selfIssued flags from wc_ParseCert(). Issue: F-12625
5265 lines
208 KiB
C
5265 lines
208 KiB
C
/* test_ssl_cert.c
|
|
*
|
|
* Copyright (C) 2006-2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSL.
|
|
*
|
|
* wolfSSL is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSL is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
#include <tests/unit.h>
|
|
|
|
#ifdef NO_INLINE
|
|
#include <wolfssl/wolfcrypt/misc.h>
|
|
#else
|
|
#define WOLFSSL_MISC_INCLUDED
|
|
#include <wolfcrypt/src/misc.c>
|
|
#endif
|
|
|
|
#include <wolfssl/ssl.h>
|
|
#include <wolfssl/internal.h>
|
|
|
|
#include <tests/utils.h>
|
|
#include <tests/api/api.h>
|
|
#include <tests/api/test_ssl_cert.h>
|
|
|
|
#if defined(__unix__) && !defined(NO_FILESYSTEM)
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
/* Tests for the certificate APIs in src/ssl_api_cert.c (moved from ssl.c). */
|
|
|
|
/* Test reading back the verification mode from an object.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_get_verify_mode(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || defined(HAVE_STUNNEL) || \
|
|
defined(WOLFSSL_MYSQL_COMPATIBLE) || defined(WOLFSSL_NGINX)) && \
|
|
!defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
int mode;
|
|
|
|
ExpectIntEQ(wolfSSL_get_verify_mode(NULL), WOLFSSL_FAILURE);
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(wolfSSL_get_verify_mode(ssl), WOLFSSL_VERIFY_NONE);
|
|
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_PEER, NULL);
|
|
ExpectIntEQ(wolfSSL_get_verify_mode(ssl), WOLFSSL_VERIFY_PEER);
|
|
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(wolfSSL_get_verify_mode(ssl),
|
|
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT);
|
|
|
|
/* Exercise the fail-except-PSK option. */
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_FAIL_EXCEPT_PSK, NULL);
|
|
mode = wolfSSL_get_verify_mode(ssl);
|
|
ExpectIntEQ(mode & WOLFSSL_VERIFY_FAIL_EXCEPT_PSK,
|
|
WOLFSSL_VERIFY_FAIL_EXCEPT_PSK);
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test reading back the verification mode from a context.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_CTX_get_verify_mode(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || defined(HAVE_STUNNEL) || \
|
|
defined(WOLFSSL_MYSQL_COMPATIBLE) || defined(WOLFSSL_NGINX)) && \
|
|
!defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
int mode;
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_get_verify_mode(NULL), WOLFSSL_FAILURE);
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(wolfSSL_CTX_get_verify_mode(ctx), WOLFSSL_VERIFY_NONE);
|
|
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(wolfSSL_CTX_get_verify_mode(ctx),
|
|
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT);
|
|
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_FAIL_EXCEPT_PSK, NULL);
|
|
mode = wolfSSL_CTX_get_verify_mode(ctx);
|
|
ExpectIntEQ(mode & WOLFSSL_VERIFY_FAIL_EXCEPT_PSK,
|
|
WOLFSSL_VERIFY_FAIL_EXCEPT_PSK);
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH)
|
|
/* Exercise the post-handshake auth option. */
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_POST_HANDSHAKE, NULL);
|
|
mode = wolfSSL_CTX_get_verify_mode(ctx);
|
|
ExpectIntEQ(mode & WOLFSSL_VERIFY_POST_HANDSHAKE,
|
|
WOLFSSL_VERIFY_POST_HANDSHAKE);
|
|
#endif
|
|
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(OPENSSL_ALL) && !defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) \
|
|
&& !defined(NO_TLS)
|
|
static int test_cert_verify_cb(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
(void)store;
|
|
return preverify;
|
|
}
|
|
#endif
|
|
|
|
/* Test reading back the verification callback.
|
|
*
|
|
* The object inherits the context's callback until one is set on it.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_get_verify_callback(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(OPENSSL_ALL) && !defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) \
|
|
&& !defined(NO_TLS)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
/* CTX verify callback getter. */
|
|
ExpectNull(wolfSSL_CTX_get_verify_callback(NULL));
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectNull(wolfSSL_CTX_get_verify_callback(ctx));
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, test_cert_verify_cb);
|
|
ExpectTrue(wolfSSL_CTX_get_verify_callback(ctx) == test_cert_verify_cb);
|
|
|
|
/* SSL verify callback getter. */
|
|
ExpectNull(wolfSSL_get_verify_callback(NULL));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_PEER, test_cert_verify_cb);
|
|
ExpectTrue(wolfSSL_get_verify_callback(ssl) == test_cert_verify_cb);
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test getting the extra certificates loaded with the chain.
|
|
*
|
|
* The stack is only present once a chain file has been loaded.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_CTX_get_extra_chain_certs(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if (defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || \
|
|
defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLF_STACK_OF(WOLFSSL_X509)* sk = NULL;
|
|
|
|
/* NULL arguments fail. */
|
|
ExpectIntEQ(wolfSSL_CTX_get_extra_chain_certs(NULL, &sk), WOLFSSL_FAILURE);
|
|
|
|
/* No certificate chain loaded: succeeds with an empty (NULL) stack. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_server_method()));
|
|
sk = NULL;
|
|
ExpectIntEQ(wolfSSL_CTX_get_extra_chain_certs(ctx, &sk), WOLFSSL_SUCCESS);
|
|
ExpectNull(sk);
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_server_method()));
|
|
/* server-cert.pem holds a 2-cert chain, so the CA goes into certChain. */
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx, svrCertFile),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
/* Builds a stack of X509 from the stored chain. */
|
|
sk = NULL;
|
|
ExpectIntEQ(wolfSSL_CTX_get_extra_chain_certs(ctx, &sk), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(sk);
|
|
|
|
/* get0 returns the same (cached) chain. */
|
|
sk = NULL;
|
|
ExpectIntEQ(wolfSSL_CTX_get0_chain_certs(ctx, &sk), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_get0_chain_certs(NULL, &sk), WOLFSSL_FAILURE);
|
|
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
/* A longer chain (leaf + 2 certs) exercises appending past the first
|
|
* node, building a multi-element stack. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx,
|
|
"certs/intermediate/server-chain.pem"), WOLFSSL_SUCCESS);
|
|
sk = NULL;
|
|
ExpectIntEQ(wolfSSL_CTX_get_extra_chain_certs(ctx, &sk), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(sk);
|
|
ExpectIntGE(wolfSSL_sk_X509_num(sk), 2);
|
|
|
|
#if (defined(OPENSSL_ALL) || defined(WOLFSSL_ASIO) || \
|
|
defined(WOLFSSL_HAPROXY) || defined(WOLFSSL_NGINX) || \
|
|
defined(WOLFSSL_QT)) && !defined(NO_WOLFSSL_STUB)
|
|
/* Stub: returns via the control command. */
|
|
wolfSSL_CTX_clear_extra_chain_certs(ctx);
|
|
#endif
|
|
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test walking the peer's certificate chain by index.
|
|
*
|
|
* Covers the count, the per-certificate length and DER accessors, and the
|
|
* alternative chain used with alternative certificates.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_get_peer_chain(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(SESSION_CERTS) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_X509_CHAIN* chain = NULL;
|
|
#if (defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || \
|
|
defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && defined(KEEP_OUR_CERT)
|
|
WOLF_STACK_OF(WOLFSSL_X509)* osk = NULL;
|
|
#endif
|
|
|
|
/* NULL / not-yet-populated cases. */
|
|
ExpectNull(wolfSSL_get_peer_chain(NULL));
|
|
ExpectIntEQ(wolfSSL_get_chain_count(NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_chain_length(NULL, 0), 0);
|
|
ExpectNull(wolfSSL_get_chain_cert(NULL, 0));
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* The client now holds the server's certificate chain. */
|
|
ExpectNotNull(chain = wolfSSL_get_peer_chain(ssl_c));
|
|
ExpectIntGT(wolfSSL_get_chain_count(chain), 0);
|
|
ExpectIntGT(wolfSSL_get_chain_length(chain, 0), 0);
|
|
ExpectNotNull(wolfSSL_get_chain_cert(chain, 0));
|
|
|
|
#ifdef WOLFSSL_ALT_CERT_CHAINS
|
|
ExpectNull(wolfSSL_get_peer_alt_chain(NULL));
|
|
ExpectNotNull(wolfSSL_get_peer_alt_chain(ssl_c));
|
|
#endif
|
|
|
|
#if (defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || \
|
|
defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && defined(KEEP_OUR_CERT)
|
|
ExpectIntEQ(wolfSSL_get0_chain_certs(NULL, &osk), WOLFSSL_FAILURE);
|
|
ExpectIntEQ(wolfSSL_get0_chain_certs(ssl_c, &osk), WOLFSSL_SUCCESS);
|
|
#endif
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test getting a peer chain certificate as an X509 object.
|
|
*
|
|
* The object returned is owned by the caller and must be freed.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_get_chain_X509(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(SESSION_CERTS) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_X509_CHAIN* chain = NULL;
|
|
WOLFSSL_X509* x509 = NULL;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectNotNull(chain = wolfSSL_get_peer_chain(ssl_c));
|
|
|
|
/* A valid index returns a parseable certificate. */
|
|
ExpectNotNull(x509 = wolfSSL_get_chain_X509(chain, 0));
|
|
wolfSSL_X509_free(x509);
|
|
x509 = NULL;
|
|
/* NULL chain and an index past MAX_CHAIN_DEPTH return NULL up front. */
|
|
ExpectNull(wolfSSL_get_chain_X509(NULL, 0));
|
|
ExpectNull(wolfSSL_get_chain_X509(chain, MAX_CHAIN_DEPTH));
|
|
/* An index past the populated certs exercises the parse-failure path. */
|
|
ExpectNull(wolfSSL_get_chain_X509(chain, wolfSSL_get_chain_count(chain)));
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test converting a peer chain certificate to PEM.
|
|
*
|
|
* A NULL buffer reports the length needed rather than converting.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_get_chain_cert_pem(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(SESSION_CERTS) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && !defined(NO_TLS) && \
|
|
(defined(WOLFSSL_DER_TO_PEM) || defined(WOLFSSL_PEM_TO_DER))
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLFSSL_X509_CHAIN* chain = NULL;
|
|
byte pem[4096];
|
|
int pemSz = 0;
|
|
int needed = 0;
|
|
int chainLen = 0;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
ExpectNotNull(chain = wolfSSL_get_peer_chain(ssl_c));
|
|
|
|
/* Successful PEM conversion. */
|
|
pemSz = (int)sizeof(pem);
|
|
ExpectIntEQ(wolfSSL_get_chain_cert_pem(chain, 0, pem, (int)sizeof(pem),
|
|
&pemSz), WOLFSSL_SUCCESS);
|
|
ExpectIntGT(pemSz, 0);
|
|
|
|
/* Argument validation. */
|
|
pemSz = (int)sizeof(pem);
|
|
ExpectIntEQ(wolfSSL_get_chain_cert_pem(NULL, 0, pem, (int)sizeof(pem),
|
|
&pemSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_get_chain_cert_pem(chain, -1, pem, (int)sizeof(pem),
|
|
&pemSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_get_chain_cert_pem(chain, 99, pem, (int)sizeof(pem),
|
|
&pemSz), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_get_chain_cert_pem(chain, 0, pem, (int)sizeof(pem),
|
|
NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* NULL buffer returns the size needed (length-only query). */
|
|
needed = 0;
|
|
/* A negative buffer length is rejected. */
|
|
ExpectIntEQ(wolfSSL_get_chain_cert_pem(chain, 0, pem, -1, &pemSz),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_get_chain_cert_pem(chain, 0, NULL, 0, &needed),
|
|
WC_NO_ERR_TRACE(LENGTH_ONLY_E));
|
|
ExpectIntGT(needed, 0);
|
|
ExpectIntLE(needed, (int)sizeof(pem));
|
|
|
|
/* A buffer shorter than the DER certificate fails up front. */
|
|
pemSz = (int)sizeof(pem);
|
|
ExpectIntEQ(wolfSSL_get_chain_cert_pem(chain, 0, pem, 1, &pemSz),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* One byte short of the full size leaves no room for the footer. */
|
|
pemSz = (int)sizeof(pem);
|
|
ExpectIntEQ(wolfSSL_get_chain_cert_pem(chain, 0, pem, needed - 1, &pemSz),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* Room for the DER length but not the base64-expanded body: the encoder
|
|
* reports an error (negative return). */
|
|
chainLen = wolfSSL_get_chain_length(chain, 0);
|
|
pemSz = (int)sizeof(pem);
|
|
ExpectIntLT(wolfSSL_get_chain_cert_pem(chain, 0, pem, chainLen + 100,
|
|
&pemSz), 0);
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test comparing the peer's certificate against one in a file.
|
|
*
|
|
* The file is parsed as PEM, so it must be the PEM form of the same certificate
|
|
* to match.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_cmp_peer_cert_to_file(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \
|
|
defined(KEEP_PEER_CERT) && defined(HAVE_EX_DATA) && \
|
|
!defined(NO_FILESYSTEM) && !defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) \
|
|
&& !defined(NO_TLS) && defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
/* Should fail gracefully before handshake. */
|
|
ExpectIntEQ(wolfSSL_cmp_peer_cert_to_file(ssl_c, svrCertFile),
|
|
WOLFSSL_FATAL_ERROR);
|
|
|
|
/* do the handshake */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* NULL arguments report failure. */
|
|
ExpectIntEQ(wolfSSL_cmp_peer_cert_to_file(NULL, svrCertFile),
|
|
WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_cmp_peer_cert_to_file(ssl_c, NULL),
|
|
WOLFSSL_FATAL_ERROR);
|
|
|
|
/* The peer (server) certificate matches the file it was loaded from. */
|
|
ExpectIntEQ(wolfSSL_cmp_peer_cert_to_file(ssl_c, svrCertFile), 0);
|
|
/* A different certificate does not match. */
|
|
ExpectIntEQ(wolfSSL_cmp_peer_cert_to_file(ssl_c, caCertFile),
|
|
WOLFSSL_FATAL_ERROR);
|
|
/* A missing file reports a file error. */
|
|
ExpectIntEQ(wolfSSL_cmp_peer_cert_to_file(ssl_c,
|
|
"certs/does-not-exist.pem"), WC_NO_ERR_TRACE(WOLFSSL_BAD_FILE));
|
|
/* A readable file that is not PEM-encoded fails conversion. */
|
|
ExpectIntEQ(wolfSSL_cmp_peer_cert_to_file(ssl_c, cliCertDerFile),
|
|
WOLFSSL_FATAL_ERROR);
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Guarded to match its only caller, test_wolfSSL_CTX_set_client_cert_cb(),
|
|
* which needs OPENSSL_EXTRA for the ctx->CBClientCert field. */
|
|
#if defined(WOLFSSL_CERT_SETUP_CB) && defined(OPENSSL_EXTRA) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
/* Client certificate callback that supplies nothing.
|
|
*
|
|
* @param [in] ssl SSL/TLS object. Unused.
|
|
* @param [out] x509 Certificate to use. Unused.
|
|
* @param [out] pkey Private key to use. Unused.
|
|
* @return 0 to indicate no certificate was supplied.
|
|
*/
|
|
static int test_ssl_cert_client_cert_cb(WOLFSSL* ssl, WOLFSSL_X509** x509,
|
|
WOLFSSL_EVP_PKEY** pkey)
|
|
{
|
|
(void)ssl;
|
|
(void)x509;
|
|
(void)pkey;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* Test setting the client certificate callback on a context.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_CTX_set_client_cert_cb(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* Reads ctx->CBClientCert, which the structure only has under
|
|
* OPENSSL_EXTRA, so this is narrower than the setter's own guard. */
|
|
#if defined(WOLFSSL_CERT_SETUP_CB) && defined(OPENSSL_EXTRA) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
|
|
/* A NULL context is ignored rather than faulting. */
|
|
wolfSSL_CTX_set_client_cert_cb(NULL, test_ssl_cert_client_cert_cb);
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
if (ctx != NULL) {
|
|
wolfSSL_CTX_set_client_cert_cb(ctx, test_ssl_cert_client_cert_cb);
|
|
ExpectTrue(ctx->CBClientCert == test_ssl_cert_client_cert_cb);
|
|
|
|
/* The callback can be cleared again. */
|
|
wolfSSL_CTX_set_client_cert_cb(ctx, NULL);
|
|
ExpectNull(ctx->CBClientCert);
|
|
}
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Guarded to cover both callers: test_wolfSSL_CTX_set_cert_cb() needs a
|
|
* server, and test_wolfSSL_cert_setup_cb_ret() needs a memio handshake. The
|
|
* counters move with the callback so they cannot go unused either. */
|
|
#if defined(WOLFSSL_CERT_SETUP_CB) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12) && (!defined(NO_WOLFSSL_SERVER) || \
|
|
(defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA)))
|
|
static int test_ssl_cert_setup_ret = 1;
|
|
static int test_ssl_cert_setup_calls = 0;
|
|
|
|
/* Certificate setup callback returning a value chosen by the test.
|
|
*
|
|
* @param [in] ssl SSL/TLS object. Unused.
|
|
* @param [in] arg Context passed when the callback was set. Unused.
|
|
* @return The value in test_ssl_cert_setup_ret.
|
|
*/
|
|
static int test_ssl_cert_setup_cb(WOLFSSL* ssl, void* arg)
|
|
{
|
|
(void)ssl;
|
|
(void)arg;
|
|
test_ssl_cert_setup_calls++;
|
|
return test_ssl_cert_setup_ret;
|
|
}
|
|
#endif
|
|
|
|
/* Test setting the certificate setup callback on a context.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_CTX_set_cert_cb(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_CERT_SETUP_CB) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
int arg = 0;
|
|
|
|
/* A NULL context is ignored rather than faulting. */
|
|
wolfSSL_CTX_set_cert_cb(NULL, test_ssl_cert_setup_cb, &arg);
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
if (ctx != NULL) {
|
|
wolfSSL_CTX_set_cert_cb(ctx, test_ssl_cert_setup_cb, &arg);
|
|
ExpectTrue(ctx->certSetupCb == test_ssl_cert_setup_cb);
|
|
ExpectPtrEq(ctx->certSetupCbArg, &arg);
|
|
|
|
/* Both the callback and its context can be cleared. */
|
|
wolfSSL_CTX_set_cert_cb(ctx, NULL, NULL);
|
|
ExpectNull(ctx->certSetupCb);
|
|
ExpectNull(ctx->certSetupCbArg);
|
|
}
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test how the return value of the certificate setup callback is handled.
|
|
*
|
|
* The callback is called on the server while the ClientHello is processed, so
|
|
* each return value is observed as the outcome of the handshake.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_cert_setup_cb_ret(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_CERT_SETUP_CB) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && !defined(NO_TLS)
|
|
/* cbRet is what the callback returns. err is the error the server then
|
|
* reports: 0 means the handshake is expected to complete, and -1 means
|
|
* only that it must fail.
|
|
*
|
|
* A negative callback return makes the wrapper report
|
|
* WOLFSSL_ERROR_WANT_X509_LOOKUP, which is a positive value and so does
|
|
* not reach wolfSSL_get_error(). Only the failure is checked for that
|
|
* case rather than the code that happens to surface. */
|
|
static const struct {
|
|
int cbRet;
|
|
int err;
|
|
} cases[] = {
|
|
{ 1, 0 },
|
|
{ 0, WC_NO_ERR_TRACE(CLIENT_CERT_CB_ERROR) },
|
|
{ -1, -1 },
|
|
{ 2, WC_NO_ERR_TRACE(CLIENT_CERT_CB_ERROR) }
|
|
};
|
|
int i;
|
|
|
|
for (i = 0; i < (int)(sizeof(cases) / sizeof(cases[0])); i++) {
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
test_ssl_cert_setup_ret = cases[i].cbRet;
|
|
test_ssl_cert_setup_calls = 0;
|
|
wolfSSL_CTX_set_cert_cb(ctx_s, test_ssl_cert_setup_cb, NULL);
|
|
|
|
if (cases[i].err == 0) {
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
}
|
|
else {
|
|
/* Drive one step at a time so the error the server reports is the
|
|
* one the callback caused, not a later I/O failure. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, 0), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
|
if (cases[i].err != -1) {
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, 0), cases[i].err);
|
|
}
|
|
else {
|
|
ExpectIntNE(wolfSSL_get_error(ssl_s, 0), 0);
|
|
}
|
|
}
|
|
/* The callback ran regardless of what it reported. */
|
|
ExpectIntGT(test_ssl_cert_setup_calls, 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
}
|
|
test_ssl_cert_setup_ret = 1;
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test getting the stack of the peer's certificates.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_get_peer_cert_chain(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(SESSION_CERTS) && \
|
|
defined(OPENSSL_EXTRA) && !defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) \
|
|
&& !defined(NO_TLS)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLF_STACK_OF(WOLFSSL_X509)* sk = NULL;
|
|
|
|
ExpectNull(wolfSSL_get_peer_cert_chain(NULL));
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
/* Nothing has been received yet, so there is no chain to build. */
|
|
ExpectNull(wolfSSL_get_peer_cert_chain(ssl_c));
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* The client now holds the server's chain. */
|
|
ExpectNotNull(sk = wolfSSL_get_peer_cert_chain(ssl_c));
|
|
ExpectIntGT(wolfSSL_sk_X509_num(sk), 0);
|
|
/* The stack is owned by the object, so asking again returns the same one
|
|
* rather than building another. */
|
|
ExpectPtrEq(wolfSSL_get_peer_cert_chain(ssl_c), sk);
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test building the stack of the peer's certificates.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_set_peer_cert_chain(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(SESSION_CERTS) && \
|
|
defined(OPENSSL_EXTRA) && !defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) \
|
|
&& !defined(NO_TLS) && !defined(NO_FILESYSTEM) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLF_STACK_OF(WOLFSSL_X509)* sk = NULL;
|
|
|
|
ExpectNull(wolfSSL_set_peer_cert_chain(NULL));
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
/* An empty session chain has nothing to build from. */
|
|
ExpectNull(wolfSSL_set_peer_cert_chain(ssl_c));
|
|
|
|
/* Ask for a client certificate so the server also ends up with a chain.
|
|
* The credentials go on the object because test_memio_setup() has already
|
|
* created it from the context. WOLFSSL_NO_CLIENT_AUTH compiles out the
|
|
* client's Certificate message, so there is nothing to ask for. */
|
|
#ifndef WOLFSSL_NO_CLIENT_AUTH
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
#endif
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* Client side: the chain is stored on the object. */
|
|
ExpectNotNull(sk = wolfSSL_set_peer_cert_chain(ssl_c));
|
|
if (ssl_c != NULL) {
|
|
ExpectPtrEq(ssl_c->peerCertChain, sk);
|
|
}
|
|
/* Called again the old chain is released and a new one stored. */
|
|
ExpectNotNull(sk = wolfSSL_set_peer_cert_chain(ssl_c));
|
|
if (ssl_c != NULL) {
|
|
ExpectPtrEq(ssl_c->peerCertChain, sk);
|
|
}
|
|
|
|
#ifndef WOLFSSL_NO_CLIENT_AUTH
|
|
/* Server side: the leaf is moved out of the stack into the session. */
|
|
ExpectNotNull(wolfSSL_set_peer_cert_chain(ssl_s));
|
|
if (ssl_s != NULL) {
|
|
ExpectNotNull(ssl_s->session->peer);
|
|
}
|
|
/* Building it again releases the peer stored by the previous call. */
|
|
ExpectNotNull(wolfSSL_set_peer_cert_chain(ssl_s));
|
|
if (ssl_s != NULL) {
|
|
ExpectNotNull(ssl_s->session->peer);
|
|
}
|
|
#else
|
|
/* With no client certificate the server has no chain to build. */
|
|
ExpectNull(wolfSSL_set_peer_cert_chain(ssl_s));
|
|
#endif
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test getting the verified certificate chain.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_get0_verified_chain(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(SESSION_CERTS) && \
|
|
defined(OPENSSL_EXTRA) && defined(KEEP_PEER_CERT) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && !defined(NO_TLS)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLF_STACK_OF(WOLFSSL_X509)* chain = NULL;
|
|
|
|
ExpectNull(wolfSSL_get0_verified_chain(NULL));
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
/* Without a peer certificate there is nothing to verify. */
|
|
ExpectNull(wolfSSL_get0_verified_chain(ssl_c));
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* The server's chain verifies against the CA the client loaded. */
|
|
ExpectNotNull(chain = wolfSSL_get0_verified_chain(ssl_c));
|
|
ExpectIntGT(wolfSSL_sk_X509_num(chain), 0);
|
|
if (ssl_c != NULL) {
|
|
ExpectPtrEq(ssl_c->verifiedChain, chain);
|
|
}
|
|
/* Called again the previous chain is released and a new one stored. */
|
|
ExpectNotNull(chain = wolfSSL_get0_verified_chain(ssl_c));
|
|
if (ssl_c != NULL) {
|
|
ExpectPtrEq(ssl_c->verifiedChain, chain);
|
|
}
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test adding certificate subject names to the CA name lists.
|
|
*
|
|
* Covers the context and object variants of both the client-CA list and the
|
|
* general CA list, and the shared helper that appends to a list.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_CA_list_add(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* The CA-list setters/getters exercised here are the OpenSSL compat layer. */
|
|
#if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CA_NAMES) && \
|
|
defined(OPENSSL_EXTRA) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_RSA) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_SERVER) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
WOLFSSL_X509* x509 = NULL;
|
|
|
|
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(caCertFile,
|
|
WOLFSSL_FILETYPE_PEM));
|
|
|
|
/* Both arguments are required. */
|
|
ExpectIntEQ(wolfSSL_CTX_add_client_CA(NULL, x509), 0);
|
|
ExpectIntEQ(wolfSSL_add_client_CA(NULL, x509), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_add1_to_CA_list(NULL, x509), 0);
|
|
ExpectIntEQ(wolfSSL_add1_to_CA_list(NULL, x509), 0);
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_add_client_CA(ctx, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_add_client_CA(ssl, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_add1_to_CA_list(ctx, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_add1_to_CA_list(ssl, NULL), 0);
|
|
|
|
/* The first call creates the list, the second appends to it. The object's
|
|
* lists are filled first as, while empty, they resolve to the context's. */
|
|
ExpectNull(wolfSSL_get_client_CA_list(ssl));
|
|
ExpectIntEQ(wolfSSL_add_client_CA(ssl, x509), 1);
|
|
ExpectIntEQ(wolfSSL_sk_X509_NAME_num(wolfSSL_get_client_CA_list(ssl)), 1);
|
|
ExpectIntEQ(wolfSSL_add_client_CA(ssl, x509), 1);
|
|
ExpectIntEQ(wolfSSL_sk_X509_NAME_num(wolfSSL_get_client_CA_list(ssl)), 2);
|
|
|
|
ExpectNull(wolfSSL_CTX_get_client_CA_list(ctx));
|
|
ExpectIntEQ(wolfSSL_CTX_add_client_CA(ctx, x509), 1);
|
|
ExpectIntEQ(wolfSSL_sk_X509_NAME_num(
|
|
wolfSSL_CTX_get_client_CA_list(ctx)), 1);
|
|
ExpectIntEQ(wolfSSL_CTX_add_client_CA(ctx, x509), 1);
|
|
ExpectIntEQ(wolfSSL_sk_X509_NAME_num(
|
|
wolfSSL_CTX_get_client_CA_list(ctx)), 2);
|
|
|
|
ExpectNull(wolfSSL_get0_CA_list(ssl));
|
|
ExpectIntEQ(wolfSSL_add1_to_CA_list(ssl, x509), 1);
|
|
ExpectIntEQ(wolfSSL_sk_X509_NAME_num(wolfSSL_get0_CA_list(ssl)), 1);
|
|
ExpectIntEQ(wolfSSL_add1_to_CA_list(ssl, x509), 1);
|
|
ExpectIntEQ(wolfSSL_sk_X509_NAME_num(wolfSSL_get0_CA_list(ssl)), 2);
|
|
|
|
ExpectNull(wolfSSL_CTX_get0_CA_list(ctx));
|
|
ExpectIntEQ(wolfSSL_CTX_add1_to_CA_list(ctx, x509), 1);
|
|
ExpectIntEQ(wolfSSL_sk_X509_NAME_num(wolfSSL_CTX_get0_CA_list(ctx)), 1);
|
|
ExpectIntEQ(wolfSSL_CTX_add1_to_CA_list(ctx, x509), 1);
|
|
ExpectIntEQ(wolfSSL_sk_X509_NAME_num(wolfSSL_CTX_get0_CA_list(ctx)), 2);
|
|
|
|
wolfSSL_X509_free(x509);
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test retrieving the CA name lists.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_CA_list_get(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* The CA-list setters/getters exercised here are the OpenSSL compat layer. */
|
|
#if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CA_NAMES) && \
|
|
defined(OPENSSL_EXTRA) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_RSA) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
WOLFSSL_X509* x509 = NULL;
|
|
|
|
/* A NULL object has no list. */
|
|
ExpectNull(wolfSSL_CTX_get_client_CA_list(NULL));
|
|
ExpectNull(wolfSSL_get_client_CA_list(NULL));
|
|
ExpectNull(wolfSSL_CTX_get0_CA_list(NULL));
|
|
ExpectNull(wolfSSL_get0_CA_list(NULL));
|
|
ExpectNull(wolfSSL_get0_peer_CA_list(NULL));
|
|
|
|
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(caCertFile,
|
|
WOLFSSL_FILETYPE_PEM));
|
|
|
|
/* Server side: the client CA names are the object's own list. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* Nothing added yet. */
|
|
ExpectNull(wolfSSL_CTX_get0_CA_list(ctx));
|
|
ExpectNull(wolfSSL_get0_CA_list(ssl));
|
|
/* No hello has been received, so there are no peer names. */
|
|
ExpectNull(wolfSSL_get0_peer_CA_list(ssl));
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_add_client_CA(ctx, x509), 1);
|
|
ExpectIntEQ(wolfSSL_CTX_add1_to_CA_list(ctx, x509), 1);
|
|
ExpectIntEQ(wolfSSL_add_client_CA(ssl, x509), 1);
|
|
ExpectIntEQ(wolfSSL_add1_to_CA_list(ssl, x509), 1);
|
|
|
|
ExpectNotNull(wolfSSL_CTX_get_client_CA_list(ctx));
|
|
ExpectNotNull(wolfSSL_get_client_CA_list(ssl));
|
|
ExpectNotNull(wolfSSL_CTX_get0_CA_list(ctx));
|
|
ExpectNotNull(wolfSSL_get0_CA_list(ssl));
|
|
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
/* Client side: the client CA names come from the peer instead. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectNull(wolfSSL_get_client_CA_list(ssl));
|
|
|
|
wolfSSL_X509_free(x509);
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test reading a list of CA names from a file.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_load_client_CA_file(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CA_NAMES) && \
|
|
!defined(NO_BIO) && defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && \
|
|
!defined(NO_RSA) && defined(WOLFSSL_PEM_TO_DER)
|
|
WOLF_STACK_OF(WOLFSSL_X509_NAME)* names = NULL;
|
|
|
|
/* A file that cannot be opened reports no names. */
|
|
ExpectNull(names = wolfSSL_load_client_CA_file("does/not/exist.pem"));
|
|
wolfSSL_sk_X509_NAME_pop_free(names, NULL);
|
|
names = NULL;
|
|
|
|
/* Every certificate in the file contributes its subject name. */
|
|
ExpectNotNull(names = wolfSSL_load_client_CA_file(caCertFile));
|
|
ExpectIntGT(wolfSSL_sk_X509_NAME_num(names), 0);
|
|
wolfSSL_sk_X509_NAME_pop_free(names, NULL);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test requiring mutual authentication.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_mutual_auth(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_RSA) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_mutual_auth(NULL, 1),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_mutual_auth(NULL, 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* Mutual authentication is a server-only setting. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(wolfSSL_CTX_mutual_auth(ctx, 1), WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
ExpectIntEQ(wolfSSL_mutual_auth(ssl, 1), WC_NO_ERR_TRACE(SIDE_ERROR));
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_mutual_auth(ctx, 1), 0);
|
|
ExpectIntEQ(wolfSSL_mutual_auth(ssl, 1), 0);
|
|
/* The setting can be turned back off. */
|
|
ExpectIntEQ(wolfSSL_CTX_mutual_auth(ctx, 0), 0);
|
|
ExpectIntEQ(wolfSSL_mutual_auth(ssl, 0), 0);
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test enabling post-handshake authentication.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_post_handshake_auth(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && defined(OPENSSL_EXTRA) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_POST_HANDSHAKE_AUTH) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_RSA) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
/* A TLS 1.3 client may ask to be authenticated after the handshake. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(wolfSSL_CTX_set_post_handshake_auth(ctx, 1), 1);
|
|
ExpectIntEQ(wolfSSL_set_post_handshake_auth(ssl, 1), 1);
|
|
/* And can turn it back off. */
|
|
ExpectIntEQ(wolfSSL_CTX_set_post_handshake_auth(ctx, 0), 1);
|
|
ExpectIntEQ(wolfSSL_set_post_handshake_auth(ssl, 0), 1);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
wolfSSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
|
|
/* A server cannot request it of itself. */
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(wolfSSL_CTX_set_post_handshake_auth(ctx, 1), 0);
|
|
ExpectIntEQ(wolfSSL_set_post_handshake_auth(ssl, 1), 0);
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test setting the certificate store used for verification.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_verify_cert_store(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && defined(OPENSSL_ALL) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_CLIENT)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
WOLFSSL_X509_STORE* store = NULL;
|
|
WOLFSSL_X509_STORE* store2 = NULL;
|
|
WOLFSSL_X509_STORE* store3 = NULL;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* The object being set is required. */
|
|
ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(NULL, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_set0_verify_cert_store(NULL, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(NULL, NULL), 0);
|
|
|
|
/* On an object a NULL store clears, so clearing when none is set
|
|
* succeeds and does nothing. The context form refuses it - see the note
|
|
* on wolfSSL_CTX_set1_verify_cert_store(). */
|
|
ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_set0_verify_cert_store(ssl, NULL), 1);
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, NULL), 1);
|
|
|
|
/* The store a context owns is not reference counted - its lifetime is
|
|
* the context's. Taking a reference on it succeeds without touching a
|
|
* count that was never initialized, and releasing it does nothing. */
|
|
ExpectIntEQ(wolfSSL_X509_STORE_up_ref(wolfSSL_CTX_get_cert_store(ctx)), 1);
|
|
wolfSSL_X509_STORE_free(wolfSSL_CTX_get_cert_store(ctx));
|
|
ExpectNotNull(wolfSSL_CTX_get_cert_store(ctx));
|
|
/* There is no store to take a reference on. */
|
|
ExpectIntEQ(wolfSSL_X509_STORE_up_ref(NULL), 0);
|
|
|
|
/* Setting the store already in use is accepted and changes nothing, both
|
|
* for the context and for an object handed the store the context owns. */
|
|
ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx,
|
|
wolfSSL_CTX_get_cert_store(ctx)), 1);
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl,
|
|
wolfSSL_CTX_get_cert_store(ctx)), 1);
|
|
|
|
/* A different store is taken with a reference. */
|
|
ExpectNotNull(store = wolfSSL_X509_STORE_new());
|
|
ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, store), 1);
|
|
|
|
/* Give the object a store of its own, then hand it the context's store:
|
|
* it drops its own and goes back to using the context's. */
|
|
ExpectNotNull(store2 = wolfSSL_X509_STORE_new());
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, store2), 1);
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, store), 1);
|
|
|
|
/* set0 hands a reference over and consumes it by keeping it, so the
|
|
* caller must own one for every call that gives the object a store of
|
|
* its own. */
|
|
ExpectNotNull(store3 = wolfSSL_X509_STORE_new());
|
|
ExpectIntEQ(wolfSSL_set0_verify_cert_store(ssl, store3), 1);
|
|
|
|
/* Setting the store the object already holds changes no reference at all,
|
|
* either way round. Consuming one would destroy the store when the caller
|
|
* handed over a pointer it did not own, and a dangling x509_store_pt is
|
|
* worse than the reference this leaks instead. */
|
|
ExpectIntEQ(wolfSSL_set0_verify_cert_store(ssl, store3), 1);
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, store3), 1);
|
|
/* Still alive and still the object's, so a reference can be taken. */
|
|
ExpectIntEQ(wolfSSL_X509_STORE_up_ref(store3), 1);
|
|
wolfSSL_X509_STORE_free(store3);
|
|
|
|
/* Handing over the context's store drops the object's own and reverts it
|
|
* to the context's. No reference is consumed: the context owns that store
|
|
* and releasing it here would leave ctx->x509_store_pt pointing at memory
|
|
* freed a second time by wolfSSL_CTX_free(). */
|
|
ExpectIntEQ(wolfSSL_set0_verify_cert_store(ssl,
|
|
wolfSSL_CTX_get_cert_store(ctx)), 1);
|
|
ExpectPtrEq(wolfSSL_CTX_get_cert_store(ctx), store);
|
|
/* Still alive and still the context's, so a reference can be taken. */
|
|
ExpectIntEQ(wolfSSL_X509_STORE_up_ref(store), 1);
|
|
wolfSSL_X509_STORE_free(store);
|
|
|
|
/* Repeating it with the object already on the context's store takes the
|
|
* early exit instead, and must leave the references alone the same way.
|
|
* This is the shape that freed the context's store: an object with no
|
|
* store of its own, handed the context's with set0. */
|
|
ExpectIntEQ(wolfSSL_set0_verify_cert_store(ssl,
|
|
wolfSSL_CTX_get_cert_store(ctx)), 1);
|
|
ExpectPtrEq(wolfSSL_CTX_get_cert_store(ctx), store);
|
|
ExpectIntEQ(wolfSSL_X509_STORE_up_ref(store), 1);
|
|
wolfSSL_X509_STORE_free(store);
|
|
|
|
/* The object is back on the context's store, so setting that same store
|
|
* with set1 takes no reference and releases none. */
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, store), 1);
|
|
ExpectPtrEq(wolfSSL_CTX_get_cert_store(ctx), store);
|
|
|
|
/* Give the object a store of its own again and clear it: the object
|
|
* releases its reference and reverts to the context's store. The store
|
|
* itself is still alive, so a reference can still be taken on it. */
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, store2), 1);
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, NULL), 1);
|
|
ExpectIntEQ(wolfSSL_X509_STORE_up_ref(store2), 1);
|
|
wolfSSL_X509_STORE_free(store2);
|
|
|
|
/* set0 clears the same way - no reference is handed over with a NULL
|
|
* store, so there is none to consume. */
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, store2), 1);
|
|
ExpectIntEQ(wolfSSL_set0_verify_cert_store(ssl, NULL), 1);
|
|
|
|
/* A NULL store is refused and changes nothing: the one field here holds
|
|
* what OpenSSL keeps as two, so releasing it would throw away the store
|
|
* given to wolfSSL_CTX_set_cert_store(). Refusing keeps the unsupported
|
|
* request visible to the caller. */
|
|
ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, NULL), 0);
|
|
ExpectPtrEq(wolfSSL_CTX_get_cert_store(ctx), store);
|
|
|
|
/* Release the references this test created. */
|
|
wolfSSL_X509_STORE_free(store);
|
|
wolfSSL_X509_STORE_free(store2);
|
|
|
|
/* A NULL context has no store. */
|
|
ExpectNull(wolfSSL_CTX_get_cert_store(NULL));
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test which store an object uses for verification.
|
|
*
|
|
* An object with no store of its own uses the context's, and keeps no pointer
|
|
* to it, so it follows the context when the context's store changes. A store
|
|
* set on the object takes precedence until cleared.
|
|
*
|
|
* Each store has its own certificate manager, and only one of them is given
|
|
* the CA that signed the CRL. Loading a CRL through the object goes to the
|
|
* manager of the store the object resolves to, so whether the load succeeds
|
|
* says which store that is.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_verify_cert_store_follows_ctx(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* wolfSSL_X509_STORE_load_locations(), used below, is compiled only when the
|
|
* directory API is available - see the guard in src/x509_str.c. */
|
|
#if !defined(NO_CERTS) && defined(OPENSSL_ALL) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_RSA) && defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \
|
|
!defined(NO_WOLFSSL_DIR) && defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
WOLFSSL_X509_STORE* noCa = NULL;
|
|
WOLFSSL_X509_STORE* noCa2 = NULL;
|
|
WOLFSSL_X509_STORE* withCa = NULL;
|
|
WOLFSSL_X509_STORE* withCa2 = NULL;
|
|
/* Two CRLs from the same CA. A manager keeps the CRLs tried against it,
|
|
* whether or not they verified, so no manager below is asked for the same
|
|
* CRL twice - a second attempt would find the cached one and succeed. */
|
|
const char* crlPem = "./certs/crl/crl.pem";
|
|
const char* crlPem2 = "./certs/crl/crl.revoked";
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
ExpectNotNull(noCa = wolfSSL_X509_STORE_new());
|
|
ExpectNotNull(noCa2 = wolfSSL_X509_STORE_new());
|
|
ExpectNotNull(withCa = wolfSSL_X509_STORE_new());
|
|
ExpectNotNull(withCa2 = wolfSSL_X509_STORE_new());
|
|
ExpectIntEQ(wolfSSL_X509_STORE_load_locations(withCa, caCertFile, NULL), 1);
|
|
ExpectIntEQ(wolfSSL_X509_STORE_load_locations(withCa2, caCertFile, NULL),
|
|
1);
|
|
|
|
/* Hand the object the store the context is using while that is the store
|
|
* the context owns: no pointer to it is kept either. */
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl,
|
|
wolfSSL_CTX_get_cert_store(ctx)), 1);
|
|
/* So the object follows the context onto a store that has the CA. Were
|
|
* the context's own store pinned to the object instead, the manager in
|
|
* use would still be the context's, which has no CA. */
|
|
ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, withCa), 1);
|
|
ExpectIntEQ(wolfSSL_LoadCRLFile(ssl, crlPem, WOLFSSL_FILETYPE_PEM), 1);
|
|
|
|
/* Same again for a store the context was given rather than owns. */
|
|
ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, noCa), 1);
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, noCa), 1);
|
|
/* That store has no CA to verify the CRL against. */
|
|
ExpectIntNE(wolfSSL_LoadCRLFile(ssl, crlPem2, WOLFSSL_FILETYPE_PEM), 1);
|
|
/* Changing the context's store changes the one the object uses. Were the
|
|
* store above pinned to the object instead, it would still be in use. */
|
|
ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, withCa2), 1);
|
|
ExpectIntEQ(wolfSSL_LoadCRLFile(ssl, crlPem, WOLFSSL_FILETYPE_PEM), 1);
|
|
|
|
/* A store set on the object is used ahead of the context's. */
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, noCa2), 1);
|
|
ExpectIntNE(wolfSSL_LoadCRLFile(ssl, crlPem, WOLFSSL_FILETYPE_PEM), 1);
|
|
|
|
/* Clearing it puts the object back on the context's store. */
|
|
ExpectIntEQ(wolfSSL_set1_verify_cert_store(ssl, NULL), 1);
|
|
ExpectIntEQ(wolfSSL_LoadCRLFile(ssl, crlPem2, WOLFSSL_FILETYPE_PEM), 1);
|
|
|
|
/* Release the references this test created. */
|
|
wolfSSL_X509_STORE_free(noCa);
|
|
wolfSSL_X509_STORE_free(noCa2);
|
|
wolfSSL_X509_STORE_free(withCa);
|
|
wolfSSL_X509_STORE_free(withCa2);
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that releasing a store the certificate manager is paired with does not
|
|
* leave the manager pointing at it.
|
|
*
|
|
* wolfSSL_CTX_set_cert_store() takes the store handed to it and has the
|
|
* manager keep a pointer back to it. Releasing that store - by clearing or by
|
|
* setting another - must not leave that pointer behind: it is used without a
|
|
* further check when looking up a certificate by issuer (see src/crl.c).
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_CTX_cert_store_manager_link(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && defined(OPENSSL_ALL) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_CLIENT)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL_X509_STORE* store = NULL;
|
|
WOLFSSL_X509_STORE* store3 = NULL;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
|
|
/* The context starts out paired with the store it owns. */
|
|
if (ctx != NULL) {
|
|
ExpectNotNull(ctx->cm);
|
|
ExpectPtrEq(ctx->cm->x509_store_p, &ctx->x509_store);
|
|
}
|
|
|
|
/* Handing a store over pairs the manager with that store instead. The
|
|
* context takes the caller's reference rather than adding one. */
|
|
ExpectNotNull(store = wolfSSL_X509_STORE_new());
|
|
wolfSSL_CTX_set_cert_store(ctx, store);
|
|
if (ctx != NULL) {
|
|
ExpectPtrEq(wolfSSL_CTX_get_cert_store(ctx), store);
|
|
ExpectPtrEq(ctx->cm->x509_store_p, store);
|
|
}
|
|
|
|
/* A NULL store is refused and changes nothing, so the pairing is
|
|
* untouched. */
|
|
ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, NULL), 0);
|
|
if (ctx != NULL) {
|
|
ExpectPtrEq(ctx->cm->x509_store_p, store);
|
|
}
|
|
|
|
/* Replacing the paired store does release it, and that is where the
|
|
* manager would be left pointing at freed memory. */
|
|
ExpectNotNull(store3 = wolfSSL_X509_STORE_new());
|
|
ExpectIntEQ(wolfSSL_CTX_set1_verify_cert_store(ctx, store3), 1);
|
|
if (ctx != NULL) {
|
|
/* store has been released - the manager keeps its own store, which
|
|
* is the one it shares a manager with. */
|
|
ExpectPtrEq(ctx->cm->x509_store_p, &ctx->x509_store);
|
|
}
|
|
|
|
/* store was released above. store3 is referenced by both this test and
|
|
* the context. */
|
|
wolfSSL_X509_STORE_free(store3);
|
|
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Guarded to match its only caller, test_wolfSSL_cert_cb_ctx(). */
|
|
#if !defined(NO_CERTS) && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \
|
|
!defined(NO_WOLFSSL_CLIENT)
|
|
/* CA cache addition callback that does nothing.
|
|
*
|
|
* @param [in] der DER encoded certificate. Unused.
|
|
* @param [in] sz Length of the certificate. Unused.
|
|
* @param [in] type Type of the certificate. Unused.
|
|
*/
|
|
static void test_ssl_cert_ca_cache_cb(unsigned char* der, int sz, int type)
|
|
{
|
|
(void)der;
|
|
(void)sz;
|
|
(void)type;
|
|
}
|
|
#endif
|
|
|
|
/* Test storing the user contexts and callbacks used during verification.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_cert_cb_ctx(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \
|
|
!defined(NO_WOLFSSL_CLIENT)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
int userCtx = 0;
|
|
|
|
/* NULL objects are ignored rather than faulting. */
|
|
wolfSSL_CTX_SetCertCbCtx(NULL, &userCtx);
|
|
wolfSSL_SetCertCbCtx(NULL, &userCtx);
|
|
wolfSSL_CTX_SetCACb(NULL, test_ssl_cert_ca_cache_cb);
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
wolfSSL_CTX_SetCertCbCtx(ctx, &userCtx);
|
|
wolfSSL_SetCertCbCtx(ssl, &userCtx);
|
|
if (ctx != NULL) {
|
|
ExpectPtrEq(ctx->verifyCbCtx, &userCtx);
|
|
}
|
|
if (ssl != NULL) {
|
|
ExpectPtrEq(ssl->verifyCbCtx, &userCtx);
|
|
}
|
|
|
|
wolfSSL_CTX_SetCACb(ctx, test_ssl_cert_ca_cache_cb);
|
|
if ((ctx != NULL) && (ctx->cm != NULL)) {
|
|
ExpectTrue(ctx->cm->caCacheCallback == test_ssl_cert_ca_cache_cb);
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test getting the certificate the object will present.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_get_certificate_api(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && \
|
|
(defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
|
|
defined(KEEP_OUR_CERT) && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) \
|
|
&& !defined(NO_WOLFSSL_SERVER) && !defined(NO_FILESYSTEM) && \
|
|
!defined(NO_RSA) && \
|
|
defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
ExpectNull(wolfSSL_get_certificate(NULL));
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* The object borrows the context's certificate. */
|
|
ExpectNotNull(wolfSSL_get_certificate(ssl));
|
|
|
|
/* Loading a certificate onto the object makes it own one instead. */
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(ssl, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(wolfSSL_get_certificate(ssl));
|
|
/* Asked again the cached object is returned. */
|
|
ExpectNotNull(wolfSSL_get_certificate(ssl));
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test the certificate unload and cache-size APIs.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_cert_unload(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) \
|
|
&& defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_POST_HANDSHAKE_AUTH) && !defined(NO_WOLFSSL_SERVER)
|
|
/* Requesting a certificate of nothing fails, and is reported as a general
|
|
* error rather than as a protocol version problem. */
|
|
ExpectIntEQ(wolfSSL_verify_client_post_handshake(NULL), 0);
|
|
#endif
|
|
#ifdef PERSIST_CERT_CACHE
|
|
ExpectIntEQ(wolfSSL_CTX_get_cert_cache_memsize(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
|
|
/* A NULL object reports a bad argument rather than a depth. */
|
|
ExpectIntEQ(wolfSSL_CTX_get_verify_depth(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wolfSSL_get_verify_depth(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx, caCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* The loaded CAs can be released without freeing the context. */
|
|
ExpectIntEQ(wolfSSL_CTX_UnloadCAs(ctx), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_UnloadCAs(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* So can the object's own certificates and keys. */
|
|
ExpectIntEQ(wolfSSL_UnloadCertsKeys(ssl), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UnloadCertsKeys(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_UnloadIntermediateCerts(NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Argument guards across the newly-visible public API surface.
|
|
*
|
|
* src/ssl_api_cert.c, ssl_api_crl_ocsp.c and their siblings are #included into
|
|
* ssl.c rather than compiled standalone, so they produced no object file and
|
|
* no module declared them until this part of the campaign. Now that they are
|
|
* measured, the shape of what is missing is unambiguous: 342 of the 646
|
|
* uncovered conditions across these files mention NULL, and in the densest of
|
|
* them it is 85-100%.
|
|
*
|
|
* They are not protocol behaviour. They are the checks each entry point makes
|
|
* on its own arguments, and the existing tests all pass arguments that are
|
|
* valid -- so every one of these decisions is taken the same way on every
|
|
* call, and none of the operands has an independence pair.
|
|
*
|
|
* A caller that queries or configures a CTX or an SSL it has not created yet,
|
|
* or passes a zero length, or asks for an output through a NULL pointer, is
|
|
* doing something ordinary and wrong. That is what these vectors are.
|
|
* ------------------------------------------------------------------------- */
|
|
int test_wolfSSL_cert_api_arg_guards(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
#ifdef HAVE_RPK
|
|
int tp = 0;
|
|
const char certTypes[] = { WOLFSSL_CERT_TYPE_X509 };
|
|
unsigned char spki[8];
|
|
|
|
XMEMSET(spki, 0, sizeof(spki));
|
|
#endif
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* --- mutual auth and verify depth: NULL object, valid argument ------ */
|
|
ExpectIntNE(wolfSSL_CTX_mutual_auth(NULL, 1), WOLFSSL_SUCCESS);
|
|
ExpectIntNE(wolfSSL_mutual_auth(NULL, 1), WOLFSSL_SUCCESS);
|
|
(void)wolfSSL_CTX_mutual_auth(ctx, 1);
|
|
(void)wolfSSL_CTX_mutual_auth(ctx, 0);
|
|
(void)wolfSSL_mutual_auth(ssl, 1);
|
|
(void)wolfSSL_mutual_auth(ssl, 0);
|
|
|
|
ExpectNull(wolfSSL_CTX_GetCertManager(NULL));
|
|
ExpectNotNull(wolfSSL_CTX_GetCertManager(ctx));
|
|
|
|
wolfSSL_CTX_set_verify_depth(NULL, 4);
|
|
wolfSSL_CTX_set_verify_depth(ctx, 4);
|
|
(void)wolfSSL_CTX_get_verify_depth(NULL);
|
|
(void)wolfSSL_CTX_get_verify_depth(ctx);
|
|
(void)wolfSSL_get_verify_depth(NULL);
|
|
(void)wolfSSL_get_verify_depth(ssl);
|
|
|
|
/* Certificate type lists and raw public keys are RPK-only symbols
|
|
* (declared under #ifdef HAVE_RPK in wolfssl/ssl.h); this option list is
|
|
* shared with modules that do not enable RPK (asn, pkcs7, and the rest of
|
|
* wolfCrypt), and tests/api.c is one translation unit across all of them,
|
|
* so referencing these unguarded breaks every OTHER module's build, not
|
|
* just this one -- exactly what happened here. */
|
|
#ifdef HAVE_RPK
|
|
/* --- certificate type lists: NULL object, NULL buffer, bad length --- */
|
|
(void)wolfSSL_CTX_set_client_cert_type(NULL, certTypes,
|
|
(int)sizeof(certTypes));
|
|
(void)wolfSSL_CTX_set_server_cert_type(NULL, certTypes,
|
|
(int)sizeof(certTypes));
|
|
(void)wolfSSL_set_client_cert_type(NULL, certTypes,
|
|
(int)sizeof(certTypes));
|
|
(void)wolfSSL_set_server_cert_type(NULL, certTypes,
|
|
(int)sizeof(certTypes));
|
|
/* NULL list with a non-zero length, and a list longer than allowed:
|
|
* both are refusals a correct caller never triggers */
|
|
(void)wolfSSL_CTX_set_client_cert_type(ctx, NULL, 1);
|
|
(void)wolfSSL_CTX_set_client_cert_type(ctx, certTypes, 0);
|
|
(void)wolfSSL_CTX_set_client_cert_type(ctx, certTypes, 99);
|
|
(void)wolfSSL_set_server_cert_type(ssl, NULL, 1);
|
|
(void)wolfSSL_set_server_cert_type(ssl, certTypes, 0);
|
|
(void)wolfSSL_set_server_cert_type(ssl, certTypes, 99);
|
|
/* the accepting partners */
|
|
(void)wolfSSL_CTX_set_client_cert_type(ctx, certTypes,
|
|
(int)sizeof(certTypes));
|
|
(void)wolfSSL_set_server_cert_type(ssl, certTypes,
|
|
(int)sizeof(certTypes));
|
|
|
|
/* negotiated type read back before any handshake, and through NULL */
|
|
(void)wolfSSL_get_negotiated_client_cert_type(NULL, &tp);
|
|
(void)wolfSSL_get_negotiated_server_cert_type(NULL, &tp);
|
|
(void)wolfSSL_get_negotiated_client_cert_type(ssl, NULL);
|
|
(void)wolfSSL_get_negotiated_server_cert_type(ssl, NULL);
|
|
(void)wolfSSL_get_negotiated_client_cert_type(ssl, &tp);
|
|
(void)wolfSSL_get_negotiated_server_cert_type(ssl, &tp);
|
|
|
|
/* --- raw public key expectations ------------------------------------ */
|
|
(void)wolfSSL_CTX_set_expected_rpk(NULL, spki, (word32)sizeof(spki));
|
|
(void)wolfSSL_set_expected_rpk(NULL, spki, (word32)sizeof(spki));
|
|
(void)wolfSSL_CTX_set_expected_rpk(ctx, NULL, (word32)sizeof(spki));
|
|
(void)wolfSSL_set_expected_rpk(ssl, NULL, (word32)sizeof(spki));
|
|
(void)wolfSSL_CTX_set_expected_rpk(ctx, spki, 0);
|
|
(void)wolfSSL_set_expected_rpk(ssl, spki, 0);
|
|
(void)wolfSSL_CTX_set_expected_rpk(ctx, spki, (word32)sizeof(spki));
|
|
(void)wolfSSL_set_expected_rpk(ssl, spki, (word32)sizeof(spki));
|
|
(void)wolfSSL_CTX_clear_expected_rpk(NULL);
|
|
(void)wolfSSL_clear_expected_rpk(NULL);
|
|
(void)wolfSSL_CTX_clear_expected_rpk(ctx);
|
|
(void)wolfSSL_clear_expected_rpk(ssl);
|
|
#endif /* HAVE_RPK */
|
|
|
|
/* --- verify configuration through NULL objects ---------------------- */
|
|
wolfSSL_CTX_set_verify(NULL, WOLFSSL_VERIFY_PEER, NULL);
|
|
wolfSSL_set_verify(NULL, WOLFSSL_VERIFY_PEER, NULL);
|
|
wolfSSL_set_verify_result(NULL, 0);
|
|
wolfSSL_CTX_SetCertCbCtx(NULL, NULL);
|
|
wolfSSL_SetCertCbCtx(NULL, NULL);
|
|
/* and the same on real objects, so each guard has its partner */
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_NONE, NULL);
|
|
wolfSSL_set_verify_result(ssl, 0);
|
|
wolfSSL_CTX_SetCertCbCtx(ctx, NULL);
|
|
wolfSSL_SetCertCbCtx(ssl, NULL);
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The CRL and OCSP configuration entry points, same argument-guard rationale.
|
|
* 24 of ssl_api_crl_ocsp.c's 28 uncovered conditions mention NULL. */
|
|
int test_wolfSSL_crl_ocsp_api_arg_guards(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
#ifdef HAVE_CRL
|
|
ExpectIntNE(wolfSSL_EnableCRL(NULL, 0), WOLFSSL_SUCCESS);
|
|
ExpectIntNE(wolfSSL_DisableCRL(NULL), WOLFSSL_SUCCESS);
|
|
ExpectIntNE(wolfSSL_CTX_EnableCRL(NULL, 0), WOLFSSL_SUCCESS);
|
|
ExpectIntNE(wolfSSL_CTX_DisableCRL(NULL), WOLFSSL_SUCCESS);
|
|
(void)wolfSSL_CTX_EnableCRL(ctx, 0);
|
|
(void)wolfSSL_CTX_DisableCRL(ctx);
|
|
(void)wolfSSL_EnableCRL(ssl, 0);
|
|
(void)wolfSSL_DisableCRL(ssl);
|
|
|
|
ExpectIntNE(wolfSSL_SetCRL_Cb(NULL, NULL), WOLFSSL_SUCCESS);
|
|
ExpectIntNE(wolfSSL_CTX_SetCRL_Cb(NULL, NULL), WOLFSSL_SUCCESS);
|
|
(void)wolfSSL_SetCRL_Cb(ssl, NULL);
|
|
(void)wolfSSL_CTX_SetCRL_Cb(ctx, NULL);
|
|
ExpectIntNE(wolfSSL_SetCRL_ErrorCb(NULL, NULL, NULL), WOLFSSL_SUCCESS);
|
|
ExpectIntNE(wolfSSL_CTX_SetCRL_ErrorCb(NULL, NULL, NULL), WOLFSSL_SUCCESS);
|
|
(void)wolfSSL_SetCRL_ErrorCb(ssl, NULL, NULL);
|
|
(void)wolfSSL_CTX_SetCRL_ErrorCb(ctx, NULL, NULL);
|
|
|
|
#ifdef HAVE_CRL_IO
|
|
ExpectIntNE(wolfSSL_SetCRL_IOCb(NULL, NULL), WOLFSSL_SUCCESS);
|
|
ExpectIntNE(wolfSSL_CTX_SetCRL_IOCb(NULL, NULL), WOLFSSL_SUCCESS);
|
|
(void)wolfSSL_SetCRL_IOCb(ssl, NULL);
|
|
(void)wolfSSL_CTX_SetCRL_IOCb(ctx, NULL);
|
|
#endif
|
|
|
|
#if !defined(NO_FILESYSTEM) && !defined(NO_WOLFSSL_DIR)
|
|
/* NULL object, NULL path, and a path that does not exist: three
|
|
* different refusals, none of which a working configuration produces */
|
|
(void)wolfSSL_LoadCRL(NULL, "certs/crl", WOLFSSL_FILETYPE_PEM, 0);
|
|
(void)wolfSSL_CTX_LoadCRL(NULL, "certs/crl", WOLFSSL_FILETYPE_PEM, 0);
|
|
(void)wolfSSL_LoadCRL(ssl, NULL, WOLFSSL_FILETYPE_PEM, 0);
|
|
(void)wolfSSL_CTX_LoadCRL(ctx, NULL, WOLFSSL_FILETYPE_PEM, 0);
|
|
(void)wolfSSL_CTX_LoadCRL(ctx, "certs/no-such-dir",
|
|
WOLFSSL_FILETYPE_PEM, 0);
|
|
(void)wolfSSL_LoadCRLFile(NULL, "certs/crl/crl.pem",
|
|
WOLFSSL_FILETYPE_PEM);
|
|
(void)wolfSSL_CTX_LoadCRLFile(NULL, "certs/crl/crl.pem",
|
|
WOLFSSL_FILETYPE_PEM);
|
|
(void)wolfSSL_LoadCRLFile(ssl, NULL, WOLFSSL_FILETYPE_PEM);
|
|
(void)wolfSSL_CTX_LoadCRLFile(ctx, NULL, WOLFSSL_FILETYPE_PEM);
|
|
#endif
|
|
|
|
/* buffer loads: NULL object, NULL buffer, zero length, bad type */
|
|
(void)wolfSSL_CTX_LoadCRLBuffer(NULL, (const unsigned char*)"x", 1,
|
|
WOLFSSL_FILETYPE_ASN1);
|
|
(void)wolfSSL_LoadCRLBuffer(NULL, (const unsigned char*)"x", 1,
|
|
WOLFSSL_FILETYPE_ASN1);
|
|
(void)wolfSSL_CTX_LoadCRLBuffer(ctx, NULL, 1, WOLFSSL_FILETYPE_ASN1);
|
|
(void)wolfSSL_LoadCRLBuffer(ssl, NULL, 1, WOLFSSL_FILETYPE_ASN1);
|
|
(void)wolfSSL_CTX_LoadCRLBuffer(ctx, (const unsigned char*)"x", 0,
|
|
WOLFSSL_FILETYPE_ASN1);
|
|
(void)wolfSSL_LoadCRLBuffer(ssl, (const unsigned char*)"x", 0,
|
|
WOLFSSL_FILETYPE_ASN1);
|
|
#endif /* HAVE_CRL */
|
|
|
|
#ifdef HAVE_OCSP
|
|
ExpectIntNE(wolfSSL_EnableOCSP(NULL, 0), WOLFSSL_SUCCESS);
|
|
ExpectIntNE(wolfSSL_DisableOCSP(NULL), WOLFSSL_SUCCESS);
|
|
(void)wolfSSL_EnableOCSP(ssl, 0);
|
|
(void)wolfSSL_DisableOCSP(ssl);
|
|
(void)wolfSSL_SetOCSP_OverrideURL(NULL, "http://ocsp.example.com/");
|
|
(void)wolfSSL_SetOCSP_OverrideURL(ssl, NULL);
|
|
(void)wolfSSL_SetOCSP_OverrideURL(ssl, "http://ocsp.example.com/");
|
|
(void)wolfSSL_SetOCSP_Cb(NULL, NULL, NULL, NULL);
|
|
(void)wolfSSL_SetOCSP_Cb(ssl, NULL, NULL, NULL);
|
|
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST
|
|
ExpectIntNE(wolfSSL_EnableOCSPStapling(NULL), WOLFSSL_SUCCESS);
|
|
ExpectIntNE(wolfSSL_DisableOCSPStapling(NULL), WOLFSSL_SUCCESS);
|
|
(void)wolfSSL_EnableOCSPStapling(ssl);
|
|
(void)wolfSSL_DisableOCSPStapling(ssl);
|
|
#endif
|
|
#endif /* HAVE_OCSP */
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if !defined(NO_CERTS) && !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT)
|
|
|
|
/* One row of the verify-mode mapping table: the mode handed to
|
|
* wolfSSL_[CTX_]set_verify() and the option bits it must produce.
|
|
* WOLFSSL_VERIFY_POST_HANDSHAKE is not covered here because its option is
|
|
* conditionally compiled; it is checked separately below. */
|
|
typedef struct {
|
|
const char* desc;
|
|
int mode;
|
|
int verifyNone;
|
|
int verifyPeer;
|
|
int failNoCert;
|
|
int failNoCertxPSK;
|
|
} VerifyModeCase;
|
|
|
|
/* Callback used only to check that set_verify() stores and clears it. */
|
|
static int test_verify_mode_cb(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
(void)store;
|
|
return preverify;
|
|
}
|
|
|
|
static const VerifyModeCase verifyModeCases[] = {
|
|
/* VERIFY_NONE is the only mode that sets verifyNone. */
|
|
{ "NONE", WOLFSSL_VERIFY_NONE, 1, 0, 0, 0 },
|
|
{ "PEER", WOLFSSL_VERIFY_PEER, 0, 1, 0, 0 },
|
|
{ "PEER|FAIL_IF_NO_PEER_CERT",
|
|
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
|
0, 1, 1, 0 },
|
|
{ "PEER|FAIL_EXCEPT_PSK",
|
|
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_EXCEPT_PSK,
|
|
0, 1, 0, 1 },
|
|
/* The two fail-on-no-cert flags are independent bits and may both be
|
|
* recorded; the PSK exception is applied first at the handshake. */
|
|
{ "PEER|FAIL_IF_NO_PEER_CERT|FAIL_EXCEPT_PSK",
|
|
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT |
|
|
WOLFSSL_VERIFY_FAIL_EXCEPT_PSK,
|
|
0, 1, 1, 1 },
|
|
/* FAIL_IF_NO_PEER_CERT on its own is recorded even though a server that
|
|
* never requests a certificate cannot act on it. */
|
|
{ "FAIL_IF_NO_PEER_CERT", WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
|
0, 0, 1, 0 },
|
|
/* VERIFY_DEFAULT clears every option instead of setting one. That is
|
|
* why it does not round-trip through get_verify_mode(). */
|
|
{ "DEFAULT", WOLFSSL_VERIFY_DEFAULT, 0, 0, 0, 0 },
|
|
/* VERIFY_NONE is matched by equality, not as a bit: OR-ing anything else
|
|
* in means the peer IS verified. */
|
|
{ "NONE|PEER", WOLFSSL_VERIFY_NONE | WOLFSSL_VERIFY_PEER, 0, 1, 0, 0 },
|
|
/* CLIENT_ONCE is accepted and ignored: on its own it leaves every option
|
|
* clear, and it never disturbs the flags it is OR-ed with. */
|
|
{ "CLIENT_ONCE", WOLFSSL_VERIFY_CLIENT_ONCE, 0, 0, 0, 0 },
|
|
{ "PEER|CLIENT_ONCE",
|
|
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_CLIENT_ONCE, 0, 1, 0, 0 },
|
|
{ "PEER|FAIL_IF_NO_PEER_CERT|CLIENT_ONCE",
|
|
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT |
|
|
WOLFSSL_VERIFY_CLIENT_ONCE,
|
|
0, 1, 1, 0 }
|
|
};
|
|
|
|
#endif /* !NO_CERTS && !NO_TLS && !NO_WOLFSSL_CLIENT */
|
|
|
|
/* Test that every verify mode flag maps onto the expected internal options.
|
|
*
|
|
* Unlike test_wolfSSL_get_verify_mode(), this reads the options directly and
|
|
* so also runs in builds without the OpenSSL compatibility layer, where
|
|
* wolfSSL_get_verify_mode() is not compiled in.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_verify_mode_options(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
size_t i;
|
|
|
|
/* A NULL object is ignored rather than dereferenced. */
|
|
wolfSSL_CTX_set_verify(NULL, WOLFSSL_VERIFY_PEER, NULL);
|
|
wolfSSL_set_verify(NULL, WOLFSSL_VERIFY_PEER, NULL);
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
for (i = 0; i < XELEM_CNT(verifyModeCases) && EXPECT_SUCCESS(); i++) {
|
|
const VerifyModeCase* c = &verifyModeCases[i];
|
|
|
|
wolfSSL_CTX_set_verify(ctx, c->mode, NULL);
|
|
ExpectIntEQ(ctx->verifyNone, c->verifyNone);
|
|
ExpectIntEQ(ctx->verifyPeer, c->verifyPeer);
|
|
ExpectIntEQ(ctx->failNoCert, c->failNoCert);
|
|
ExpectIntEQ(ctx->failNoCertxPSK, c->failNoCertxPSK);
|
|
|
|
wolfSSL_set_verify(ssl, c->mode, NULL);
|
|
ExpectIntEQ(ssl->options.verifyNone, c->verifyNone);
|
|
ExpectIntEQ(ssl->options.verifyPeer, c->verifyPeer);
|
|
ExpectIntEQ(ssl->options.failNoCert, c->failNoCert);
|
|
ExpectIntEQ(ssl->options.failNoCertxPSK, c->failNoCertxPSK);
|
|
|
|
if (!EXPECT_SUCCESS()) {
|
|
fprintf(stderr, "\nverify mode case failed: %s\n", c->desc);
|
|
}
|
|
}
|
|
|
|
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH)
|
|
/* Post-handshake auth is an ordinary bit alongside the others. */
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_POST_HANDSHAKE, NULL);
|
|
ExpectIntEQ(ctx->verifyPeer, 1);
|
|
ExpectIntEQ(ctx->verifyPostHandshake, 1);
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_POST_HANDSHAKE, NULL);
|
|
ExpectIntEQ(ssl->options.verifyPeer, 1);
|
|
ExpectIntEQ(ssl->options.verifyPostHandshake, 1);
|
|
|
|
/* VERIFY_NONE clears it along with everything else. */
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(ctx->verifyPostHandshake, 0);
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(ssl->options.verifyPostHandshake, 0);
|
|
|
|
/* So does CLIENT_ONCE on its own, which is to say it selects nothing. */
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_POST_HANDSHAKE, NULL);
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_CLIENT_ONCE, NULL);
|
|
ExpectIntEQ(ctx->verifyPostHandshake, 0);
|
|
#endif
|
|
|
|
/* Setting a mode replaces the previous one rather than accumulating. */
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_PEER, NULL);
|
|
ExpectIntEQ(ssl->options.verifyPeer, 1);
|
|
ExpectIntEQ(ssl->options.failNoCert, 0);
|
|
|
|
/* The verify callback is stored and cleared alongside the mode. */
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_PEER, test_verify_mode_cb);
|
|
ExpectTrue(ssl->verifyCallback == test_verify_mode_cb);
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_PEER, NULL);
|
|
ExpectTrue(ssl->verifyCallback == NULL);
|
|
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, test_verify_mode_cb);
|
|
ExpectTrue(ctx->verifyCallback == test_verify_mode_cb);
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL);
|
|
ExpectTrue(ctx->verifyCallback == NULL);
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that WOLFSSL_VERIFY_CLIENT_ONCE is accepted and ignored.
|
|
*
|
|
* The flag exists for source compatibility with OpenSSL. wolfSSL stores no
|
|
* option for it, so it must neither change the other flags it is OR-ed with
|
|
* nor be reported back by get_verify_mode().
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_verify_client_once_ignored(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* Adding CLIENT_ONCE to a mode leaves that mode's options untouched. */
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(ssl->options.verifyPeer, 1);
|
|
ExpectIntEQ(ssl->options.failNoCert, 1);
|
|
ExpectIntEQ(ssl->options.verifyNone, 0);
|
|
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT | WOLFSSL_VERIFY_CLIENT_ONCE,
|
|
NULL);
|
|
ExpectIntEQ(ssl->options.verifyPeer, 1);
|
|
ExpectIntEQ(ssl->options.failNoCert, 1);
|
|
ExpectIntEQ(ssl->options.verifyNone, 0);
|
|
|
|
/* On its own it selects nothing at all - the same state as
|
|
* WOLFSSL_VERIFY_DEFAULT, not the same as WOLFSSL_VERIFY_NONE. */
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_CLIENT_ONCE, NULL);
|
|
ExpectIntEQ(ssl->options.verifyNone, 0);
|
|
ExpectIntEQ(ssl->options.verifyPeer, 0);
|
|
ExpectIntEQ(ssl->options.failNoCert, 0);
|
|
ExpectIntEQ(ssl->options.failNoCertxPSK, 0);
|
|
|
|
/* CLIENT_ONCE does not turn VERIFY_NONE into something else either:
|
|
* VERIFY_NONE is matched by equality, so the pair verifies the peer. */
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_NONE | WOLFSSL_VERIFY_CLIENT_ONCE,
|
|
NULL);
|
|
ExpectIntEQ(ssl->options.verifyNone, 0);
|
|
|
|
#if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || \
|
|
defined(HAVE_STUNNEL) || defined(WOLFSSL_MYSQL_COMPATIBLE) || \
|
|
defined(WOLFSSL_NGINX)
|
|
/* The bit is never reported back, on the object or on the context. */
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_CLIENT_ONCE,
|
|
NULL);
|
|
ExpectIntEQ(wolfSSL_get_verify_mode(ssl), WOLFSSL_VERIFY_PEER);
|
|
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_CLIENT_ONCE, NULL);
|
|
ExpectIntEQ(wolfSSL_CTX_get_verify_mode(ctx), WOLFSSL_VERIFY_PEER);
|
|
|
|
/* WOLFSSL_VERIFY_DEFAULT does not round-trip either: it clears every
|
|
* option, so the mode reads back as 0. */
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_DEFAULT, NULL);
|
|
ExpectIntEQ(wolfSSL_get_verify_mode(ssl), 0);
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_DEFAULT, NULL);
|
|
ExpectIntEQ(wolfSSL_CTX_get_verify_mode(ctx), 0);
|
|
#endif
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that the context's verify mode is inherited by objects made from it,
|
|
* and that overriding on an object leaves the context alone.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_verify_mode_ctx_inherit(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
WOLFSSL* ssl2 = NULL;
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
|
|
/* An object created after the context is configured inherits the mode. */
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT | WOLFSSL_VERIFY_FAIL_EXCEPT_PSK,
|
|
NULL);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(ssl->options.verifyPeer, 1);
|
|
ExpectIntEQ(ssl->options.failNoCert, 1);
|
|
ExpectIntEQ(ssl->options.failNoCertxPSK, 1);
|
|
ExpectIntEQ(ssl->options.verifyNone, 0);
|
|
|
|
/* Overriding on the object does not write back to the context. */
|
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(ssl->options.verifyNone, 1);
|
|
ExpectIntEQ(ssl->options.verifyPeer, 0);
|
|
ExpectIntEQ(ssl->options.failNoCert, 0);
|
|
ExpectIntEQ(ssl->options.failNoCertxPSK, 0);
|
|
ExpectIntEQ(ctx->verifyNone, 0);
|
|
ExpectIntEQ(ctx->verifyPeer, 1);
|
|
ExpectIntEQ(ctx->failNoCert, 1);
|
|
ExpectIntEQ(ctx->failNoCertxPSK, 1);
|
|
|
|
/* So a later object still picks up the context's unchanged mode. */
|
|
ExpectNotNull(ssl2 = wolfSSL_new(ctx));
|
|
ExpectIntEQ(ssl2->options.verifyPeer, 1);
|
|
ExpectIntEQ(ssl2->options.failNoCert, 1);
|
|
ExpectIntEQ(ssl2->options.failNoCertxPSK, 1);
|
|
ExpectIntEQ(ssl2->options.verifyNone, 0);
|
|
|
|
wolfSSL_free(ssl2);
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that WOLFSSL_VERIFY_NONE really does disable peer verification.
|
|
*
|
|
* The client has no CA loaded, so the server's certificate cannot be chained.
|
|
* Verifying must fail and WOLFSSL_VERIFY_NONE must succeed - the second half
|
|
* alone would pass even if verification never ran. The mode is always set
|
|
* explicitly because the default depends on the build: with
|
|
* OPENSSL_COMPATIBLE_DEFAULTS a new context starts at WOLFSSL_VERIFY_NONE.
|
|
*
|
|
* The last case pins the behavior of the equality match on WOLFSSL_VERIFY_NONE:
|
|
* OR-ing it with WOLFSSL_VERIFY_PEER verifies the peer, it does not disable
|
|
* verification.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_verify_none_accepts_untrusted(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA)
|
|
static const struct {
|
|
const char* desc;
|
|
int mode;
|
|
int expectFail;
|
|
} cases[] = {
|
|
{ "PEER", WOLFSSL_VERIFY_PEER, 1 },
|
|
{ "NONE", WOLFSSL_VERIFY_NONE, 0 },
|
|
{ "NONE|PEER", WOLFSSL_VERIFY_NONE | WOLFSSL_VERIFY_PEER, 1 }
|
|
};
|
|
size_t i;
|
|
|
|
for (i = 0; i < XELEM_CNT(cases) && EXPECT_SUCCESS(); i++) {
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
|
|
/* Build the client context here so that test_memio_setup() leaves it
|
|
* alone and no CA is loaded into it. */
|
|
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
if (ctx_c != NULL) {
|
|
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
|
|
}
|
|
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
wolfSSL_set_verify(ssl_c, cases[i].mode, NULL);
|
|
|
|
if (cases[i].expectFail) {
|
|
/* Verifying, with no CA: the chain has no signer. */
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(ASN_NO_SIGNER_E));
|
|
}
|
|
else {
|
|
/* Not verifying: the same untrusted certificate is accepted. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
if (!EXPECT_SUCCESS()) {
|
|
fprintf(stderr, "\nverify none case failed: %s\n", cases[i].desc);
|
|
}
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* TLS 1.2 PSK suite to use for the FAIL_EXCEPT_PSK exception, if this build
|
|
* has one. The static-PSK suites (PSK-*) are gated on WOLFSSL_STATIC_PSK,
|
|
* which --enable-psk alone does not set, so they cannot be the first choice:
|
|
* naming one unconditionally compiles the PSK half of the test away in every
|
|
* ordinary PSK build. */
|
|
#if !defined(NO_PSK) && !defined(WOLFSSL_NO_TLS12)
|
|
#if defined(BUILD_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256)
|
|
#define TEST_VFY_PSK_SUITE "ECDHE-PSK-AES128-GCM-SHA256"
|
|
#elif defined(BUILD_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256)
|
|
#define TEST_VFY_PSK_SUITE "DHE-PSK-AES128-GCM-SHA256"
|
|
#elif defined(BUILD_TLS_PSK_WITH_AES_128_GCM_SHA256)
|
|
#define TEST_VFY_PSK_SUITE "PSK-AES128-GCM-SHA256"
|
|
#endif
|
|
#endif
|
|
|
|
/* Test the WOLFSSL_VERIFY_FAIL_EXCEPT_PSK exception on a TLS 1.2 server.
|
|
*
|
|
* The client never sends a certificate. The server must accept that on a PSK
|
|
* suite and reject it on a certificate suite, which is the only difference
|
|
* between this mode and WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_verify_fail_except_psk(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA) && \
|
|
!defined(WOLFSSL_NO_CLIENT_AUTH)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
#ifdef TEST_VFY_PSK_SUITE
|
|
/* A PSK suite is the exception: no peer certificate is required. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_EXCEPT_PSK, NULL);
|
|
wolfSSL_set_psk_client_callback(ssl_c, my_psk_client_cb);
|
|
wolfSSL_set_psk_server_callback(ssl_s, my_psk_server_cb);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, TEST_VFY_PSK_SUITE),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, TEST_VFY_PSK_SUITE),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(ssl_s->options.usingPSK_cipher, 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_c);
|
|
ctx_c = NULL;
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_s = NULL;
|
|
#endif /* TEST_VFY_PSK_SUITE */
|
|
|
|
/* A certificate suite is not the exception: the same mode and the same
|
|
* cert-less client are now rejected. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_EXCEPT_PSK, NULL);
|
|
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(NO_PEER_CERT));
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_CLIENT_AUTH) && !defined(NO_RSA)
|
|
|
|
/* Run one handshake where the server demands a client certificate and the
|
|
* client has none, and check the server rejects it with NO_PEER_CERT.
|
|
*
|
|
* The rejection happens in a different place per protocol version, which is
|
|
* why this is run over several of them: TLS 1.3 rejects after Finished, TLS
|
|
* 1.2 on the empty Certificate message, and TLS 1.1 and below only reach the
|
|
* check in DoClientKeyExchange (the empty-Certificate check there is gated on
|
|
* IsAtLeastTLSv1_2).
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
static int test_vfy_no_client_cert(method_provider method_c,
|
|
method_provider method_s, int rounds)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
method_c, method_s), 0);
|
|
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
|
|
/* The client has no certificate loaded, so it answers the server's
|
|
* request with an empty Certificate message. */
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, rounds, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(NO_PEER_CERT));
|
|
ExpectIntEQ(ssl_s->options.havePeerCert, 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Run one handshake where the server does not verify the peer and the client
|
|
* does have a certificate, and check the server never asks for it.
|
|
*
|
|
* WOLFSSL_VERIFY_NONE on a server means "send no CertificateRequest", which
|
|
* is only observable from the client: it is never asked, so it never sends
|
|
* the certificate it holds.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
static int test_vfy_none_server_no_request(method_provider method_c,
|
|
method_provider method_s, int rounds)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
method_c, method_s), 0);
|
|
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_NONE, NULL);
|
|
|
|
/* Give the client a certificate it could present if it were asked.
|
|
* CERT_FILETYPE tracks the cliCertFile/cliKeyFile paths, which are DER
|
|
* rather than PEM in a build without WOLFSSL_PEM_TO_DER. */
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile,
|
|
CERT_FILETYPE), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile,
|
|
CERT_FILETYPE), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, rounds, NULL), 0);
|
|
|
|
/* No CertificateRequest arrived, so the client sent no certificate and
|
|
* the server holds none. */
|
|
ExpectIntEQ(ssl_c->options.sendVerify, 0);
|
|
ExpectIntEQ(ssl_s->options.havePeerCert, 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES && !WOLFSSL_NO_CLIENT_AUTH */
|
|
|
|
/* Test that a server requiring a client certificate rejects a client that has
|
|
* none, on every protocol version the build supports.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_verify_no_client_cert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_CLIENT_AUTH) && !defined(NO_RSA)
|
|
|
|
#if defined(WOLFSSL_TLS13)
|
|
/* TLS 1.3: rejected when the server processes the client's Finished. */
|
|
ExpectIntEQ(test_vfy_no_client_cert(wolfTLSv1_3_client_method,
|
|
wolfTLSv1_3_server_method, 10), TEST_SUCCESS);
|
|
#endif
|
|
#if !defined(WOLFSSL_NO_TLS12)
|
|
/* TLS 1.2: rejected on the empty Certificate message. */
|
|
ExpectIntEQ(test_vfy_no_client_cert(wolfTLSv1_2_client_method,
|
|
wolfTLSv1_2_server_method, 10), TEST_SUCCESS);
|
|
#endif
|
|
#if !defined(NO_OLD_TLS)
|
|
/* TLS 1.1: the empty-Certificate check does not apply below TLS 1.2, so
|
|
* this is rejected later, in DoClientKeyExchange. */
|
|
ExpectIntEQ(test_vfy_no_client_cert(wolfTLSv1_1_client_method,
|
|
wolfTLSv1_1_server_method, 10), TEST_SUCCESS);
|
|
#endif
|
|
#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12)
|
|
/* DTLS 1.2 over the same TLS 1.2 code path, but across a cookie
|
|
* exchange and a retransmittable flight. */
|
|
ExpectIntEQ(test_vfy_no_client_cert(wolfDTLSv1_2_client_method,
|
|
wolfDTLSv1_2_server_method, 20), TEST_SUCCESS);
|
|
#endif
|
|
#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_TLS13)
|
|
ExpectIntEQ(test_vfy_no_client_cert(wolfDTLSv1_3_client_method,
|
|
wolfDTLSv1_3_server_method, 20), TEST_SUCCESS);
|
|
#endif
|
|
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that WOLFSSL_VERIFY_NONE on a server suppresses the certificate
|
|
* request, on every protocol version the build supports.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_verify_none_server_no_request(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_CLIENT_AUTH) && !defined(NO_RSA) && \
|
|
!defined(NO_FILESYSTEM)
|
|
|
|
#if defined(WOLFSSL_TLS13)
|
|
ExpectIntEQ(test_vfy_none_server_no_request(wolfTLSv1_3_client_method,
|
|
wolfTLSv1_3_server_method, 10), TEST_SUCCESS);
|
|
#endif
|
|
#if !defined(WOLFSSL_NO_TLS12)
|
|
ExpectIntEQ(test_vfy_none_server_no_request(wolfTLSv1_2_client_method,
|
|
wolfTLSv1_2_server_method, 10), TEST_SUCCESS);
|
|
#endif
|
|
#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12)
|
|
ExpectIntEQ(test_vfy_none_server_no_request(wolfDTLSv1_2_client_method,
|
|
wolfDTLSv1_2_server_method, 20), TEST_SUCCESS);
|
|
#endif
|
|
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a TLS 1.3 server set to WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT
|
|
* without WOLFSSL_VERIFY_PEER still refuses a client that has no
|
|
* certificate.
|
|
*
|
|
* The CertificateRequest is sent only for WOLFSSL_VERIFY_PEER, so nothing
|
|
* asks the client for a certificate and nothing notices it is missing until
|
|
* DoTls13Finished checks at the end of the handshake.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_verify_tls13_failnocert_only(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
!defined(WOLFSSL_NO_CLIENT_AUTH) && !defined(NO_RSA)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(ssl_s->options.verifyPeer, 0);
|
|
ExpectIntEQ(ssl_s->options.failNoCert, 1);
|
|
|
|
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s,
|
|
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
|
|
WC_NO_ERR_TRACE(NO_PEER_CERT));
|
|
/* The client was never asked, so it never sent one. */
|
|
ExpectIntEQ(ssl_s->msgsReceived.got_certificate, 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA)
|
|
|
|
/* Replace one whole record in a memio buffer with a supplied one.
|
|
*
|
|
* Used to put a message on the wire that no conforming wolfSSL peer would
|
|
* send. Only safe before the cipher spec changes: an encrypted record cannot
|
|
* be substituted without also re-doing the AEAD.
|
|
*
|
|
* The records of one flight may arrive as a single memio message or as one
|
|
* message each, depending on whether the build groups handshake messages, so
|
|
* the buffer is walked by record and the containing message resized after.
|
|
*
|
|
* @param [in,out] ctx memio context.
|
|
* @param [in] client 1 for the client's inbound buffer (what the server
|
|
* wrote), 0 for the server's inbound buffer.
|
|
* @param [in] hsType handshake type of the record to replace.
|
|
* @param [in] rec replacement record, including its record header.
|
|
* @param [in] recSz length of rec.
|
|
* @return 0 on success, -1 when no such record is present.
|
|
*/
|
|
static int test_vfy_replace_record(struct test_memio_ctx* ctx, int client,
|
|
byte hsType, const byte* rec, int recSz)
|
|
{
|
|
byte* buff;
|
|
int* len;
|
|
int* msgSizes;
|
|
int msgCount;
|
|
int off = 0;
|
|
int target = -1;
|
|
int targetSz = 0;
|
|
int delta;
|
|
int msgOff;
|
|
int i;
|
|
|
|
if (client) {
|
|
buff = ctx->c_buff; len = &ctx->c_len;
|
|
msgSizes = ctx->c_msg_sizes; msgCount = ctx->c_msg_count;
|
|
}
|
|
else {
|
|
buff = ctx->s_buff; len = &ctx->s_len;
|
|
msgSizes = ctx->s_msg_sizes; msgCount = ctx->s_msg_count;
|
|
}
|
|
|
|
/* A plaintext record: content type, two version bytes, a two byte
|
|
* length, then the body - whose first byte is the handshake type. */
|
|
while (off + RECORD_HEADER_SZ <= *len) {
|
|
int recLen = (buff[off + 3] << 8) | buff[off + 4];
|
|
|
|
if (off + RECORD_HEADER_SZ + recLen > *len) {
|
|
break;
|
|
}
|
|
if (buff[off] == handshake && recLen > 0 &&
|
|
buff[off + RECORD_HEADER_SZ] == hsType) {
|
|
target = off;
|
|
targetSz = RECORD_HEADER_SZ + recLen;
|
|
break;
|
|
}
|
|
off += RECORD_HEADER_SZ + recLen;
|
|
}
|
|
if (target < 0) {
|
|
return -1;
|
|
}
|
|
|
|
delta = recSz - targetSz;
|
|
if (*len + delta > TEST_MEMIO_BUF_SZ) {
|
|
return -1;
|
|
}
|
|
|
|
XMEMMOVE(buff + target + recSz, buff + target + targetSz,
|
|
(size_t)(*len - target - targetSz));
|
|
XMEMCPY(buff + target, rec, (size_t)recSz);
|
|
*len += delta;
|
|
|
|
/* Resize whichever message holds the record that was replaced. */
|
|
msgOff = 0;
|
|
for (i = 0; i < msgCount; i++) {
|
|
if (target < msgOff + msgSizes[i]) {
|
|
msgSizes[i] += delta;
|
|
break;
|
|
}
|
|
msgOff += msgSizes[i];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES && !WOLFSSL_NO_TLS12 &&
|
|
* !NO_RSA */
|
|
|
|
/* Test that a client rejects an empty Certificate message from the server.
|
|
*
|
|
* No conforming server sends one - a server with no certificate cannot use a
|
|
* certificate cipher suite at all - so the server's Certificate record is
|
|
* substituted on the wire. This is the client-side half of the empty
|
|
* certificate check, which is otherwise never reached.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_verify_empty_server_cert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(WOLFSSL_NO_TLS12) && !defined(NO_RSA)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
/* Handshake record holding a Certificate message with an empty
|
|
* certificate_list: record type 22, TLS 1.2, 7 bytes of payload; then
|
|
* handshake type 11 with a 3 byte body; then a zero list length. */
|
|
static const byte emptyCert[] = {
|
|
0x16, 0x03, 0x03, 0x00, 0x07,
|
|
0x0b, 0x00, 0x00, 0x03,
|
|
0x00, 0x00, 0x00
|
|
};
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, NULL);
|
|
|
|
/* Client sends its hello. */
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
/* Server replies with its flight, still in the clear. */
|
|
ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
|
WOLFSSL_ERROR_WANT_READ);
|
|
|
|
/* Swap the server's Certificate for one carrying no certificates. */
|
|
ExpectIntEQ(test_vfy_replace_record(&test_ctx, 1, certificate,
|
|
emptyCert, (int)sizeof(emptyCert)), 0);
|
|
|
|
ExpectIntEQ(wolfSSL_connect(ssl_c), WOLFSSL_FATAL_ERROR);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR),
|
|
WC_NO_ERR_TRACE(NO_PEER_CERT));
|
|
ExpectIntEQ(ssl_c->options.havePeerCert, 0);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that WOLFSSL_VERIFY_POST_HANDSHAKE defers client authentication past
|
|
* the initial handshake.
|
|
*
|
|
* The CertificateRequest is suppressed during the initial handshake when
|
|
* post-handshake auth is selected, so a client with no certificate completes
|
|
* the handshake instead of being rejected. This is the one configuration that
|
|
* reaches the post-handshake arm of the Finished sanity check with no
|
|
* request outstanding.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_verify_post_handshake_defers(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
|
|
defined(WOLFSSL_POST_HANDSHAKE_AUTH) && !defined(WOLFSSL_NO_CLIENT_AUTH) \
|
|
&& !defined(NO_RSA)
|
|
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
|
|
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_POST_HANDSHAKE, NULL);
|
|
ExpectIntEQ(ssl_s->options.verifyPeer, 1);
|
|
ExpectIntEQ(ssl_s->options.verifyPostHandshake, 1);
|
|
|
|
/* The client has no certificate, and is never asked for one during the
|
|
* initial handshake, so the handshake completes. */
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(ssl_s->msgsReceived.got_certificate, 0);
|
|
ExpectIntEQ(ssl_s->options.havePeerCert, 0);
|
|
ExpectNull(ssl_s->certReqCtx);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \
|
|
!defined(NO_TLS) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \
|
|
defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \
|
|
defined(USE_CERT_BUFFERS_2048) && !defined(IGNORE_KEY_EXTENSIONS)
|
|
|
|
#define TEST_EKU_CERT_BUF_SZ (2 * FOURK_BUF)
|
|
|
|
/* Keys and scratch buffers shared by every case. */
|
|
typedef struct test_eku_fixture {
|
|
RsaKey* caKey; /* the 2048-bit test root */
|
|
RsaKey* midKey; /* CA between the CA under test and the leaf */
|
|
RsaKey* intKey; /* the CA whose Extended Key Usage is under test */
|
|
RsaKey* leafKey;
|
|
WC_RNG* rng;
|
|
byte* midDer;
|
|
byte* interDer;
|
|
byte* chainDer;
|
|
} test_eku_fixture;
|
|
|
|
/* One chain Extended Key Usage scenario. */
|
|
typedef struct test_eku_case {
|
|
const char* extKeyUsage; /* Extended Key Usage on the CA under test,
|
|
* NULL omits the extension */
|
|
int selfSignedCa; /* CA under test is self-signed and is itself
|
|
* the trust anchor */
|
|
int selfIssuedCa; /* CA under test takes its issuer's subject
|
|
* name, as a key rollover certificate does,
|
|
* but keeps its own key */
|
|
int clientPresents; /* client sends the chain, server verifies it */
|
|
int pinCa; /* CA under test is also loaded as a trusted
|
|
* CA before the handshake */
|
|
int extraLevel; /* insert a CA between the CA under test and
|
|
* the leaf, putting the CA under test at
|
|
* depth 2 */
|
|
int overrideCb; /* verify callback installed on the verifying
|
|
* side: 0 none, 1 accepts every error,
|
|
* 2 waives only the purpose error */
|
|
int verifyDepth; /* verify depth pinned on the verifying side,
|
|
* 0 leaves the default in place */
|
|
int expectRet; /* expected handshake result */
|
|
const char* keyUsage; /* Key Usage on the CA under test, NULL uses
|
|
* keyCertSign,cRLSign */
|
|
} test_eku_case;
|
|
|
|
/* Stand in for an application that knowingly accepts a rejected certificate. */
|
|
static int test_eku_override_cb(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
(void)preverify;
|
|
(void)store;
|
|
return 1;
|
|
}
|
|
|
|
/* Stand in for an application that knowingly accepts a repurposed CA and
|
|
* nothing else. Every other rejection is left to stand, so a chain the library
|
|
* turns away for another reason must still fail. */
|
|
static int test_eku_purpose_only_cb(int preverify,
|
|
WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
if (store->error == WC_NO_ERR_TRACE(EXTKEYUSE_AUTH_E)
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
|| store->error == WOLFSSL_X509_V_ERR_INVALID_PURPOSE
|
|
#endif
|
|
) {
|
|
return 1;
|
|
}
|
|
return preverify;
|
|
}
|
|
|
|
/* Build a CA:TRUE certificate for subjKey. A NULL issuerDer makes it
|
|
* self-signed, otherwise it is signed by issuerKey in the name of issuerDer. A
|
|
* NULL extKeyUsage omits the Extended Key Usage extension entirely. When
|
|
* selfIssued is set the certificate takes the issuer's own subject name
|
|
* instead of cn, which is the shape of a CA key rollover link certificate
|
|
* (RFC 4210 OldWithNew): issuer and subject match, but the key is subjKey and
|
|
* the signature is issuerKey's. Returns the DER length, or < 0 on failure. */
|
|
static int test_eku_gen_ca(byte* out, int outMax, RsaKey* subjKey,
|
|
const byte* issuerDer, int issuerDerSz, RsaKey* issuerKey, WC_RNG* rng,
|
|
const char* extKeyUsage, const char* cn, int selfIssued,
|
|
const char* keyUsage)
|
|
{
|
|
Cert cert;
|
|
int ret = 0;
|
|
|
|
if (selfIssued && issuerDer == NULL)
|
|
return -1;
|
|
if (wc_InitCert(&cert) != 0)
|
|
return -1;
|
|
cert.isCA = 1;
|
|
cert.sigType = CTC_SHA256wRSA;
|
|
if (selfIssued) {
|
|
/* Copy the issuer's subject name verbatim. The name fields
|
|
* wc_InitCert() cleared are left alone, since wc_SetSubjectBuffer()
|
|
* only fills in what the issuer actually carries and the issuer name
|
|
* set below is built from the same source. */
|
|
if (wc_SetSubjectBuffer(&cert, issuerDer, issuerDerSz) != 0)
|
|
ret = -1;
|
|
}
|
|
else {
|
|
XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE - 1);
|
|
XSTRNCPY(cert.subject.org, "wolfSSL_test", CTC_NAME_SIZE - 1);
|
|
XSTRNCPY(cert.subject.commonName, cn, CTC_NAME_SIZE - 1);
|
|
}
|
|
if (ret == 0 && wc_SetSubjectKeyIdFromPublicKey(&cert, subjKey, NULL) != 0)
|
|
ret = -1;
|
|
if (ret == 0 && wc_SetKeyUsage(&cert,
|
|
(keyUsage != NULL) ? keyUsage : "keyCertSign,cRLSign") != 0)
|
|
ret = -1;
|
|
if (ret == 0 && extKeyUsage != NULL &&
|
|
wc_SetExtKeyUsage(&cert, extKeyUsage) != 0)
|
|
ret = -1;
|
|
/* wc_InitCert() leaves selfSigned set, so naming no issuer is what makes
|
|
* the generated certificate self-signed. */
|
|
if (ret == 0 && issuerDer != NULL) {
|
|
if (wc_SetAuthKeyIdFromCert(&cert, issuerDer, issuerDerSz) != 0)
|
|
ret = -1;
|
|
if (ret == 0 && wc_SetIssuerBuffer(&cert, issuerDer, issuerDerSz) != 0)
|
|
ret = -1;
|
|
}
|
|
if (ret == 0)
|
|
ret = wc_MakeCert(&cert, out, (word32)outMax, subjKey, NULL, rng);
|
|
if (ret >= 0)
|
|
ret = wc_SignCert(cert.bodySz, cert.sigType, out, (word32)outMax,
|
|
(issuerDer == NULL) ? subjKey : issuerKey, NULL, rng);
|
|
#ifdef WOLFSSL_CERT_GEN_CACHE
|
|
wc_SetCert_Free(&cert);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/* Build a TLS leaf signed by the given CA. The leaf carries both serverAuth
|
|
* and clientAuth so only the issuing CA's purpose is tested. */
|
|
static int test_eku_gen_leaf(byte* out, int outMax, RsaKey* leafKey,
|
|
const byte* issuerDer, int issuerDerSz, RsaKey* issuerKey, WC_RNG* rng)
|
|
{
|
|
Cert cert;
|
|
int ret = 0;
|
|
|
|
if (wc_InitCert(&cert) != 0)
|
|
return -1;
|
|
cert.isCA = 0;
|
|
cert.sigType = CTC_SHA256wRSA;
|
|
XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE - 1);
|
|
XSTRNCPY(cert.subject.org, "wolfSSL_test", CTC_NAME_SIZE - 1);
|
|
XSTRNCPY(cert.subject.commonName, "EKU Leaf", CTC_NAME_SIZE - 1);
|
|
if (wc_SetSubjectKeyIdFromPublicKey(&cert, leafKey, NULL) != 0)
|
|
ret = -1;
|
|
if (ret == 0 && wc_SetAuthKeyIdFromCert(&cert, issuerDer, issuerDerSz) != 0)
|
|
ret = -1;
|
|
if (ret == 0 &&
|
|
wc_SetKeyUsage(&cert, "digitalSignature,keyEncipherment") != 0)
|
|
ret = -1;
|
|
if (ret == 0 && wc_SetExtKeyUsage(&cert, "serverAuth,clientAuth") != 0)
|
|
ret = -1;
|
|
if (ret == 0 && wc_SetIssuerBuffer(&cert, issuerDer, issuerDerSz) != 0)
|
|
ret = -1;
|
|
if (ret == 0)
|
|
ret = wc_MakeCert(&cert, out, (word32)outMax, leafKey, NULL, rng);
|
|
if (ret >= 0)
|
|
ret = wc_SignCert(cert.bodySz, cert.sigType, out, (word32)outMax,
|
|
issuerKey, NULL, rng);
|
|
#ifdef WOLFSSL_CERT_GEN_CACHE
|
|
wc_SetCert_Free(&cert);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/* Run a memio handshake in which one side presents the given chain and the
|
|
* peer verifies it against rootDer. When clientPresents is set the client sends
|
|
* the chain and the server verifies it, which is the client authentication
|
|
* direction. A non-NULL pinnedDer is loaded as an additional trusted CA on the
|
|
* verifying side, so the chain CA is already a known signer when it arrives.
|
|
* The handshake result and the compatibility-layer verify result are returned
|
|
* through hsRet and verifyRet. */
|
|
static int test_eku_chain_handshake(const byte* chainDer, int chainSz,
|
|
const byte* rootDer, int rootSz, const byte* pinnedDer, int pinnedSz,
|
|
const test_eku_case* tc, int* hsRet, long* verifyRet)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL_CTX* verifyCtx = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
WOLFSSL* verifySsl = NULL;
|
|
VerifyCallback verifyCb = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
if (tc->overrideCb == 1)
|
|
verifyCb = test_eku_override_cb;
|
|
else if (tc->overrideCb == 2)
|
|
verifyCb = test_eku_purpose_only_cb;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
/* The server always presents the generated chain so a single set of
|
|
* credentials covers both directions. */
|
|
ExpectIntEQ(test_memio_setup_ex(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfSSLv23_client_method, wolfSSLv23_server_method,
|
|
(byte*)rootDer, rootSz, (byte*)chainDer, chainSz,
|
|
(byte*)client_key_der_2048, (int)sizeof_client_key_der_2048), 0);
|
|
|
|
if (tc->clientPresents) {
|
|
/* Leave the server chain unverified so only the server's view of the
|
|
* client chain decides the handshake. */
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_buffer_format(ssl_c, chainDer,
|
|
(long)chainSz, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_buffer(ssl_c, client_key_der_2048,
|
|
(long)sizeof_client_key_der_2048, WOLFSSL_FILETYPE_ASN1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_buffer(ctx_s, rootDer,
|
|
(long)rootSz, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, verifyCb);
|
|
verifyCtx = ctx_s;
|
|
verifySsl = ssl_s;
|
|
}
|
|
else {
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, verifyCb);
|
|
verifyCtx = ctx_c;
|
|
verifySsl = ssl_c;
|
|
}
|
|
|
|
#ifdef OPENSSL_EXTRA
|
|
if (tc->verifyDepth > 0)
|
|
wolfSSL_set_verify_depth(verifySsl, tc->verifyDepth);
|
|
#endif
|
|
|
|
if (pinnedDer != NULL) {
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_buffer(verifyCtx, pinnedDer,
|
|
(long)pinnedSz, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
|
}
|
|
|
|
/* The harness only reports pass or fail, so read the reason from the side
|
|
* that did the verifying. */
|
|
if (EXPECT_SUCCESS()) {
|
|
if (test_memio_do_handshake(ssl_c, ssl_s, 10, NULL) == 0)
|
|
*hsRet = 0;
|
|
else
|
|
*hsRet = wolfSSL_get_error(verifySsl, WOLFSSL_FATAL_ERROR);
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
*verifyRet = wolfSSL_get_verify_result(verifySsl);
|
|
#endif
|
|
}
|
|
(void)verifyRet;
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Build the chain for one scenario, run the handshake and check both the
|
|
* handshake result and the verify result it is reported as. */
|
|
static int test_eku_chain_case(const test_eku_fixture* f,
|
|
const test_eku_case* tc)
|
|
{
|
|
EXPECT_DECLS;
|
|
const byte* rootDer = ca_cert_der_2048;
|
|
const byte* leafIssuerDer;
|
|
int rootSz = (int)sizeof_ca_cert_der_2048;
|
|
int leafIssuerSz;
|
|
RsaKey* leafIssuerKey;
|
|
int midSz = 0;
|
|
int caSz = 0;
|
|
int leafSz = 0;
|
|
int chainSz = 0;
|
|
int hsRet = -1;
|
|
long verifyRet = -1;
|
|
|
|
/* The CA under test is always signed by the root. */
|
|
ExpectIntGT((caSz = test_eku_gen_ca(f->interDer, TEST_EKU_CERT_BUF_SZ,
|
|
f->intKey, tc->selfSignedCa ? NULL : ca_cert_der_2048,
|
|
(int)sizeof_ca_cert_der_2048, f->caKey, f->rng, tc->extKeyUsage,
|
|
tc->selfSignedCa ? "EKU Self Signed CA" : "EKU Intermediate",
|
|
tc->selfIssuedCa, tc->keyUsage)), 0);
|
|
|
|
leafIssuerDer = f->interDer;
|
|
leafIssuerSz = caSz;
|
|
leafIssuerKey = f->intKey;
|
|
|
|
/* An extra level inserts a CA that does carry the TLS purposes between the
|
|
* CA under test and the leaf, so the CA under test is no longer the issuer
|
|
* of the leaf and sits at depth 2. */
|
|
if (tc->extraLevel) {
|
|
ExpectIntGT((midSz = test_eku_gen_ca(f->midDer, TEST_EKU_CERT_BUF_SZ,
|
|
f->midKey, f->interDer, caSz, f->intKey, f->rng,
|
|
"serverAuth,clientAuth", "EKU Middle CA", 0, NULL)), 0);
|
|
leafIssuerDer = f->midDer;
|
|
leafIssuerSz = midSz;
|
|
leafIssuerKey = f->midKey;
|
|
}
|
|
|
|
ExpectIntGT((leafSz = test_eku_gen_leaf(f->chainDer, TEST_EKU_CERT_BUF_SZ,
|
|
f->leafKey, leafIssuerDer, leafIssuerSz, leafIssuerKey, f->rng)), 0);
|
|
/* The chain buffer holds the leaf first, then its issuers in order. */
|
|
ExpectIntLE(leafSz + caSz + midSz, TEST_EKU_CERT_BUF_SZ);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
chainSz = leafSz;
|
|
if (tc->extraLevel) {
|
|
XMEMCPY(f->chainDer + chainSz, f->midDer, (size_t)midSz);
|
|
chainSz += midSz;
|
|
}
|
|
XMEMCPY(f->chainDer + chainSz, f->interDer, (size_t)caSz);
|
|
chainSz += caSz;
|
|
if (tc->selfSignedCa) {
|
|
rootDer = f->interDer;
|
|
rootSz = caSz;
|
|
}
|
|
ExpectIntEQ(test_eku_chain_handshake(f->chainDer, chainSz,
|
|
rootDer, rootSz, tc->pinCa ? f->interDer : NULL, caSz, tc,
|
|
&hsRet, &verifyRet), TEST_SUCCESS);
|
|
}
|
|
|
|
ExpectIntEQ(hsRet, tc->expectRet);
|
|
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
|
|
/* An override leaves the rejection already recorded in place, so only the
|
|
* cases the library decided on its own have a predictable verify result. */
|
|
if (tc->overrideCb == 0) {
|
|
ExpectIntEQ(verifyRet, (tc->expectRet == 0) ? WOLFSSL_X509_V_OK :
|
|
(tc->expectRet == WC_NO_ERR_TRACE(NOT_CA_ERROR)) ?
|
|
WOLFSSL_X509_V_ERR_INVALID_CA : WOLFSSL_X509_V_ERR_INVALID_PURPOSE);
|
|
}
|
|
#endif
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif /* chain EKU test dependencies */
|
|
|
|
/* Test that the Extended Key Usage of a chain-supplied intermediate CA is
|
|
* enforced against the TLS purpose being validated, per RFC 5280 4.2.1.12.
|
|
*
|
|
* A CA restricted to some other purpose, code signing here, must not be able to
|
|
* authenticate a TLS peer even though the leaf below it asks for serverAuth.
|
|
* An absent extension and anyExtendedKeyUsage both leave every purpose valid
|
|
* and must still complete the handshake, and a self-signed trust anchor is
|
|
* exempt whatever its Extended Key Usage says. The anchor exemption is keyed on
|
|
* the anchor's public key, so a self-issued CA key rollover certificate, which
|
|
* shares the anchor's names but not its key, stays subject to the check.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_chain_ca_ext_key_usage(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \
|
|
!defined(NO_TLS) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \
|
|
defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \
|
|
defined(USE_CERT_BUFFERS_2048) && !defined(IGNORE_KEY_EXTENSIONS)
|
|
static const test_eku_case cases[] = {
|
|
/* eku, selfSigned, selfIssued, clientPresents, pinCa, extraLevel,
|
|
* overrideCb, verifyDepth, expected handshake result, keyUsage. */
|
|
|
|
/* Server authentication: a CA restricted to code signing, or to
|
|
* client authentication, must be refused. */
|
|
{ "codeSigning", 0, 0, 0, 0, 0, 0, 0,
|
|
WC_NO_ERR_TRACE(EXTKEYUSE_AUTH_E),
|
|
NULL },
|
|
{ "clientAuth", 0, 0, 0, 0, 0, 0, 0,
|
|
WC_NO_ERR_TRACE(EXTKEYUSE_AUTH_E),
|
|
NULL },
|
|
/* A CA that carries the purpose, or leaves it unrestricted, must not
|
|
* be over-rejected. */
|
|
{ "serverAuth", 0, 0, 0, 0, 0, 0, 0, 0, NULL },
|
|
{ NULL, 0, 0, 0, 0, 0, 0, 0, 0, NULL },
|
|
{ "any", 0, 0, 0, 0, 0, 0, 0, 0, NULL },
|
|
/* The rule applies to a CA the certificate manager already holds, not
|
|
* only to one seen for the first time. */
|
|
{ "codeSigning", 0, 0, 0, 1, 0, 0, 0,
|
|
WC_NO_ERR_TRACE(EXTKEYUSE_AUTH_E),
|
|
NULL },
|
|
{ "serverAuth", 0, 0, 0, 1, 0, 0, 0, 0, NULL },
|
|
/* A self-signed trust anchor is exempt: here it is the anchor test,
|
|
* not the Extended Key Usage, that decides. */
|
|
{ "codeSigning", 1, 0, 0, 0, 0, 0, 0, 0, NULL },
|
|
/* A self-issued CA key rollover certificate is not an anchor. It
|
|
* carries the anchor's issuer and subject names, so DecodedCert's
|
|
* selfIssued flag is set, but it holds a different key and is signed
|
|
* by the anchor. The Extended Key Usage must still be enforced. */
|
|
{ "codeSigning", 0, 1, 0, 0, 0, 0, 0,
|
|
WC_NO_ERR_TRACE(EXTKEYUSE_AUTH_E),
|
|
NULL },
|
|
{ "serverAuth", 0, 1, 0, 0, 0, 0, 0, 0, NULL },
|
|
/* Key Usage, not Extended Key Usage: the same rollover certificate
|
|
* cannot sign the leaf when its Key Usage omits keyCertSign. */
|
|
#ifndef ALLOW_INVALID_CERTSIGN
|
|
{ "serverAuth", 0, 1, 0, 0, 0, 0, 0,
|
|
WC_NO_ERR_TRACE(NOT_CA_ERROR),
|
|
"digitalSignature" },
|
|
#endif
|
|
/* Enforcement is not limited to the CA directly above the leaf: here
|
|
* the CA under test issues "EKU Middle CA", which issues the leaf. */
|
|
{ "codeSigning", 0, 0, 0, 0, 1, 0, 0,
|
|
WC_NO_ERR_TRACE(EXTKEYUSE_AUTH_E),
|
|
NULL },
|
|
{ "serverAuth", 0, 0, 0, 0, 1, 0, 0, 0, NULL },
|
|
/* An application that installs a verify callback keeps the last word,
|
|
* as it does for every other chain error. */
|
|
{ "codeSigning", 0, 0, 0, 0, 0, 1, 0, 0, NULL },
|
|
#ifdef OPENSSL_EXTRA
|
|
/* The purpose check must not stand in for the checks that run before
|
|
* it. With the depth limit pinned below the chain, a callback that
|
|
* waives only the purpose error still has to see the chain length
|
|
* rejection, whether or not the CA under test carries the purpose. */
|
|
{ "serverAuth", 0, 0, 0, 0, 1, 2, 1,
|
|
WC_NO_ERR_TRACE(MAX_CHAIN_ERROR),
|
|
NULL },
|
|
{ "codeSigning", 0, 0, 0, 0, 1, 2, 1,
|
|
WC_NO_ERR_TRACE(MAX_CHAIN_ERROR),
|
|
NULL },
|
|
#endif
|
|
#ifndef WOLFSSL_NO_CLIENT_AUTH
|
|
/* Client authentication: the server applies the same rule with the
|
|
* clientAuth purpose. */
|
|
{ "codeSigning", 0, 0, 1, 0, 0, 0, 0,
|
|
WC_NO_ERR_TRACE(EXTKEYUSE_AUTH_E),
|
|
NULL },
|
|
{ "serverAuth", 0, 0, 1, 0, 0, 0, 0,
|
|
WC_NO_ERR_TRACE(EXTKEYUSE_AUTH_E),
|
|
NULL },
|
|
{ "clientAuth", 0, 0, 1, 0, 0, 0, 0, 0, NULL },
|
|
{ NULL, 0, 0, 1, 0, 0, 0, 0, 0, NULL },
|
|
{ "any", 0, 0, 1, 0, 0, 0, 0, 0, NULL },
|
|
/* The rollover shape is rejected in the client authentication
|
|
* direction too. */
|
|
{ "serverAuth", 0, 1, 1, 0, 0, 0, 0,
|
|
WC_NO_ERR_TRACE(EXTKEYUSE_AUTH_E),
|
|
NULL },
|
|
{ "clientAuth", 0, 1, 1, 0, 0, 0, 0, 0, NULL },
|
|
#endif
|
|
};
|
|
test_eku_fixture fixture;
|
|
WC_RNG rng;
|
|
RsaKey caKey;
|
|
RsaKey midKey;
|
|
RsaKey intKey;
|
|
RsaKey leafKey;
|
|
int rngInit = 0;
|
|
int caInit = 0;
|
|
int midInit = 0;
|
|
int intInit = 0;
|
|
int leafInit = 0;
|
|
word32 idx;
|
|
size_t i;
|
|
|
|
XMEMSET(&fixture, 0, sizeof(fixture));
|
|
ExpectNotNull(fixture.midDer = (byte*)XMALLOC(TEST_EKU_CERT_BUF_SZ, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
ExpectNotNull(fixture.interDer = (byte*)XMALLOC(TEST_EKU_CERT_BUF_SZ, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
ExpectNotNull(fixture.chainDer = (byte*)XMALLOC(TEST_EKU_CERT_BUF_SZ, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
if (EXPECT_SUCCESS()) rngInit = 1;
|
|
ExpectIntEQ(wc_InitRsaKey(&caKey, NULL), 0);
|
|
if (EXPECT_SUCCESS()) caInit = 1;
|
|
idx = 0;
|
|
ExpectIntEQ(wc_RsaPrivateKeyDecode(ca_key_der_2048, &idx, &caKey,
|
|
(word32)sizeof_ca_key_der_2048), 0);
|
|
ExpectIntEQ(wc_InitRsaKey(&midKey, NULL), 0);
|
|
if (EXPECT_SUCCESS()) midInit = 1;
|
|
idx = 0;
|
|
ExpectIntEQ(wc_RsaPrivateKeyDecode(rsa_key_der_2048, &idx, &midKey,
|
|
(word32)sizeof_rsa_key_der_2048), 0);
|
|
ExpectIntEQ(wc_InitRsaKey(&intKey, NULL), 0);
|
|
if (EXPECT_SUCCESS()) intInit = 1;
|
|
idx = 0;
|
|
ExpectIntEQ(wc_RsaPrivateKeyDecode(server_key_der_2048, &idx, &intKey,
|
|
(word32)sizeof_server_key_der_2048), 0);
|
|
ExpectIntEQ(wc_InitRsaKey(&leafKey, NULL), 0);
|
|
if (EXPECT_SUCCESS()) leafInit = 1;
|
|
idx = 0;
|
|
ExpectIntEQ(wc_RsaPrivateKeyDecode(client_key_der_2048, &idx, &leafKey,
|
|
(word32)sizeof_client_key_der_2048), 0);
|
|
|
|
fixture.caKey = &caKey;
|
|
fixture.midKey = &midKey;
|
|
fixture.intKey = &intKey;
|
|
fixture.leafKey = &leafKey;
|
|
fixture.rng = &rng;
|
|
|
|
for (i = 0; i < XELEM_CNT(cases) && EXPECT_SUCCESS(); i++) {
|
|
ExpectIntEQ(test_eku_chain_case(&fixture, &cases[i]), TEST_SUCCESS);
|
|
}
|
|
|
|
if (rngInit) wc_FreeRng(&rng);
|
|
if (caInit) wc_FreeRsaKey(&caKey);
|
|
if (midInit) wc_FreeRsaKey(&midKey);
|
|
if (intInit) wc_FreeRsaKey(&intKey);
|
|
if (leafInit) wc_FreeRsaKey(&leafKey);
|
|
XFREE(fixture.midDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(fixture.interDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(fixture.chainDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WOLFSSL_SMALL_CERT_VERIFY) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \
|
|
!defined(NO_TLS) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \
|
|
defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \
|
|
defined(USE_CERT_BUFFERS_2048) && !defined(IGNORE_KEY_EXTENSIONS)
|
|
|
|
#define TEST_SCV_CERT_BUF_SZ (2 * FOURK_BUF)
|
|
|
|
/* One small certificate verify scenario. */
|
|
typedef struct test_scv_case {
|
|
int forged; /* leaf is signed by a key the root does not hold */
|
|
int expired; /* leaf's validity window lies wholly in the past */
|
|
int expectRet; /* expected handshake result */
|
|
} test_scv_case;
|
|
|
|
/* SetupStoreCtxCallback() stores GetX509Error() under
|
|
* OPENSSL_COMPATIBLE_DEFAULTS. Other builds pass the ASN date error through. */
|
|
#ifdef OPENSSL_COMPATIBLE_DEFAULTS
|
|
#define TEST_SCV_DATE_BEFORE_E WOLFSSL_X509_V_ERR_CERT_NOT_YET_VALID
|
|
#define TEST_SCV_DATE_AFTER_E WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED
|
|
#else
|
|
#define TEST_SCV_DATE_BEFORE_E WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E)
|
|
#define TEST_SCV_DATE_AFTER_E WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)
|
|
#endif
|
|
|
|
/* Stand in for a device with no real-time clock, the deployment the small
|
|
* certificate verify build targets: date errors are the only ones it waives. */
|
|
static int test_scv_date_cb(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
if (store->error == TEST_SCV_DATE_BEFORE_E ||
|
|
store->error == TEST_SCV_DATE_AFTER_E) {
|
|
return 1;
|
|
}
|
|
return preverify;
|
|
}
|
|
|
|
#undef TEST_SCV_DATE_BEFORE_E
|
|
#undef TEST_SCV_DATE_AFTER_E
|
|
|
|
/* Build a TLS leaf naming the 2048-bit test root as its issuer and carrying
|
|
* leafKey's public half. signKey decides whether it is genuine: the root's own
|
|
* key issues it, any other key forges it. Returns the DER length, or < 0. */
|
|
static int test_scv_gen_leaf(byte* out, int outMax, RsaKey* leafKey,
|
|
RsaKey* signKey, WC_RNG* rng, int expired)
|
|
{
|
|
static const byte notBefore[] = { ASN_UTC_TIME, 13,
|
|
'1', '9', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z' };
|
|
static const byte notAfter[] = { ASN_UTC_TIME, 13,
|
|
'2', '0', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z' };
|
|
Cert cert;
|
|
int ret = 0;
|
|
|
|
if (wc_InitCert(&cert) != 0)
|
|
return -1;
|
|
cert.isCA = 0;
|
|
cert.sigType = CTC_SHA256wRSA;
|
|
XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE - 1);
|
|
XSTRNCPY(cert.subject.org, "wolfSSL_test", CTC_NAME_SIZE - 1);
|
|
XSTRNCPY(cert.subject.commonName, "SCV Leaf", CTC_NAME_SIZE - 1);
|
|
if (expired) {
|
|
XMEMCPY(cert.beforeDate, notBefore, sizeof(notBefore));
|
|
cert.beforeDateSz = (int)sizeof(notBefore);
|
|
XMEMCPY(cert.afterDate, notAfter, sizeof(notAfter));
|
|
cert.afterDateSz = (int)sizeof(notAfter);
|
|
}
|
|
if (wc_SetSubjectKeyIdFromPublicKey(&cert, leafKey, NULL) != 0)
|
|
ret = -1;
|
|
if (ret == 0 && wc_SetAuthKeyIdFromCert(&cert, ca_cert_der_2048,
|
|
(int)sizeof_ca_cert_der_2048) != 0)
|
|
ret = -1;
|
|
if (ret == 0 && wc_SetKeyUsage(&cert,
|
|
"digitalSignature,keyEncipherment") != 0)
|
|
ret = -1;
|
|
if (ret == 0 && wc_SetIssuerBuffer(&cert, ca_cert_der_2048,
|
|
(int)sizeof_ca_cert_der_2048) != 0)
|
|
ret = -1;
|
|
if (ret == 0)
|
|
ret = wc_MakeCert(&cert, out, (word32)outMax, leafKey, NULL, rng);
|
|
if (ret >= 0)
|
|
ret = wc_SignCert(cert.bodySz, cert.sigType, out, (word32)outMax,
|
|
signKey, NULL, rng);
|
|
#ifdef WOLFSSL_CERT_GEN_CACHE
|
|
wc_SetCert_Free(&cert);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/* Run a memio handshake in which the server presents the given leaf with the
|
|
* client test key, and the client verifies it against the 2048-bit test root
|
|
* with the date tolerant callback above. */
|
|
static int test_scv_handshake(const byte* leafDer, int leafSz, int* hsRet)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup_ex(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfSSLv23_client_method, wolfSSLv23_server_method,
|
|
(byte*)ca_cert_der_2048, (int)sizeof_ca_cert_der_2048,
|
|
(byte*)leafDer, leafSz,
|
|
(byte*)client_key_der_2048, (int)sizeof_client_key_der_2048), 0);
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, test_scv_date_cb);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
if (test_memio_do_handshake(ssl_c, ssl_s, 10, NULL) == 0)
|
|
*hsRet = 0;
|
|
else
|
|
*hsRet = wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR);
|
|
}
|
|
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Build the leaf for one scenario, confirm what the library's own signature
|
|
* check makes of it, then run the handshake. */
|
|
static int test_scv_case_run(byte* leafDer, RsaKey* leafKey, RsaKey* caKey,
|
|
RsaKey* attackerKey, WC_RNG* rng, const test_scv_case* tc)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CERT_MANAGER* cm = NULL;
|
|
int leafSz = 0;
|
|
int hsRet = -1;
|
|
|
|
ExpectIntGT((leafSz = test_scv_gen_leaf(leafDer, TEST_SCV_CERT_BUF_SZ,
|
|
leafKey, tc->forged ? attackerKey : caKey, rng, tc->expired)), 0);
|
|
|
|
/* Ground truth from the same library: only the signature is looked at. */
|
|
ExpectNotNull(cm = wolfSSL_CertManagerNew());
|
|
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, ca_cert_der_2048,
|
|
(long)sizeof_ca_cert_der_2048, WOLFSSL_FILETYPE_ASN1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wc_CheckCertSignature(leafDer, (word32)leafSz, NULL, cm),
|
|
tc->forged ? WC_NO_ERR_TRACE(ASN_SIG_CONFIRM_E) : 0);
|
|
wolfSSL_CertManagerFree(cm);
|
|
|
|
ExpectIntEQ(test_scv_handshake(leafDer, leafSz, &hsRet), TEST_SUCCESS);
|
|
ExpectIntEQ(hsRet, tc->expectRet);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif /* small certificate verify signature precedence dependencies */
|
|
|
|
/* Test that under WOLFSSL_SMALL_CERT_VERIFY a peer certificate signature
|
|
* failure is reported ahead of the errors a full verify only reaches after the
|
|
* signature has been confirmed.
|
|
*
|
|
* That build checks the signature separately from the parse, so the two results
|
|
* have to be merged afterwards. Reporting the parse result when both failed
|
|
* hands the application a date error for a certificate the trusted CA never
|
|
* signed, and the callback a clockless device installs to waive date errors
|
|
* then completes the handshake against the attacker's key.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_small_cert_verify_sig_error(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_SMALL_CERT_VERIFY) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \
|
|
!defined(NO_TLS) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \
|
|
defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \
|
|
defined(USE_CERT_BUFFERS_2048) && !defined(IGNORE_KEY_EXTENSIONS)
|
|
static const test_scv_case cases[] = {
|
|
/* forged, expired, expected handshake result. */
|
|
|
|
/* A leaf the root did issue verifies. */
|
|
{ 0, 0, 0 },
|
|
/* An expired leaf the root did issue is what the callback exists for,
|
|
* so it must still be waived. */
|
|
{ 0, 1, 0 },
|
|
/* A leaf the root never signed is refused, and the reason the
|
|
* application is given names the signature. */
|
|
{ 1, 0, WC_NO_ERR_TRACE(ASN_SIG_CONFIRM_E) },
|
|
/* The same forgery, also outside its validity window. The date error
|
|
* must not stand in for the signature error, or the callback above
|
|
* waives a certificate the root never signed. */
|
|
{ 1, 1, WC_NO_ERR_TRACE(ASN_SIG_CONFIRM_E) },
|
|
};
|
|
byte* leafDer = NULL;
|
|
WC_RNG rng;
|
|
RsaKey caKey;
|
|
RsaKey attackerKey;
|
|
RsaKey leafKey;
|
|
int rngInit = 0;
|
|
int caInit = 0;
|
|
int attackerInit = 0;
|
|
int leafInit = 0;
|
|
word32 idx;
|
|
size_t i;
|
|
|
|
ExpectNotNull(leafDer = (byte*)XMALLOC(TEST_SCV_CERT_BUF_SZ, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
if (EXPECT_SUCCESS()) rngInit = 1;
|
|
ExpectIntEQ(wc_InitRsaKey(&caKey, NULL), 0);
|
|
if (EXPECT_SUCCESS()) caInit = 1;
|
|
idx = 0;
|
|
ExpectIntEQ(wc_RsaPrivateKeyDecode(ca_key_der_2048, &idx, &caKey,
|
|
(word32)sizeof_ca_key_der_2048), 0);
|
|
/* Any key the root does not hold serves as the attacker's. */
|
|
ExpectIntEQ(wc_InitRsaKey(&attackerKey, NULL), 0);
|
|
if (EXPECT_SUCCESS()) attackerInit = 1;
|
|
idx = 0;
|
|
ExpectIntEQ(wc_RsaPrivateKeyDecode(server_key_der_2048, &idx, &attackerKey,
|
|
(word32)sizeof_server_key_der_2048), 0);
|
|
ExpectIntEQ(wc_InitRsaKey(&leafKey, NULL), 0);
|
|
if (EXPECT_SUCCESS()) leafInit = 1;
|
|
idx = 0;
|
|
ExpectIntEQ(wc_RsaPrivateKeyDecode(client_key_der_2048, &idx, &leafKey,
|
|
(word32)sizeof_client_key_der_2048), 0);
|
|
|
|
for (i = 0; i < XELEM_CNT(cases) && EXPECT_SUCCESS(); i++) {
|
|
ExpectIntEQ(test_scv_case_run(leafDer, &leafKey, &caKey, &attackerKey,
|
|
&rng, &cases[i]), TEST_SUCCESS);
|
|
}
|
|
|
|
if (rngInit) wc_FreeRng(&rng);
|
|
if (caInit) wc_FreeRsaKey(&caKey);
|
|
if (attackerInit) wc_FreeRsaKey(&attackerKey);
|
|
if (leafInit) wc_FreeRsaKey(&leafKey);
|
|
XFREE(leafDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \
|
|
!defined(NO_TLS) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \
|
|
defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \
|
|
defined(WOLFSSL_ALT_NAMES) && defined(USE_CERT_BUFFERS_2048) && \
|
|
defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) && \
|
|
defined(KEEP_PEER_CERT) && !defined(IGNORE_KEY_EXTENSIONS) && \
|
|
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13))
|
|
|
|
#define TEST_CHAIN_BUF_SZ (MAX_X509_SIZE + FOURK_BUF)
|
|
#define TEST_CHAIN_SAN_NAME_SZ 120
|
|
#define TEST_CHAIN_SAN_ENTRY_SZ (TEST_CHAIN_SAN_NAME_SZ + 2)
|
|
|
|
/* Encode nameCnt dNSName entries as the GeneralNames sequence Cert.altNames
|
|
* holds. Returns the length used, or a negative value when it does not fit. */
|
|
static int test_chain_altnames(byte* out, int outSz, int nameCnt)
|
|
{
|
|
int contentSz = nameCnt * TEST_CHAIN_SAN_ENTRY_SZ;
|
|
int idx = 0;
|
|
int i;
|
|
|
|
/* The two-byte length form keeps the header size fixed. */
|
|
if ((contentSz < 256) || (contentSz > 0xFFFF) || (outSz < contentSz + 4)) {
|
|
return BUFFER_E;
|
|
}
|
|
out[idx++] = ASN_SEQUENCE | ASN_CONSTRUCTED;
|
|
out[idx++] = ASN_LONG_LENGTH | 2;
|
|
out[idx++] = (byte)(contentSz >> 8);
|
|
out[idx++] = (byte)(contentSz & 0xFF);
|
|
for (i = 0; i < nameCnt; i++) {
|
|
out[idx++] = ASN_CONTEXT_SPECIFIC | ASN_DNS_TYPE;
|
|
out[idx++] = TEST_CHAIN_SAN_NAME_SZ;
|
|
XMEMSET(out + idx, 'a', TEST_CHAIN_SAN_NAME_SZ);
|
|
idx += TEST_CHAIN_SAN_NAME_SZ;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
/* Build a certificate signed by the given issuer. A non-zero minSz pads the
|
|
* certificate with subject alternative names until its DER reaches that size,
|
|
* which is how a certificate too large for a session chain slot is made.
|
|
* Returns the DER length, or < 0 on failure. */
|
|
static int test_chain_gen_cert(byte* out, int outMax, RsaKey* subjKey,
|
|
const byte* issuerDer, int issuerDerSz, RsaKey* issuerKey, WC_RNG* rng,
|
|
const char* cn, int isCA, int minSz)
|
|
{
|
|
Cert cert;
|
|
int ret = 0;
|
|
int nameCnt = 0;
|
|
int tries;
|
|
|
|
for (tries = 0; tries < 8; tries++) {
|
|
if (wc_InitCert(&cert) != 0) {
|
|
return WOLFSSL_FATAL_ERROR;
|
|
}
|
|
cert.isCA = isCA;
|
|
cert.sigType = CTC_SHA256wRSA;
|
|
XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE - 1);
|
|
XSTRNCPY(cert.subject.org, "wolfSSL_test", CTC_NAME_SIZE - 1);
|
|
XSTRNCPY(cert.subject.commonName, cn, CTC_NAME_SIZE - 1);
|
|
if (nameCnt > 0) {
|
|
ret = test_chain_altnames(cert.altNames, (int)sizeof(cert.altNames),
|
|
nameCnt);
|
|
if (ret < 0) {
|
|
break;
|
|
}
|
|
cert.altNamesSz = ret;
|
|
ret = 0;
|
|
}
|
|
if (wc_SetSubjectKeyIdFromPublicKey(&cert, subjKey, NULL) != 0)
|
|
ret = WOLFSSL_FATAL_ERROR;
|
|
if (ret == 0 &&
|
|
wc_SetAuthKeyIdFromCert(&cert, issuerDer, issuerDerSz) != 0)
|
|
ret = WOLFSSL_FATAL_ERROR;
|
|
if (ret == 0 && wc_SetKeyUsage(&cert, isCA ?
|
|
"keyCertSign,cRLSign" : "digitalSignature,keyEncipherment") != 0)
|
|
ret = WOLFSSL_FATAL_ERROR;
|
|
if (ret == 0 && (!isCA) &&
|
|
wc_SetExtKeyUsage(&cert, "serverAuth,clientAuth") != 0)
|
|
ret = WOLFSSL_FATAL_ERROR;
|
|
if (ret == 0 && wc_SetIssuerBuffer(&cert, issuerDer, issuerDerSz) != 0)
|
|
ret = WOLFSSL_FATAL_ERROR;
|
|
if (ret == 0)
|
|
ret = wc_MakeCert(&cert, out, (word32)outMax, subjKey, NULL, rng);
|
|
if (ret >= 0)
|
|
ret = wc_SignCert(cert.bodySz, cert.sigType, out, (word32)outMax,
|
|
issuerKey, NULL, rng);
|
|
#ifdef WOLFSSL_CERT_GEN_CACHE
|
|
wc_SetCert_Free(&cert);
|
|
#endif
|
|
if ((ret < 0) || (ret >= minSz)) {
|
|
break;
|
|
}
|
|
nameCnt += (minSz - ret + TEST_CHAIN_SAN_ENTRY_SZ - 1) /
|
|
TEST_CHAIN_SAN_ENTRY_SZ + 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* One oversized-certificate scenario. */
|
|
typedef struct test_chain_slot_case {
|
|
int oversizeLeaf; /* the peer's own certificate does not fit a slot */
|
|
int oversizeInter; /* the intermediate CA does not fit a slot */
|
|
int clientPresents; /* client sends the chain and the server reads it */
|
|
} test_chain_slot_case;
|
|
|
|
/* Compare an X509 against the DER it was built from. */
|
|
static int test_chain_is_cert(WOLFSSL_X509* x509, const byte* der, int derSz)
|
|
{
|
|
const byte* got = NULL;
|
|
int gotSz = 0;
|
|
|
|
if (x509 == NULL) {
|
|
return 0;
|
|
}
|
|
got = wolfSSL_X509_get_der(x509, &gotSz);
|
|
|
|
return (got != NULL) && (gotSz == derSz) && (XMEMCMP(got, der, (size_t)derSz) == 0);
|
|
}
|
|
|
|
/* What wolfSSL_X509_STORE_CTX_get_chain() published at index 0, captured from
|
|
* the verify callback where the store carries a session chain. */
|
|
static struct {
|
|
const byte* leafDer;
|
|
int leafSz;
|
|
int ran;
|
|
int leafAtZero;
|
|
} test_chain_store_probe;
|
|
|
|
static int test_chain_store_probe_cb(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|
{
|
|
if ((store != NULL) && (store->sesChain != NULL)) {
|
|
WOLF_STACK_OF(WOLFSSL_X509)* sk =
|
|
wolfSSL_X509_STORE_CTX_get_chain(store);
|
|
|
|
test_chain_store_probe.ran = 1;
|
|
test_chain_store_probe.leafAtZero = (sk == NULL) ? -1 :
|
|
test_chain_is_cert(wolfSSL_sk_X509_value(sk, 0),
|
|
test_chain_store_probe.leafDer, test_chain_store_probe.leafSz);
|
|
}
|
|
return preverify;
|
|
}
|
|
|
|
#if defined(HAVE_EXT_CACHE) && !defined(NO_SESSION_CACHE)
|
|
/* Carry the client's session through i2d/d2i and onto a fresh object, which
|
|
* has no kept copy of the peer and must answer from the chain alone. */
|
|
static int test_chain_session_carry(const test_chain_slot_case* tc,
|
|
WOLFSSL_CTX* ctx_c, WOLFSSL* ssl, const byte* leafDer, int leafSz,
|
|
int interSz)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
WOLFSSL* fresh = NULL;
|
|
WOLFSSL_X509* peer = NULL;
|
|
byte* sessDer = NULL;
|
|
const byte* ptr = NULL;
|
|
int sz = 0;
|
|
|
|
ExpectIntGT((sz = wolfSSL_i2d_SSL_SESSION(ssl->session, &sessDer)), 0);
|
|
ptr = sessDer;
|
|
ExpectNotNull(sess = wolfSSL_d2i_SSL_SESSION(NULL, &ptr, sz));
|
|
if (sess != NULL) {
|
|
ExpectIntEQ(sess->chain.count, 2);
|
|
ExpectIntEQ(sess->chain.certs[0].length,
|
|
tc->oversizeLeaf ? 0 : leafSz);
|
|
ExpectIntEQ(sess->chain.certs[1].length,
|
|
tc->oversizeInter ? 0 : interSz);
|
|
}
|
|
|
|
/* A TLS 1.3 client has nothing to resume until a ticket arrives. */
|
|
if (wolfSSL_SESSION_is_resumable(sess)) {
|
|
ExpectNotNull(fresh = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(wolfSSL_set_session(fresh, sess), WOLFSSL_SUCCESS);
|
|
peer = wolfSSL_get_peer_certificate(fresh);
|
|
if (tc->oversizeLeaf) {
|
|
ExpectNull(peer);
|
|
}
|
|
else {
|
|
ExpectIntEQ(test_chain_is_cert(peer, leafDer, leafSz), 1);
|
|
}
|
|
}
|
|
|
|
wolfSSL_X509_free(peer);
|
|
wolfSSL_free(fresh);
|
|
wolfSSL_SESSION_free(sess);
|
|
XFREE(sessDer, NULL, DYNAMIC_TYPE_OPENSSL);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* Present [leaf, intermediate] to a client that trusts the test root and check
|
|
* which certificate each compatibility-layer chain getter names as the peer. */
|
|
static int test_chain_slot_case_run(const test_chain_slot_case* tc,
|
|
method_provider method_c, method_provider method_s, RsaKey* caKey,
|
|
RsaKey* interKey, RsaKey* leafKey, WC_RNG* rng, byte* interDer,
|
|
byte* leafDer, byte* chainDer)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
WOLF_STACK_OF(WOLFSSL_X509)* sk = NULL;
|
|
WOLFSSL_X509* peer = NULL;
|
|
WOLFSSL_X509* slotX509 = NULL;
|
|
int interSz = 0;
|
|
int leafSz = 0;
|
|
int pemSz = 0;
|
|
int i;
|
|
|
|
ExpectIntGT((interSz = test_chain_gen_cert(interDer, TEST_CHAIN_BUF_SZ,
|
|
interKey, ca_cert_der_2048, (int)sizeof_ca_cert_der_2048, caKey, rng,
|
|
"Slot Intermediate", 1,
|
|
tc->oversizeInter ? MAX_X509_SIZE : 0)), 0);
|
|
ExpectIntGT((leafSz = test_chain_gen_cert(leafDer, TEST_CHAIN_BUF_SZ,
|
|
leafKey, interDer, interSz, interKey, rng, "Slot Leaf", 0,
|
|
tc->oversizeLeaf ? MAX_X509_SIZE : 0)), 0);
|
|
ExpectIntEQ(interSz >= MAX_X509_SIZE, tc->oversizeInter);
|
|
ExpectIntEQ(leafSz >= MAX_X509_SIZE, tc->oversizeLeaf);
|
|
ExpectIntLE(leafSz + interSz, TEST_CHAIN_BUF_SZ);
|
|
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(chainDer, leafDer, (size_t)leafSz);
|
|
XMEMCPY(chainDer + leafSz, interDer, (size_t)interSz);
|
|
}
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
/* The server presents the generated chain either way, so one set of
|
|
* credentials covers both directions. */
|
|
ExpectIntEQ(test_memio_setup_ex(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
method_c, method_s,
|
|
(byte*)ca_cert_der_2048, (int)sizeof_ca_cert_der_2048,
|
|
chainDer, leafSz + interSz,
|
|
(byte*)client_key_der_2048, (int)sizeof_client_key_der_2048), 0);
|
|
XMEMSET(&test_chain_store_probe, 0, sizeof(test_chain_store_probe));
|
|
test_chain_store_probe.leafDer = leafDer;
|
|
test_chain_store_probe.leafSz = leafSz;
|
|
if (tc->clientPresents) {
|
|
/* Only the server's view of the client chain is under test. */
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_buffer_format(ssl_c, chainDer,
|
|
(long)(leafSz + interSz), WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_buffer(ssl_c, client_key_der_2048,
|
|
(long)sizeof_client_key_der_2048, WOLFSSL_FILETYPE_ASN1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_buffer(ctx_s, ca_cert_der_2048,
|
|
(long)sizeof_ca_cert_der_2048, WOLFSSL_FILETYPE_ASN1),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, test_chain_store_probe_cb);
|
|
ssl = ssl_s;
|
|
}
|
|
else {
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER,
|
|
test_chain_store_probe_cb);
|
|
ssl = ssl_c;
|
|
}
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_get_verify_result(ssl), WOLFSSL_X509_V_OK);
|
|
|
|
/* The handshake verifies and keeps the real peer certificate whatever its
|
|
* size, so it is what every chain getter has to agree with. */
|
|
ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl));
|
|
ExpectIntEQ(test_chain_is_cert(peer, leafDer, leafSz), 1);
|
|
|
|
/* A certificate too large for its slot must not let the one behind it take
|
|
* its index, which is what this getter reads as the peer. */
|
|
ExpectNotNull(sk = wolfSSL_get0_verified_chain(ssl));
|
|
ExpectIntEQ(test_chain_is_cert(wolfSSL_sk_X509_value(sk, 0), leafDer,
|
|
leafSz), 1);
|
|
|
|
ExpectIntEQ(wolfSSL_get_chain_count(wolfSSL_get_peer_chain(ssl)), 2);
|
|
|
|
/* The store-context getter reads the same positional chain, so it must
|
|
* name the same peer: the leaf, or nothing when the leaf was dropped. */
|
|
ExpectIntEQ(test_chain_store_probe.ran, 1);
|
|
ExpectIntEQ(test_chain_store_probe.leafAtZero, tc->oversizeLeaf ? -1 : 1);
|
|
|
|
/* An empty slot answers that it holds no certificate rather than handing
|
|
* out whatever its buffer happens to contain. */
|
|
for (i = 0; i < 2; i++) {
|
|
int empty = (i == 0) ? tc->oversizeLeaf : tc->oversizeInter;
|
|
|
|
ExpectIntEQ(wolfSSL_get_chain_length(
|
|
wolfSSL_get_peer_chain(ssl), i) == 0, empty);
|
|
ExpectIntEQ(wolfSSL_get_chain_cert(
|
|
wolfSSL_get_peer_chain(ssl), i) == NULL, empty);
|
|
slotX509 = wolfSSL_get_chain_X509(wolfSSL_get_peer_chain(ssl), i);
|
|
ExpectIntEQ(slotX509 == NULL, empty);
|
|
if (!empty) {
|
|
ExpectIntEQ(test_chain_is_cert(slotX509, (i == 0) ? leafDer :
|
|
interDer, (i == 0) ? leafSz : interSz), 1);
|
|
}
|
|
wolfSSL_X509_free(slotX509);
|
|
slotX509 = NULL;
|
|
ExpectIntEQ(wolfSSL_get_chain_cert_pem(wolfSSL_get_peer_chain(ssl), i,
|
|
NULL, 0, &pemSz), empty ? WC_NO_ERR_TRACE(BAD_FUNC_ARG) :
|
|
WC_NO_ERR_TRACE(LENGTH_ONLY_E));
|
|
}
|
|
|
|
/* The server moves the peer's own certificate out of the stack and keeps
|
|
* it as the session peer, so what is left there starts at the issuer. A
|
|
* dropped issuer is left out of the stack, as it was before it had a
|
|
* slot. */
|
|
ExpectNotNull(sk = wolfSSL_get_peer_cert_chain(ssl));
|
|
ExpectIntEQ(wolfSSL_sk_X509_num(sk),
|
|
(tc->clientPresents ? 0 : 1) + (tc->oversizeInter ? 0 : 1));
|
|
if (tc->clientPresents) {
|
|
ExpectNotNull(ssl_s);
|
|
if (ssl_s != NULL) {
|
|
ExpectIntEQ(test_chain_is_cert(ssl_s->session->peer, leafDer,
|
|
leafSz), 1);
|
|
}
|
|
if (!tc->oversizeInter) {
|
|
ExpectIntEQ(test_chain_is_cert(wolfSSL_sk_X509_value(sk, 0),
|
|
interDer, interSz), 1);
|
|
}
|
|
}
|
|
else {
|
|
ExpectIntEQ(test_chain_is_cert(wolfSSL_sk_X509_value(sk, 0), leafDer,
|
|
leafSz), 1);
|
|
}
|
|
|
|
/* Without access to the kept copy this one can only fail closed. On the
|
|
* server the session peer was set above and is answered from there. */
|
|
if (tc->oversizeLeaf && (!tc->clientPresents)) {
|
|
ExpectNull(wolfSSL_SESSION_get0_peer(ssl->session));
|
|
}
|
|
else {
|
|
ExpectIntEQ(test_chain_is_cert(wolfSSL_SESSION_get0_peer(ssl->session),
|
|
leafDer, leafSz), 1);
|
|
}
|
|
|
|
#if defined(HAVE_EXT_CACHE) && !defined(NO_SESSION_CACHE)
|
|
if (!tc->clientPresents) {
|
|
ExpectIntEQ(test_chain_session_carry(tc, ctx_c, ssl, leafDer, leafSz,
|
|
interSz), TEST_SUCCESS);
|
|
}
|
|
#endif
|
|
|
|
wolfSSL_X509_free(peer);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif /* session chain slot test dependencies */
|
|
|
|
/* Test that a certificate too large for a session chain slot does not let the
|
|
* certificate behind it be reported as the peer.
|
|
*
|
|
* The wire limit applies to the certificate list as a whole, so a single
|
|
* certificate of MAX_X509_SIZE bytes or more is accepted and verified while
|
|
* being too large for the session chain. The compatibility-layer getters read
|
|
* that chain positionally, taking index 0 to be the peer's own certificate, so
|
|
* dropping an entry there made them name the issuing CA as the peer.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_session_chain_oversized_cert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \
|
|
!defined(NO_TLS) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \
|
|
defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \
|
|
defined(WOLFSSL_ALT_NAMES) && defined(USE_CERT_BUFFERS_2048) && \
|
|
defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) && \
|
|
defined(KEEP_PEER_CERT) && !defined(IGNORE_KEY_EXTENSIONS) && \
|
|
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13))
|
|
static const test_chain_slot_case cases[] = {
|
|
/* oversizeLeaf, oversizeInter, clientPresents */
|
|
{ 0, 0, 0 }, /* control: both fit */
|
|
{ 1, 0, 0 }, /* the peer's own certificate is dropped */
|
|
{ 0, 1, 0 }, /* an issuer is dropped, the peer still fits */
|
|
#ifndef WOLFSSL_NO_CLIENT_AUTH
|
|
/* The same in the client authentication direction, where an
|
|
* application decides what the peer is allowed to do. */
|
|
{ 0, 0, 1 },
|
|
{ 1, 0, 1 },
|
|
{ 0, 1, 1 },
|
|
#endif
|
|
};
|
|
/* The chain is recorded by ProcessPeerCerts(), which every Certificate
|
|
* handler goes through, datagram transports included. */
|
|
static const struct {
|
|
method_provider c;
|
|
method_provider s;
|
|
} methods[] = {
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
{ wolfTLSv1_2_client_method, wolfTLSv1_2_server_method },
|
|
#endif
|
|
#ifdef WOLFSSL_TLS13
|
|
{ wolfTLSv1_3_client_method, wolfTLSv1_3_server_method },
|
|
#endif
|
|
#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12)
|
|
{ wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method },
|
|
#endif
|
|
#ifdef WOLFSSL_DTLS13
|
|
{ wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method },
|
|
#endif
|
|
};
|
|
WC_RNG rng;
|
|
RsaKey caKey;
|
|
RsaKey interKey;
|
|
RsaKey leafKey;
|
|
byte* interDer = NULL;
|
|
byte* leafDer = NULL;
|
|
byte* chainDer = NULL;
|
|
int rngInit = 0;
|
|
int caInit = 0;
|
|
int interInit = 0;
|
|
int leafInit = 0;
|
|
word32 idx;
|
|
size_t i;
|
|
size_t m;
|
|
|
|
ExpectNotNull(interDer = (byte*)XMALLOC(TEST_CHAIN_BUF_SZ, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
ExpectNotNull(leafDer = (byte*)XMALLOC(TEST_CHAIN_BUF_SZ, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
ExpectNotNull(chainDer = (byte*)XMALLOC(2 * TEST_CHAIN_BUF_SZ, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
if (EXPECT_SUCCESS()) rngInit = 1;
|
|
ExpectIntEQ(wc_InitRsaKey(&caKey, NULL), 0);
|
|
if (EXPECT_SUCCESS()) caInit = 1;
|
|
idx = 0;
|
|
ExpectIntEQ(wc_RsaPrivateKeyDecode(ca_key_der_2048, &idx, &caKey,
|
|
(word32)sizeof_ca_key_der_2048), 0);
|
|
ExpectIntEQ(wc_InitRsaKey(&interKey, NULL), 0);
|
|
if (EXPECT_SUCCESS()) interInit = 1;
|
|
idx = 0;
|
|
ExpectIntEQ(wc_RsaPrivateKeyDecode(server_key_der_2048, &idx, &interKey,
|
|
(word32)sizeof_server_key_der_2048), 0);
|
|
ExpectIntEQ(wc_InitRsaKey(&leafKey, NULL), 0);
|
|
if (EXPECT_SUCCESS()) leafInit = 1;
|
|
idx = 0;
|
|
ExpectIntEQ(wc_RsaPrivateKeyDecode(client_key_der_2048, &idx, &leafKey,
|
|
(word32)sizeof_client_key_der_2048), 0);
|
|
|
|
for (m = 0; m < XELEM_CNT(methods) && EXPECT_SUCCESS(); m++) {
|
|
for (i = 0; i < XELEM_CNT(cases) && EXPECT_SUCCESS(); i++) {
|
|
ExpectIntEQ(test_chain_slot_case_run(&cases[i], methods[m].c,
|
|
methods[m].s, &caKey, &interKey, &leafKey, &rng, interDer,
|
|
leafDer, chainDer), TEST_SUCCESS);
|
|
}
|
|
}
|
|
|
|
if (rngInit) wc_FreeRng(&rng);
|
|
if (caInit) wc_FreeRsaKey(&caKey);
|
|
if (interInit) wc_FreeRsaKey(&interKey);
|
|
if (leafInit) wc_FreeRsaKey(&leafKey);
|
|
XFREE(interDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(leafDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(chainDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \
|
|
!defined(NO_TLS) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \
|
|
defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \
|
|
defined(WOLFSSL_ALT_NAMES) && defined(USE_CERT_BUFFERS_2048) && \
|
|
defined(SESSION_CERTS) && defined(OPENSSL_ALL) && \
|
|
defined(KEEP_PEER_CERT) && !defined(IGNORE_KEY_EXTENSIONS) && \
|
|
defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(WOLFSSL_TICKET_HAVE_ID) && !defined(NO_CERT_IN_TICKET) && \
|
|
!defined(NO_SESSION_CACHE) && !defined(WOLFSSL_NO_CLIENT_AUTH)
|
|
|
|
/* Authenticate a client holding [leaf, intermediate] over TLS 1.3 with
|
|
* stateful tickets, then resume. The resumed handshake carries no Certificate
|
|
* message, so the peer certificate the server ends up with is whatever the
|
|
* ticket recorded. Returns that certificate through peerDer/peerDerSz. */
|
|
static int test_chain_ticket_case_run(int oversizeLeaf, RsaKey* caKey,
|
|
RsaKey* interKey, RsaKey* leafKey, WC_RNG* rng, byte* interDer,
|
|
byte* leafDer, byte* chainDer, const byte** peerDer, int* peerDerSz,
|
|
int* leafSzOut, int* interSzOut)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
WOLFSSL_X509* peer = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char buf[64];
|
|
int interSz = 0;
|
|
int leafSz = 0;
|
|
|
|
*peerDer = NULL;
|
|
*peerDerSz = 0;
|
|
|
|
ExpectIntGT((interSz = test_chain_gen_cert(interDer, TEST_CHAIN_BUF_SZ,
|
|
interKey, ca_cert_der_2048, (int)sizeof_ca_cert_der_2048, caKey, rng,
|
|
"Ticket Intermediate", 1, 0)), 0);
|
|
ExpectIntGT((leafSz = test_chain_gen_cert(leafDer, TEST_CHAIN_BUF_SZ,
|
|
leafKey, interDer, interSz, interKey, rng, "Ticket Leaf", 0,
|
|
oversizeLeaf ? MAX_X509_SIZE : 0)), 0);
|
|
ExpectIntEQ(leafSz >= MAX_X509_SIZE, oversizeLeaf);
|
|
/* The ticket only records a certificate that fits its own limit, so the
|
|
* intermediate has to be within it for the shift to be observable. */
|
|
ExpectIntLE(interSz, MAX_TICKET_PEER_CERT_SZ);
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(chainDer, leafDer, (size_t)leafSz);
|
|
XMEMCPY(chainDer + leafSz, interDer, (size_t)interSz);
|
|
}
|
|
*leafSzOut = leafSz;
|
|
*interSzOut = interSz;
|
|
|
|
/* First connection: the client authenticates with the generated chain. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup_ex(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
(byte*)ca_cert_der_2048, (int)sizeof_ca_cert_der_2048,
|
|
chainDer, leafSz + interSz,
|
|
(byte*)client_key_der_2048, (int)sizeof_client_key_der_2048), 0);
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectIntEQ(wolfSSL_use_certificate_chain_buffer_format(ssl_c, chainDer,
|
|
(long)(leafSz + interSz), WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_buffer(ssl_c, client_key_der_2048,
|
|
(long)sizeof_client_key_der_2048, WOLFSSL_FILETYPE_ASN1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_buffer(ctx_s, ca_cert_der_2048,
|
|
(long)sizeof_ca_cert_der_2048, WOLFSSL_FILETYPE_ASN1),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
/* A ticket that is only a session identifier, resolved from the server's
|
|
* own cache on resumption. */
|
|
ExpectTrue(wolfSSL_set_options(ssl_s, WOLFSSL_OP_NO_TICKET) != 0);
|
|
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* The handshake itself still sees the real client certificate. */
|
|
ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_s));
|
|
ExpectIntEQ(test_chain_is_cert(peer, leafDer, leafSz), 1);
|
|
wolfSSL_X509_free(peer);
|
|
peer = NULL;
|
|
|
|
/* Let the client take delivery of the NewSessionTicket. */
|
|
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
|
|
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
ExpectIntEQ(wolfSSL_SessionIsSetup(sess), 1);
|
|
if (sess != NULL) {
|
|
ExpectIntEQ(sess->ticketLen, ID_LEN);
|
|
}
|
|
|
|
wolfSSL_free(ssl_c); ssl_c = NULL;
|
|
wolfSSL_free(ssl_s); ssl_s = NULL;
|
|
|
|
/* Second connection: resume. The client sends no certificate, so the
|
|
* server's peer certificate comes entirely from the ticket. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup_ex(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method,
|
|
(byte*)ca_cert_der_2048, (int)sizeof_ca_cert_der_2048,
|
|
chainDer, leafSz + interSz,
|
|
(byte*)client_key_der_2048, (int)sizeof_client_key_der_2048), 0);
|
|
wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_NONE, NULL);
|
|
ExpectTrue(wolfSSL_set_options(ssl_s, WOLFSSL_OP_NO_TICKET) != 0);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectTrue(wolfSSL_session_reused(ssl_s));
|
|
|
|
peer = wolfSSL_get_peer_certificate(ssl_s);
|
|
if (peer != NULL) {
|
|
*peerDer = wolfSSL_X509_get_der(peer, peerDerSz);
|
|
}
|
|
/* Copy out before the object goes away. */
|
|
if ((*peerDer != NULL) && (*peerDerSz > 0) &&
|
|
(*peerDerSz <= TEST_CHAIN_BUF_SZ)) {
|
|
XMEMCPY(chainDer, *peerDer, (size_t)*peerDerSz);
|
|
*peerDer = chainDer;
|
|
}
|
|
else {
|
|
*peerDer = NULL;
|
|
*peerDerSz = 0;
|
|
}
|
|
wolfSSL_X509_free(peer);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif /* session ticket peer certificate test dependencies */
|
|
|
|
/* Test that a resumed session does not report the issuing CA as the peer.
|
|
*
|
|
* A TLS 1.3 stateful ticket records chain.certs[0] as the peer certificate,
|
|
* and the resumed handshake rebuilds the server's peer certificate from it
|
|
* without the client sending one. With the peer's own certificate dropped from
|
|
* the chain for size, that recorded certificate was the issuing CA, so on a
|
|
* resumed connection even wolfSSL_get_peer_certificate() named the CA.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_session_ticket_oversized_peer_cert(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \
|
|
!defined(NO_TLS) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \
|
|
defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \
|
|
defined(WOLFSSL_ALT_NAMES) && defined(USE_CERT_BUFFERS_2048) && \
|
|
defined(SESSION_CERTS) && defined(OPENSSL_ALL) && \
|
|
defined(KEEP_PEER_CERT) && !defined(IGNORE_KEY_EXTENSIONS) && \
|
|
defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
|
|
defined(WOLFSSL_TICKET_HAVE_ID) && !defined(NO_CERT_IN_TICKET) && \
|
|
!defined(NO_SESSION_CACHE) && !defined(WOLFSSL_NO_CLIENT_AUTH)
|
|
WC_RNG rng;
|
|
RsaKey caKey;
|
|
RsaKey interKey;
|
|
RsaKey leafKey;
|
|
byte* interDer = NULL;
|
|
byte* leafDer = NULL;
|
|
byte* chainDer = NULL;
|
|
const byte* peerDer = NULL;
|
|
int peerDerSz = 0;
|
|
int leafSz = 0;
|
|
int interSz = 0;
|
|
int rngInit = 0;
|
|
int caInit = 0;
|
|
int interInit = 0;
|
|
int leafInit = 0;
|
|
word32 idx;
|
|
|
|
ExpectNotNull(interDer = (byte*)XMALLOC(TEST_CHAIN_BUF_SZ, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
ExpectNotNull(leafDer = (byte*)XMALLOC(TEST_CHAIN_BUF_SZ, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
ExpectNotNull(chainDer = (byte*)XMALLOC(2 * TEST_CHAIN_BUF_SZ, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
if (EXPECT_SUCCESS()) rngInit = 1;
|
|
ExpectIntEQ(wc_InitRsaKey(&caKey, NULL), 0);
|
|
if (EXPECT_SUCCESS()) caInit = 1;
|
|
idx = 0;
|
|
ExpectIntEQ(wc_RsaPrivateKeyDecode(ca_key_der_2048, &idx, &caKey,
|
|
(word32)sizeof_ca_key_der_2048), 0);
|
|
ExpectIntEQ(wc_InitRsaKey(&interKey, NULL), 0);
|
|
if (EXPECT_SUCCESS()) interInit = 1;
|
|
idx = 0;
|
|
ExpectIntEQ(wc_RsaPrivateKeyDecode(server_key_der_2048, &idx, &interKey,
|
|
(word32)sizeof_server_key_der_2048), 0);
|
|
ExpectIntEQ(wc_InitRsaKey(&leafKey, NULL), 0);
|
|
if (EXPECT_SUCCESS()) leafInit = 1;
|
|
idx = 0;
|
|
ExpectIntEQ(wc_RsaPrivateKeyDecode(client_key_der_2048, &idx, &leafKey,
|
|
(word32)sizeof_client_key_der_2048), 0);
|
|
|
|
/* Control: the peer's own certificate fits, so the ticket records it and
|
|
* the resumed connection names it. */
|
|
ExpectIntEQ(test_chain_ticket_case_run(0, &caKey, &interKey, &leafKey,
|
|
&rng, interDer, leafDer, chainDer, &peerDer, &peerDerSz, &leafSz,
|
|
&interSz), TEST_SUCCESS);
|
|
ExpectNotNull(peerDer);
|
|
ExpectIntEQ(peerDerSz, leafSz);
|
|
if ((peerDer != NULL) && (peerDerSz == leafSz)) {
|
|
ExpectIntEQ(XMEMCMP(peerDer, leafDer, (size_t)leafSz), 0);
|
|
}
|
|
|
|
/* The peer's own certificate does not fit. The ticket must not record the
|
|
* issuing CA in its place; with no certificate to record it holds none. */
|
|
ExpectIntEQ(test_chain_ticket_case_run(1, &caKey, &interKey, &leafKey,
|
|
&rng, interDer, leafDer, chainDer, &peerDer, &peerDerSz, &leafSz,
|
|
&interSz), TEST_SUCCESS);
|
|
ExpectIntNE(peerDerSz, interSz);
|
|
ExpectNull(peerDer);
|
|
|
|
if (rngInit) wc_FreeRng(&rng);
|
|
if (caInit) wc_FreeRsaKey(&caKey);
|
|
if (interInit) wc_FreeRsaKey(&interKey);
|
|
if (leafInit) wc_FreeRsaKey(&leafKey);
|
|
XFREE(interDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(leafDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(chainDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \
|
|
defined(HAVE_ECC) && defined(SESSION_CERTS) && \
|
|
defined(KEEP_PEER_CERT) && defined(OPENSSL_EXTRA) && \
|
|
((defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) && \
|
|
!defined(WOLFSSL_NO_CLIENT_AUTH)) || \
|
|
(defined(HAVE_SESSION_TICKET) && !defined(NO_SESSION_CACHE) && \
|
|
(defined(WOLFSSL_TLS13) || !defined(WOLFSSL_NO_TLS12))))
|
|
/* Retry an application data read or write that an async wait interrupted. */
|
|
static int test_chain_io(WOLFSSL* ssl, int doWrite, char* buf, int sz)
|
|
{
|
|
int ret;
|
|
int tries = 0;
|
|
|
|
do {
|
|
ret = doWrite ? wolfSSL_write(ssl, buf, sz) :
|
|
wolfSSL_read(ssl, buf, sz);
|
|
} while ((ret < 0) && (++tries < 100) &&
|
|
(wolfSSL_get_error(ssl, ret) == WC_NO_ERR_TRACE(WC_PENDING_E)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Whether an X509 holds the given DER. */
|
|
static int test_chain_x509_is(WOLFSSL_X509* x509, const byte* der, int derSz)
|
|
{
|
|
const byte* got = NULL;
|
|
int gotSz = 0;
|
|
|
|
if (x509 != NULL) {
|
|
got = wolfSSL_X509_get_der(x509, &gotSz);
|
|
}
|
|
|
|
return (got != NULL) && (gotSz == derSz) &&
|
|
(XMEMCMP(got, der, (size_t)derSz) == 0);
|
|
}
|
|
|
|
/* Whether the session chain, and the views built from it, name the peer's
|
|
* certificate alone. */
|
|
static int test_chain_holds_only_peer(WOLFSSL* ssl)
|
|
{
|
|
WOLFSSL_X509_CHAIN* chain = wolfSSL_get_peer_chain(ssl);
|
|
WOLFSSL_X509* peer = wolfSSL_get_peer_certificate(ssl);
|
|
const byte* der = NULL;
|
|
int derSz = 0;
|
|
int ret = 0;
|
|
|
|
if ((peer != NULL) && (wolfSSL_get_chain_count(chain) == 1)) {
|
|
der = wolfSSL_X509_get_der(peer, &derSz);
|
|
ret = (der != NULL) && (derSz == wolfSSL_get_chain_length(chain, 0)) &&
|
|
(XMEMCMP(der, wolfSSL_get_chain_cert(chain, 0),
|
|
(size_t)derSz) == 0);
|
|
}
|
|
/* Before the stack: building it on a server replaces the session peer. */
|
|
if (ret) {
|
|
ret = test_chain_x509_is(wolfSSL_SESSION_get0_peer(ssl->session), der,
|
|
derSz);
|
|
}
|
|
if (ret && (ssl->options.side == WOLFSSL_CLIENT_END)) {
|
|
ret = test_chain_x509_is(wolfSSL_sk_X509_value(
|
|
wolfSSL_get_peer_cert_chain(ssl), 0), der, derSz);
|
|
}
|
|
wolfSSL_X509_free(peer);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/* Test that each TLS 1.3 post-handshake authentication replaces the session
|
|
* chain rather than appending to it, and that a declined one keeps it.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_session_chain_tls13_pha(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \
|
|
defined(HAVE_ECC) && defined(WOLFSSL_TLS13) && defined(SESSION_CERTS) && \
|
|
defined(KEEP_PEER_CERT) && defined(OPENSSL_EXTRA) && \
|
|
defined(WOLFSSL_POST_HANDSHAKE_AUTH) && !defined(WOLFSSL_NO_CLIENT_AUTH)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char msg[] = "ping";
|
|
char buf[sizeof(msg)];
|
|
int i;
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_allow_post_handshake_auth(ssl_c), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliEccCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
|
|
/* A different identity each round, so a stale first slot shows. */
|
|
for (i = 0; i < 2; i++) {
|
|
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c,
|
|
(i == 0) ? cliCertFile : cliEccCertFile, WOLFSSL_FILETYPE_PEM),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c,
|
|
(i == 0) ? cliKeyFile : cliEccKeyFile, WOLFSSL_FILETYPE_PEM),
|
|
WOLFSSL_SUCCESS);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
|
|
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_chain_io(ssl_s, 1, msg, (int)sizeof(msg)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(test_chain_io(ssl_c, 0, buf, (int)sizeof(buf)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(test_chain_io(ssl_c, 1, msg, (int)sizeof(msg)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(test_chain_io(ssl_s, 0, buf, (int)sizeof(buf)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(test_chain_holds_only_peer(ssl_s), 1);
|
|
}
|
|
|
|
/* An empty Certificate leaves the identity verified last in place. */
|
|
ExpectIntEQ(wolfSSL_UnloadCertsKeys(ssl_c), WOLFSSL_SUCCESS);
|
|
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER, NULL);
|
|
ExpectIntEQ(wolfSSL_request_certificate(ssl_s), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_chain_io(ssl_s, 1, msg, (int)sizeof(msg)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(test_chain_io(ssl_c, 0, buf, (int)sizeof(buf)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(test_chain_io(ssl_c, 1, msg, (int)sizeof(msg)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(test_chain_io(ssl_s, 0, buf, (int)sizeof(buf)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(test_chain_holds_only_peer(ssl_s), 1);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \
|
|
defined(HAVE_ECC) && defined(SESSION_CERTS) && \
|
|
defined(KEEP_PEER_CERT) && defined(OPENSSL_EXTRA) && \
|
|
defined(HAVE_SESSION_TICKET) && !defined(NO_SESSION_CACHE) && \
|
|
(defined(WOLFSSL_TLS13) || !defined(WOLFSSL_NO_TLS12))
|
|
static int test_chain_resume_declined_run(method_provider method_c,
|
|
method_provider method_s)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
char msg[] = "ping";
|
|
char buf[sizeof(msg)];
|
|
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
method_c, method_s), 0);
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, caEccCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
/* Let the client take delivery of the NewSessionTicket. */
|
|
ExpectIntEQ(test_chain_io(ssl_s, 1, msg, (int)sizeof(msg)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(test_chain_io(ssl_c, 0, buf, (int)sizeof(buf)),
|
|
(int)sizeof(msg));
|
|
ExpectIntEQ(test_chain_holds_only_peer(ssl_c), 1);
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
|
|
wolfSSL_free(ssl_c);
|
|
ssl_c = NULL;
|
|
wolfSSL_free(ssl_s);
|
|
ssl_s = NULL;
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ctx_s = NULL;
|
|
|
|
/* A server that cannot resume the session and has another certificate. */
|
|
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
|
|
ExpectNotNull(ctx_s = wolfSSL_CTX_new(method_s()));
|
|
ExpectIntEQ(wolfSSL_CTX_set_session_cache_mode(ctx_s,
|
|
WOLFSSL_SESS_CACHE_OFF), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_s, eccCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, eccKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
|
|
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
|
|
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
|
|
method_c, method_s), 0);
|
|
ExpectIntEQ(wolfSSL_UseSessionTicket(ssl_c), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0);
|
|
ExpectIntEQ(test_chain_holds_only_peer(ssl_c), 1);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
/* Test that a full handshake after a declined resumption does not keep the
|
|
* chain of the session that was offered.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_session_chain_resume_declined(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \
|
|
defined(HAVE_ECC) && defined(SESSION_CERTS) && \
|
|
defined(KEEP_PEER_CERT) && defined(OPENSSL_EXTRA) && \
|
|
defined(HAVE_SESSION_TICKET) && !defined(NO_SESSION_CACHE) && \
|
|
(defined(WOLFSSL_TLS13) || !defined(WOLFSSL_NO_TLS12))
|
|
#ifdef WOLFSSL_TLS13
|
|
ExpectIntEQ(test_chain_resume_declined_run(wolfTLSv1_3_client_method,
|
|
wolfTLSv1_3_server_method), TEST_SUCCESS);
|
|
#endif
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
ExpectIntEQ(test_chain_resume_declined_run(wolfTLSv1_2_client_method,
|
|
wolfTLSv1_2_server_method), TEST_SUCCESS);
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Test that a session chain holding MAX_CHAIN_DEPTH certificates still names
|
|
* its peer.
|
|
*
|
|
* @return TEST_SUCCESS on success.
|
|
*/
|
|
int test_wolfSSL_SESSION_get0_peer_full_chain(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(OPENSSL_EXTRA) && defined(SESSION_CERTS) && \
|
|
defined(USE_CERT_BUFFERS_2048) && !defined(NO_RSA)
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
WOLFSSL_X509* peer = NULL;
|
|
const byte* der = NULL;
|
|
int derSz = 0;
|
|
int i;
|
|
|
|
ExpectIntLT(sizeof_server_cert_der_2048, MAX_X509_SIZE);
|
|
ExpectNotNull(sess = wolfSSL_SESSION_new());
|
|
if (EXPECT_SUCCESS()) {
|
|
for (i = 0; i < MAX_CHAIN_DEPTH; i++) {
|
|
XMEMCPY(sess->chain.certs[i].buffer, server_cert_der_2048,
|
|
(size_t)sizeof_server_cert_der_2048);
|
|
sess->chain.certs[i].length = sizeof_server_cert_der_2048;
|
|
}
|
|
sess->chain.count = MAX_CHAIN_DEPTH;
|
|
}
|
|
ExpectNotNull(peer = wolfSSL_SESSION_get0_peer(sess));
|
|
ExpectNotNull(der = wolfSSL_X509_get_der(peer, &derSz));
|
|
ExpectIntEQ(derSz, sizeof_server_cert_der_2048);
|
|
ExpectBufEQ(der, server_cert_der_2048, sizeof_server_cert_der_2048);
|
|
|
|
wolfSSL_SESSION_free(sess);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* Compiled exactly when the body of test_wolfSSL_crl_io_mock() below is: the
|
|
* mock has no other caller, so a wider condition here leaves it defined and
|
|
* unused, which -Werror=unused-function rejects. Keep the two in step.
|
|
* NO_FILESYSTEM is in the condition because the body drives the callback
|
|
* through wolfSSL_CertManagerLoadCA()/Verify(), which ssl.h declares
|
|
* unconditionally but src/ssl_certman.c only implements inside
|
|
* #ifndef NO_FILESYSTEM. WOLFSSL_PEM_TO_DER is in it because the CA has to
|
|
* be a PEM file: wolfSSL_CertManagerLoadCA() goes through
|
|
* wolfSSL_CTX_load_verify_locations(), which takes no format argument and
|
|
* reads PEM only -- handing it certs/client-crl-dist.der fails with
|
|
* ASN_NO_PEM_HEADER, so there is no DER fixture to fall back to. */
|
|
#if defined(HAVE_CRL) && defined(HAVE_CRL_IO) && !defined(NO_CERTS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \
|
|
!defined(NO_FILESYSTEM) && defined(WOLFSSL_PEM_TO_DER)
|
|
static int g_crlIoCalls;
|
|
static int g_crlIoResult;
|
|
|
|
static int test_crl_io_mock(WOLFSSL_CRL* crl, const char* url, int urlSz)
|
|
{
|
|
(void)crl; (void)url; (void)urlSz;
|
|
g_crlIoCalls++;
|
|
return g_crlIoResult;
|
|
}
|
|
#endif
|
|
|
|
int test_wolfSSL_crl_io_mock(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CRL) && defined(HAVE_CRL_IO) && !defined(NO_CERTS) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \
|
|
!defined(NO_FILESYSTEM) && defined(WOLFSSL_PEM_TO_DER)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
int i;
|
|
static const int results[] = { 0, -1, 1,
|
|
WOLFSSL_CBIO_ERR_WANT_READ };
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_EnableCRL(ctx, WOLFSSL_CRL_CHECK),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
|
|
/* the guard operands first */
|
|
(void)wolfSSL_SetCRL_IOCb(NULL, test_crl_io_mock);
|
|
(void)wolfSSL_CTX_SetCRL_IOCb(NULL, test_crl_io_mock);
|
|
(void)wolfSSL_SetCRL_IOCb(ssl, NULL);
|
|
(void)wolfSSL_CTX_SetCRL_IOCb(ctx, NULL);
|
|
|
|
(void)wolfSSL_CTX_SetCRL_IOCb(ctx, test_crl_io_mock);
|
|
(void)wolfSSL_SetCRL_IOCb(ssl, test_crl_io_mock);
|
|
|
|
/* Then the callback for real. Loading a CA does not consult a CRL, so
|
|
* driving this through load_verify_locations left the mock uncalled and
|
|
* the whole loop measuring nothing; the callback is reached from the CRL
|
|
* check inside certificate verification. */
|
|
{
|
|
WOLFSSL_CERT_MANAGER* cm = NULL;
|
|
int invoked = 0;
|
|
|
|
/* A certificate that actually carries a distribution point. Under
|
|
* WOLFSSL_CRL_ALLOW_MISSING_CDP, CheckCertCRL returns before the
|
|
* callback for a certificate without one, so driving this with
|
|
* svrCertFile would leave the mock uncalled in that build. This one
|
|
* is self-signed, so it is its own CA. */
|
|
const char* cdpCert = "./certs/client-crl-dist.pem";
|
|
|
|
ExpectNotNull(cm = wolfSSL_CertManagerNew());
|
|
ExpectIntEQ(wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECK),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm, cdpCert, NULL),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CertManagerSetCRL_IOCb(cm, test_crl_io_mock),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
for (i = 0; i < (int)(sizeof(results) / sizeof(results[0])); i++) {
|
|
g_crlIoResult = results[i];
|
|
g_crlIoCalls = 0;
|
|
/* no CRL is loaded for this issuer, so the check has to fetch one;
|
|
* the verify itself is expected to fail */
|
|
(void)wolfSSL_CertManagerVerify(cm, cdpCert,
|
|
WOLFSSL_FILETYPE_PEM);
|
|
if (g_crlIoCalls > 0)
|
|
invoked++;
|
|
}
|
|
/* If none of them reached the transport the rows below it are vacuous */
|
|
ExpectIntGT(invoked, 0);
|
|
wolfSSL_CertManagerFree(cm);
|
|
}
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* OCSP stapling accessors, and CRL delivered through a mocked transport.
|
|
*
|
|
* The previous argument-guard sweep moved ssl_api_crl_ocsp.c by one condition,
|
|
* because it guessed at which functions held the gaps. Reading them settles
|
|
* it: they are not the enable/disable calls, they are the stapling request
|
|
* and response accessors, and their operands are of three kinds --
|
|
*
|
|
* ssl->options.side != WOLFSSL_CLIENT_END a SERVER object, which no
|
|
* ctx->method->side != WOLFSSL_CLIENT_END client-side test can supply
|
|
*
|
|
* ssl->ocspProducedDateFormat != ASN_UTC_TIME a response that was never
|
|
* processed, or one whose
|
|
* producedDate is a
|
|
* GeneralizedTime
|
|
*
|
|
* idx >= XELEM_CNT(ssl->ocspCsrResp), len < 0 an out-of-range slot
|
|
*
|
|
* None of them is reachable from a working client that staples successfully,
|
|
* which is the only shape the existing tests have.
|
|
* ------------------------------------------------------------------------- */
|
|
int test_wolfSSL_ocsp_stapling_accessors(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_OCSP) && defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
|
|
!defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
|
|
!defined(NO_TLS)
|
|
WOLFSSL_CTX* cctx = NULL; /* client */
|
|
WOLFSSL_CTX* sctx = NULL; /* server: the side operand's partner */
|
|
WOLFSSL* cssl = NULL;
|
|
WOLFSSL* sssl = NULL;
|
|
|
|
ExpectNotNull(cctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectNotNull(sctx = wolfSSL_CTX_new(wolfSSLv23_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_use_certificate_file(sctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(sctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
|
|
ExpectNotNull(cssl = wolfSSL_new(cctx));
|
|
ExpectNotNull(sssl = wolfSSL_new(sctx));
|
|
|
|
/* --- the stapling request calls, once per operand ------------------- */
|
|
(void)wolfSSL_UseOCSPStapling(NULL, WOLFSSL_CSR_OCSP, 0);
|
|
(void)wolfSSL_CTX_UseOCSPStapling(NULL, WOLFSSL_CSR_OCSP, 0);
|
|
/* a SERVER object: side != CLIENT_END, which is the operand a client-only
|
|
* test leaves permanently false */
|
|
(void)wolfSSL_UseOCSPStapling(sssl, WOLFSSL_CSR_OCSP, 0);
|
|
(void)wolfSSL_CTX_UseOCSPStapling(sctx, WOLFSSL_CSR_OCSP, 0);
|
|
/* the accepting partners */
|
|
(void)wolfSSL_UseOCSPStapling(cssl, WOLFSSL_CSR_OCSP, 0);
|
|
(void)wolfSSL_CTX_UseOCSPStapling(cctx, WOLFSSL_CSR_OCSP, 0);
|
|
|
|
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
|
|
(void)wolfSSL_UseOCSPStaplingV2(NULL, WOLFSSL_CSR2_OCSP, 0);
|
|
(void)wolfSSL_CTX_UseOCSPStaplingV2(NULL, WOLFSSL_CSR2_OCSP, 0);
|
|
(void)wolfSSL_UseOCSPStaplingV2(sssl, WOLFSSL_CSR2_OCSP, 0);
|
|
(void)wolfSSL_CTX_UseOCSPStaplingV2(sctx, WOLFSSL_CSR2_OCSP, 0);
|
|
(void)wolfSSL_UseOCSPStaplingV2(cssl, WOLFSSL_CSR2_OCSP, 0);
|
|
(void)wolfSSL_CTX_UseOCSPStaplingV2(cctx, WOLFSSL_CSR2_OCSP, 0);
|
|
#endif
|
|
|
|
#ifndef NO_ASN_TIME
|
|
/* --- the producedDate accessor -------------------------------------- */
|
|
{
|
|
byte when[32];
|
|
int fmt = 0;
|
|
|
|
XMEMSET(when, 0, sizeof(when));
|
|
(void)wolfSSL_get_ocsp_producedDate(NULL, when, sizeof(when), &fmt);
|
|
/* no response processed: ocspProducedDateFormat is neither UTC nor
|
|
* generalized, which is the pair for both operands at :879 */
|
|
(void)wolfSSL_get_ocsp_producedDate(cssl, when, sizeof(when), &fmt);
|
|
/* the output-pointer operands, reached only once a format is set */
|
|
(void)wolfSSL_get_ocsp_producedDate(cssl, NULL, sizeof(when), &fmt);
|
|
(void)wolfSSL_get_ocsp_producedDate(cssl, when, sizeof(when), NULL);
|
|
/* a buffer too small to hold the date */
|
|
(void)wolfSSL_get_ocsp_producedDate(cssl, when, 1, &fmt);
|
|
|
|
/* Drive the format operands directly. A stapled response carrying a
|
|
* GeneralizedTime rather than a UTCTime is legal, rare, and not
|
|
* something the test responder emits -- so this is the only way the
|
|
* second half of that decision is ever taken.
|
|
*
|
|
* Guarded because Expect* records a failure and carries on rather than
|
|
* returning: if wolfSSL_new() above failed, cssl is NULL here and
|
|
* these are dereferences of it, which is what the static analyser
|
|
* reported. Every call that merely PASSES cssl is safe either way --
|
|
* the API checks it -- so only the direct field writes need this. */
|
|
if (cssl != NULL) {
|
|
cssl->ocspProducedDateFormat = ASN_UTC_TIME;
|
|
(void)wolfSSL_get_ocsp_producedDate(cssl, when, sizeof(when), &fmt);
|
|
(void)wolfSSL_get_ocsp_producedDate(cssl, NULL, sizeof(when), &fmt);
|
|
(void)wolfSSL_get_ocsp_producedDate(cssl, when, sizeof(when), NULL);
|
|
(void)wolfSSL_get_ocsp_producedDate(cssl, when, 1, &fmt);
|
|
cssl->ocspProducedDateFormat = ASN_GENERALIZED_TIME;
|
|
(void)wolfSSL_get_ocsp_producedDate(cssl, when, sizeof(when), &fmt);
|
|
cssl->ocspProducedDateFormat = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
wolfSSL_free(cssl);
|
|
wolfSSL_free(sssl);
|
|
wolfSSL_CTX_free(cctx);
|
|
wolfSSL_CTX_free(sctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* X509 accessors and the DTLS API, each with the object its guard needs.
|
|
*
|
|
* Two files sat at the bottom of the newly-visible surface for the same
|
|
* reason, and it is not that their guards are hard to reach -- it is that the
|
|
* ACCEPTING half of each pair needs an object the existing tests do not have.
|
|
*
|
|
* x509.c every accessor is `x509 == NULL || outSz == NULL || ...`, so the
|
|
* NULL half is trivial and the valid half needs a parsed
|
|
* certificate. There was no test holding one.
|
|
*
|
|
* ssl_api_dtls.c every guard is `ssl == NULL || !ssl->options.dtls`, so the
|
|
* second operand needs a DTLS connection. Every test in this group
|
|
* used a TLS one, which leaves that operand constant.
|
|
*
|
|
* Both fixtures are cheap: a certificate loaded from certs/, and a WOLFSSL
|
|
* made from a DTLS method. Neither needs a peer.
|
|
* ------------------------------------------------------------------------- */
|
|
int test_wolfSSL_x509_accessor_guards(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* wolfSSL_X509_load_certificate_file / _get_signature / _get_pubkey_buffer /
|
|
* _free are declared in wolfssl/ssl.h only under this set of macros, not under
|
|
* WOLFSSL_CERT_GEN as an earlier version of this guard assumed. With the wrong
|
|
* macro they became implicit declarations, which -Werror=implicit-function-
|
|
* declaration and -Werror=nested-externs turn into build failures (and the
|
|
* implicit int return then trips -Werror=int-conversion on the assignment). */
|
|
/* wolfSSL_X509_get_signature, _get_next_altname and _get_pubkey_buffer are
|
|
* declared in ssl.h unconditionally but implemented in src/x509.c under
|
|
* OPENSSL_EXTRA || KEEP_OUR_CERT || KEEP_PEER_CERT only. A build with
|
|
* SESSION_CERTS or OPENSSL_EXTRA_X509_SMALL and none of those three sees the
|
|
* declarations and no definitions, which is a link error, not a compile error
|
|
* -- invisible to anything that only reads headers. Match the implementation. */
|
|
#if !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
|
|
(defined(OPENSSL_EXTRA) || defined(KEEP_PEER_CERT) || \
|
|
defined(KEEP_OUR_CERT))
|
|
WOLFSSL_X509* x509 = NULL;
|
|
byte buf[2048];
|
|
int iSz = (int)sizeof(buf);
|
|
int wSz = (int)sizeof(buf);
|
|
const byte* der = NULL;
|
|
int derSz = 0;
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
|
|
/* the NULL half of every guard, before any fixture exists */
|
|
(void)wolfSSL_X509_get_der(NULL, &derSz);
|
|
(void)wolfSSL_X509_get_serial_number(NULL, buf, &iSz);
|
|
(void)wolfSSL_X509_get_signature(NULL, buf, &iSz);
|
|
(void)wolfSSL_X509_get_next_altname(NULL);
|
|
(void)wolfSSL_X509_load_certificate_file(NULL, WOLFSSL_FILETYPE_PEM);
|
|
(void)wolfSSL_X509_load_certificate_file("certs/no-such-file.pem",
|
|
WOLFSSL_FILETYPE_PEM);
|
|
|
|
/* the accepting half: a real parsed certificate */
|
|
x509 = wolfSSL_X509_load_certificate_file(svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM);
|
|
if (x509 != NULL) {
|
|
/* second operand of each guard: valid object, NULL output */
|
|
(void)wolfSSL_X509_get_der(x509, NULL);
|
|
(void)wolfSSL_X509_get_serial_number(x509, buf, NULL);
|
|
(void)wolfSSL_X509_get_serial_number(x509, NULL, &iSz);
|
|
(void)wolfSSL_X509_get_signature(x509, buf, NULL);
|
|
/* a buffer too small for the signature: the size operand, which a
|
|
* caller sizing from the query call never takes */
|
|
iSz = 1;
|
|
(void)wolfSSL_X509_get_signature(x509, buf, &iSz);
|
|
/* the query form: NULL buffer with a size pointer */
|
|
iSz = 0;
|
|
(void)wolfSSL_X509_get_signature(x509, NULL, &iSz);
|
|
iSz = (int)sizeof(buf);
|
|
(void)wolfSSL_X509_get_signature(x509, buf, &iSz);
|
|
|
|
derSz = 0;
|
|
der = wolfSSL_X509_get_der(x509, &derSz);
|
|
(void)der;
|
|
|
|
iSz = (int)sizeof(buf);
|
|
(void)wolfSSL_X509_get_serial_number(x509, buf, &iSz);
|
|
(void)wolfSSL_X509_get_next_altname(x509);
|
|
(void)wolfSSL_X509_notBefore(x509);
|
|
(void)wolfSSL_X509_notAfter(x509);
|
|
(void)wolfSSL_X509_version(x509);
|
|
|
|
#ifdef OPENSSL_EXTRA
|
|
(void)wolfSSL_X509_check_host(x509, NULL, 0, 0, NULL);
|
|
(void)wolfSSL_X509_check_host(NULL, "example.com", 11, 0, NULL);
|
|
#endif
|
|
wSz = (int)sizeof(buf);
|
|
(void)wolfSSL_X509_get_pubkey_buffer(x509, buf, &wSz);
|
|
(void)wolfSSL_X509_get_pubkey_buffer(x509, NULL, &wSz);
|
|
(void)wolfSSL_X509_get_pubkey_buffer(x509, buf, NULL);
|
|
|
|
/* --- host and IP matching, both operands of each guard --------- */
|
|
#ifndef NO_ASN
|
|
/* `(x == NULL) || (chk == NULL)` */
|
|
(void)wolfSSL_X509_check_host(NULL, "example.com", 11, 0, NULL);
|
|
(void)wolfSSL_X509_check_host(x509, NULL, 11, 0, NULL);
|
|
(void)wolfSSL_X509_check_host(x509, "example.com", 11, 0, NULL);
|
|
/* `chklen > 1 && chk[chklen - 1] == 0` -- a length that includes the
|
|
* terminator, which a caller using strlen() never passes */
|
|
(void)wolfSSL_X509_check_host(x509, "example.com", 12, 0, NULL);
|
|
(void)wolfSSL_X509_check_host(x509, "e", 1, 0, NULL);
|
|
(void)wolfSSL_X509_check_host(x509, "", 0, 0, NULL);
|
|
|
|
/* `(x == NULL) || (x->derCert == NULL) || (ipasc == NULL)` */
|
|
(void)wolfSSL_X509_check_ip_asc(NULL, "127.0.0.1", 0);
|
|
(void)wolfSSL_X509_check_ip_asc(x509, NULL, 0);
|
|
(void)wolfSSL_X509_check_ip_asc(x509, "127.0.0.1", 0);
|
|
(void)wolfSSL_X509_check_ip_asc(x509, "not-an-ip", 0);
|
|
#endif
|
|
|
|
wolfSSL_X509_free(x509);
|
|
}
|
|
|
|
/* --- load_certificate_file: `fname == NULL`, and the size bounds ---- */
|
|
(void)wolfSSL_X509_load_certificate_file(NULL, WOLFSSL_FILETYPE_PEM);
|
|
/* a file that exists but is empty: sz < 0 || sz > MAX is the guard the
|
|
* happy path never reaches */
|
|
{
|
|
const char* emptyPem = "test-x509-empty.tmp";
|
|
XFILE ef = XFOPEN(emptyPem, "wb");
|
|
|
|
if (ef != XBADFILE) {
|
|
XFCLOSE(ef);
|
|
(void)wolfSSL_X509_load_certificate_file(emptyPem,
|
|
WOLFSSL_FILETYPE_PEM);
|
|
(void)remove(emptyPem);
|
|
}
|
|
/* a directory where a file is expected */
|
|
(void)wolfSSL_X509_load_certificate_file("certs",
|
|
WOLFSSL_FILETYPE_PEM);
|
|
/* an unknown format on a real certificate */
|
|
(void)wolfSSL_X509_load_certificate_file(svrCertFile, -1);
|
|
}
|
|
(void)wSz;
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_wolfSSL_dtls_api_on_dtls_object(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_DTLS) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_CERTS) && \
|
|
!defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* dctx = NULL; /* the object the second operand needs */
|
|
WOLFSSL_CTX* tctx = NULL; /* a TLS one, for the operand's other half */
|
|
WOLFSSL* dssl = NULL;
|
|
WOLFSSL* tssl = NULL;
|
|
byte peer[64];
|
|
unsigned int peerSz = (unsigned int)sizeof(peer);
|
|
|
|
XMEMSET(peer, 0, sizeof(peer));
|
|
ExpectNotNull(dctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method()));
|
|
ExpectNotNull(tctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectNotNull(dssl = wolfSSL_new(dctx));
|
|
ExpectNotNull(tssl = wolfSSL_new(tctx));
|
|
/* A failed constructor leaves the later calls holding NULL. Those calls
|
|
* are not inside Expect(), so they would run anyway and dereference it. */
|
|
if (dctx == NULL || tctx == NULL || dssl == NULL || tssl == NULL) {
|
|
wolfSSL_free(dssl);
|
|
wolfSSL_free(tssl);
|
|
wolfSSL_CTX_free(dctx);
|
|
wolfSSL_CTX_free(tctx);
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* `ssl == NULL || !ssl->options.dtls` -- three vectors, one per outcome */
|
|
(void)wolfSSL_dtls_got_timeout(NULL);
|
|
(void)wolfSSL_dtls_got_timeout(tssl); /* not a DTLS connection */
|
|
(void)wolfSSL_dtls_got_timeout(dssl); /* the accepting partner */
|
|
(void)wolfSSL_dtls_retransmit(NULL);
|
|
(void)wolfSSL_dtls_retransmit(tssl);
|
|
(void)wolfSSL_dtls_retransmit(dssl);
|
|
(void)wolfSSL_dtls_get_current_timeout(tssl);
|
|
(void)wolfSSL_dtls_get_current_timeout(dssl);
|
|
(void)wolfSSL_dtls(tssl);
|
|
(void)wolfSSL_dtls(dssl);
|
|
|
|
/* peer accessors on a connection that has no peer set yet */
|
|
peerSz = (unsigned int)sizeof(peer);
|
|
(void)wolfSSL_dtls_get_peer(dssl, peer, &peerSz);
|
|
(void)wolfSSL_dtls_get_peer(dssl, NULL, &peerSz);
|
|
(void)wolfSSL_dtls_get_peer(dssl, peer, NULL);
|
|
/* Implemented only under WOLFSSL_DTLS_CID && !WOLFSSL_NO_SOCK
|
|
* (src/ssl_api_dtls.c); declared unconditionally in ssl.h, so a config
|
|
* with WOLFSSL_DTLS on and WOLFSSL_DTLS_CID off compiles this call and
|
|
* fails at LINK time. Confirmed with a real build. */
|
|
#if defined(WOLFSSL_DTLS_CID) && !defined(WOLFSSL_NO_SOCK)
|
|
(void)wolfSSL_dtls_set_pending_peer(dssl, peer, 0);
|
|
(void)wolfSSL_dtls_set_pending_peer(dssl, NULL,
|
|
(unsigned int)sizeof(peer));
|
|
(void)wolfSSL_dtls_set_pending_peer(dssl, peer,
|
|
(unsigned int)sizeof(peer));
|
|
/* and again now that a peer exists, so the `peer.sa != NULL` operand
|
|
* gets both values */
|
|
(void)wolfSSL_dtls_set_pending_peer(dssl, peer,
|
|
(unsigned int)sizeof(peer));
|
|
#endif
|
|
|
|
/* MTU: `ctx == NULL || newMtu > MAX_RECORD_SIZE`, both operands */
|
|
/* Declared under (WOLFSSL_SCTP || WOLFSSL_DTLS_MTU) && WOLFSSL_DTLS in ssl.h;
|
|
* guarding on WOLFSSL_DTLS_MTU alone left the SCTP-only configs calling an
|
|
* undeclared function. */
|
|
#if (defined(WOLFSSL_SCTP) || defined(WOLFSSL_DTLS_MTU)) && defined(WOLFSSL_DTLS)
|
|
(void)wolfSSL_CTX_dtls_set_mtu(NULL, 512);
|
|
(void)wolfSSL_CTX_dtls_set_mtu(dctx, 0xFFFF);
|
|
(void)wolfSSL_CTX_dtls_set_mtu(dctx, 512);
|
|
(void)wolfSSL_dtls_set_mtu(dssl, 0xFFFF);
|
|
(void)wolfSSL_dtls_set_mtu(dssl, 512);
|
|
#endif
|
|
|
|
#ifdef WOLFSSL_DTLS13
|
|
(void)wolfSSL_dtls13_has_pending_msg(dssl);
|
|
(void)wolfSSL_dtls13_use_quick_timeout(dssl);
|
|
wolfSSL_dtls13_set_send_more_acks(dssl, 1);
|
|
wolfSSL_dtls13_set_send_more_acks(dssl, 0);
|
|
#endif
|
|
|
|
wolfSSL_free(dssl);
|
|
wolfSSL_free(tssl);
|
|
wolfSSL_CTX_free(dctx);
|
|
wolfSSL_CTX_free(tctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Certificate loading from files that are wrong in file-system ways.
|
|
*
|
|
* ssl_misc.c is 0/11 and every one of its conditions is in a static file
|
|
* helper -- wolfssl_file_len and wolfssl_read_file_static -- guarding against
|
|
* a seek that fails, a length that is zero or absurd, and a read that returns
|
|
* fewer bytes than the length promised:
|
|
*
|
|
* if ((ret == 0) && ((sz > MAX_WOLFSSL_FILE_SIZE) || (sz <= 0L)))
|
|
* if ((ret == 0) && ((file = XFOPEN(fname, "rb")) == XBADFILE))
|
|
* if ((ret == 0) && (XFREAD(...) != sz))
|
|
*
|
|
* They are static, but they are not out of reach: every public load-from-file
|
|
* entry point runs through them, so the vector is the FILE rather than the
|
|
* argument. An empty file gives sz <= 0; a directory passed where a file is
|
|
* expected gives a seek or read that fails; a name that does not exist gives
|
|
* XBADFILE. None of those is something a working configuration supplies, and
|
|
* no existing test supplies them either.
|
|
* ------------------------------------------------------------------------- */
|
|
int test_wolfSSL_load_pathological_files(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_TLS)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL_CERT_MANAGER* cm = NULL;
|
|
const char* emptyFile = "test-empty-cert.tmp";
|
|
XFILE f = XBADFILE;
|
|
|
|
/* an empty file: the `sz <= 0` operand, which no real certificate has */
|
|
f = XFOPEN(emptyFile, "wb");
|
|
if (f != XBADFILE)
|
|
XFCLOSE(f);
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
|
|
/* XBADFILE: a name that does not exist */
|
|
(void)wolfSSL_CTX_load_verify_locations(ctx, "no-such-file.pem", NULL);
|
|
(void)wolfSSL_CTX_use_certificate_file(ctx, "no-such-file.pem",
|
|
WOLFSSL_FILETYPE_PEM);
|
|
(void)wolfSSL_CTX_use_PrivateKey_file(ctx, "no-such-file.pem",
|
|
WOLFSSL_FILETYPE_PEM);
|
|
|
|
/* sz <= 0: the empty file */
|
|
(void)wolfSSL_CTX_load_verify_locations(ctx, emptyFile, NULL);
|
|
(void)wolfSSL_CTX_use_certificate_file(ctx, emptyFile,
|
|
WOLFSSL_FILETYPE_PEM);
|
|
(void)wolfSSL_CTX_use_PrivateKey_file(ctx, emptyFile,
|
|
WOLFSSL_FILETYPE_PEM);
|
|
(void)wolfSSL_CTX_use_certificate_chain_file(ctx, emptyFile);
|
|
|
|
/* a directory where a file is expected: the seek and read failure arms */
|
|
(void)wolfSSL_CTX_load_verify_locations(ctx, "certs", NULL);
|
|
(void)wolfSSL_CTX_use_certificate_file(ctx, "certs",
|
|
WOLFSSL_FILETYPE_PEM);
|
|
|
|
/* a file that exists and is not a certificate at all */
|
|
(void)wolfSSL_CTX_load_verify_locations(ctx, "Makefile", NULL);
|
|
|
|
/* the accepting partner, so every operand above has one */
|
|
(void)wolfSSL_CTX_load_verify_locations(ctx, caCertFile, NULL);
|
|
|
|
/* the same set through the CertManager, which has its own copies of the
|
|
* load paths */
|
|
cm = wolfSSL_CertManagerNew();
|
|
if (cm != NULL) {
|
|
(void)wolfSSL_CertManagerLoadCA(cm, "no-such-file.pem", NULL);
|
|
(void)wolfSSL_CertManagerLoadCA(cm, emptyFile, NULL);
|
|
(void)wolfSSL_CertManagerLoadCA(cm, "certs", NULL);
|
|
(void)wolfSSL_CertManagerLoadCA(cm, caCertFile, NULL);
|
|
(void)wolfSSL_CertManagerVerify(cm, "no-such-file.pem",
|
|
WOLFSSL_FILETYPE_PEM);
|
|
(void)wolfSSL_CertManagerVerify(cm, emptyFile, WOLFSSL_FILETYPE_PEM);
|
|
(void)wolfSSL_CertManagerVerify(cm, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM);
|
|
(void)wolfSSL_CertManagerUnloadCAs(cm);
|
|
(void)wolfSSL_CertManagerUnloadCAs(NULL);
|
|
wolfSSL_CertManagerFree(cm);
|
|
}
|
|
{
|
|
/* Returns an owned CertManager; discarding it leaks
|
|
* (LeakSanitizer: 280 bytes from wolfSSL_CertManagerNew_ex). */
|
|
WOLFSSL_CERT_MANAGER* tmpCm = wolfSSL_CertManagerNew_ex(NULL);
|
|
|
|
if (tmpCm != NULL)
|
|
wolfSSL_CertManagerFree(tmpCm);
|
|
}
|
|
|
|
wolfSSL_CTX_free(ctx);
|
|
(void)remove(emptyFile);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* The file-helper failure arms, reached with a FIFO.
|
|
*
|
|
* Three of ssl_misc.c's conditions cannot be produced by any regular file:
|
|
*
|
|
* if ((ret == 0) && (XFSEEK(fp, 0, SEEK_END) != 0)) seek failed
|
|
* if ((ret == 0) && (XFREAD(...) != (size_t)sz)) short read
|
|
*
|
|
* A regular file always seeks and always reads what it promised. A FIFO does
|
|
* neither: fopen() succeeds, so the XBADFILE arm is passed, and then fseek()
|
|
* fails with ESPIPE because a pipe has no position. That is the exact shape
|
|
* the guard is written for, and nothing on disk can imitate it.
|
|
*
|
|
* The FIFO is opened read-write by the test before the library touches it, so
|
|
* the library's fopen() cannot block waiting for a writer -- a test that hangs
|
|
* costs the whole variant just as surely as one that crashes.
|
|
* ------------------------------------------------------------------------- */
|
|
int test_wolfSSL_load_from_fifo(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* __linux__, not __unix__: the fixture holds the FIFO open with O_RDWR, which
|
|
* POSIX leaves undefined for FIFOs. Linux defines it; elsewhere the open could
|
|
* fail and every check below would silently skip. */
|
|
#if !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && defined(__linux__) && !defined(NO_TLS)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
const char* fifo = "test-cert-fifo.tmp";
|
|
int fd = -1;
|
|
|
|
(void)remove(fifo);
|
|
if (mkfifo(fifo, 0600) != 0) {
|
|
/* no FIFO support here; that is a platform fact, not a failure */
|
|
return EXPECT_RESULT();
|
|
}
|
|
/* Hold it open both ways so the library's fopen() returns immediately
|
|
* and there is something to read. */
|
|
fd = open(fifo, O_RDWR | O_NONBLOCK);
|
|
if (fd >= 0) {
|
|
const char junk[] = "-----BEGIN CERTIFICATE-----\n";
|
|
ssize_t w = write(fd, junk, sizeof(junk) - 1);
|
|
(void)w;
|
|
}
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
if (fd >= 0) {
|
|
/* fopen succeeds, fseek fails: the seek-failure arm */
|
|
(void)wolfSSL_CTX_load_verify_locations(ctx, fifo, NULL);
|
|
(void)wolfSSL_CTX_use_certificate_file(ctx, fifo,
|
|
WOLFSSL_FILETYPE_PEM);
|
|
(void)wolfSSL_CTX_use_PrivateKey_file(ctx, fifo,
|
|
WOLFSSL_FILETYPE_PEM);
|
|
(void)wolfSSL_CTX_use_certificate_chain_file(ctx, fifo);
|
|
{
|
|
WOLFSSL_CERT_MANAGER* cm = wolfSSL_CertManagerNew();
|
|
if (cm != NULL) {
|
|
(void)wolfSSL_CertManagerLoadCA(cm, fifo, NULL);
|
|
(void)wolfSSL_CertManagerVerify(cm, fifo,
|
|
WOLFSSL_FILETYPE_PEM);
|
|
wolfSSL_CertManagerFree(cm);
|
|
}
|
|
}
|
|
/* the accepting partner through the same code */
|
|
(void)wolfSSL_CTX_load_verify_locations(ctx, caCertFile, NULL);
|
|
}
|
|
|
|
wolfSSL_CTX_free(ctx);
|
|
if (fd >= 0)
|
|
close(fd);
|
|
(void)remove(fifo);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Allocation failure injection.
|
|
*
|
|
* Error propagation is the largest remaining category in the campaign: 633 of
|
|
* 2855 uncovered conditions, almost all of the shape
|
|
*
|
|
* ret = something();
|
|
* if (ret != 0) { ... } or if (p == NULL) { ... }
|
|
*
|
|
* after a call that cannot fail in a working configuration. No number of
|
|
* successful runs pairs those operands, because the failing value never
|
|
* occurs. The only way to produce it is to make the underlying operation fail
|
|
* on purpose.
|
|
*
|
|
* wolfSSL_SetAllocators() is the cheapest lever for that: one harness, and it
|
|
* reaches every out-of-memory arm in every file at once, because every
|
|
* allocation in the library goes through it. The sweep fails the Nth
|
|
* allocation and lets the rest succeed, for each N in turn -- so each run
|
|
* takes a different one of those arms, and the runs where N is past the end
|
|
* of the workload are the shared accepting partner.
|
|
*
|
|
* Failing exactly one allocation rather than everything from N onward keeps
|
|
* each vector isolated: a cascade would take many arms at once and prove
|
|
* nothing about any single one.
|
|
*
|
|
* The allocators are restored before the test returns. Leaving a failing
|
|
* allocator installed would break every test that runs after this one in the
|
|
* same binary, which costs the whole variant.
|
|
* ------------------------------------------------------------------------- */
|
|
/* OPT-IN, via WOLFSSL_MCDC_ALLOC_SWEEP, which the coverage campaign's own
|
|
* option list defines and no ordinary build does.
|
|
*
|
|
* Not timidity: this deliberately fails allocations dozens of times deep
|
|
* inside the library, and an allocation that fails part-way through can leave
|
|
* a global -- the session cache, a CertManager's tables -- in a state the next
|
|
* test in the same binary then trips over. The value of the sweep is the
|
|
* coverage it measures, and that is measured in the campaign build; running it
|
|
* in every CI configuration adds risk to unrelated tests and no evidence. A
|
|
* PRB run aborted with SIGABRT after a cascade of certificate-loading and
|
|
* memio failures in tests that come later in the binary, which is exactly that
|
|
* shape. It was not reproduced here in --enable-all or --enable-smallstack, in
|
|
* either order against ssl_hs, so this is not a diagnosis -- it is declining to
|
|
* carry a hazard whose only benefit is realised elsewhere.
|
|
*
|
|
* This ran everywhere except WOLFSSL_SMALL_STACK for a while, because under
|
|
* that build DecodeCertInternal indexed RPKdataASN before checking the ret
|
|
* that CALLOC_ASNGETDATA sets, so failing an allocation dereferenced NULL
|
|
* while parsing any certificate -- a per-index sweep crashed at five
|
|
* allocation indices (7, 30, 51, 68, 90), all the same instruction, reached
|
|
* through load_verify_locations, use_certificate_file,
|
|
* use_certificate_chain_file and CertManagerVerify. That was reported and
|
|
* fixed upstream in PR 11378, which is in the tree, so the small-stack
|
|
* exclusion is gone and the sweep now runs on every variant. A crash here
|
|
* still costs the whole variant, so it is worth re-checking after any change
|
|
* to the ASN.1 allocation macros. */
|
|
/* wolfSSL_SetAllocators lives in wolfcrypt/src/memory.c under
|
|
* #ifdef USE_WOLFSSL_MEMORY; without it the symbol is declared and never
|
|
* defined. */
|
|
#if defined(WOLFSSL_MCDC_ALLOC_SWEEP) && defined(USE_WOLFSSL_MEMORY) && \
|
|
!defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_DEBUG_MEMORY) && \
|
|
!defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_FILESYSTEM) && \
|
|
!defined(NO_TLS)
|
|
|
|
static int fi_failAt = -1; /* which allocation to fail; -1 = none */
|
|
static int fi_count; /* allocations seen since the last reset */
|
|
static int fi_failed; /* did we actually inject one this run */
|
|
|
|
static void* fi_malloc(size_t n)
|
|
{
|
|
/* The index is taken BEFORE the test. Writing this as
|
|
* if (fi_failAt >= 0 && fi_count++ == fi_failAt)
|
|
* short-circuits the increment away whenever injection is off, so the
|
|
* counting pass counts nothing, the sweep bound comes out zero and the
|
|
* harness silently measures the happy path. That is the same operand
|
|
* short-circuit these tests exist to cover, in the test's own code. */
|
|
int i = fi_count++;
|
|
|
|
if (fi_failAt >= 0 && i == fi_failAt) {
|
|
fi_failed = 1;
|
|
return NULL;
|
|
}
|
|
return malloc(n);
|
|
}
|
|
|
|
static void fi_free(void* p)
|
|
{
|
|
free(p);
|
|
}
|
|
|
|
static void* fi_realloc(void* p, size_t n)
|
|
{
|
|
int i = fi_count++;
|
|
|
|
if (fi_failAt >= 0 && i == fi_failAt) {
|
|
fi_failed = 1;
|
|
return NULL;
|
|
}
|
|
return realloc(p, n);
|
|
}
|
|
|
|
/* The workload every vector runs. Deliberately ordinary: build a context,
|
|
* load real credentials, build a connection object, ask for a few extensions,
|
|
* tear it all down. What varies between vectors is only which allocation
|
|
* inside it fails. */
|
|
static void fi_workload(void)
|
|
{
|
|
WOLFSSL_CTX* ctx;
|
|
WOLFSSL* ssl;
|
|
|
|
ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
|
|
if (ctx == NULL)
|
|
return;
|
|
(void)wolfSSL_CTX_load_verify_locations(ctx, caCertFile, NULL);
|
|
(void)wolfSSL_CTX_use_certificate_file(ctx, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM);
|
|
(void)wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile,
|
|
WOLFSSL_FILETYPE_PEM);
|
|
|
|
ssl = wolfSSL_new(ctx);
|
|
if (ssl != NULL) {
|
|
#ifdef HAVE_SNI
|
|
(void)wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, "example.com", 11);
|
|
#endif
|
|
#ifdef HAVE_ALPN
|
|
{
|
|
char alpnList[] = "h2"; /* takes char*, not const char* */
|
|
(void)wolfSSL_UseALPN(ssl, alpnList, 2,
|
|
WOLFSSL_ALPN_CONTINUE_ON_MISMATCH);
|
|
}
|
|
#endif
|
|
#ifdef HAVE_SUPPORTED_CURVES
|
|
(void)wolfSSL_UseSupportedCurve(ssl, WOLFSSL_ECC_SECP256R1);
|
|
#endif
|
|
#ifdef HAVE_SESSION_TICKET
|
|
(void)wolfSSL_UseSessionTicket(ssl);
|
|
#endif
|
|
#ifdef HAVE_MAX_FRAGMENT
|
|
(void)wolfSSL_UseMaxFragment(ssl, WOLFSSL_MFL_2_9);
|
|
#endif
|
|
#ifdef HAVE_TRUSTED_CA
|
|
(void)wolfSSL_UseTrustedCA(ssl, WOLFSSL_TRUSTED_CA_PRE_AGREED,
|
|
NULL, 0);
|
|
#endif
|
|
#ifdef HAVE_OCSP
|
|
(void)wolfSSL_EnableOCSP(ssl, 0);
|
|
#endif
|
|
(void)wolfSSL_SetVersion(ssl, WOLFSSL_TLSV1_2);
|
|
(void)wolfSSL_set_cipher_list(ssl, "DEFAULT");
|
|
#ifndef NO_SESSION_CACHE
|
|
{
|
|
WOLFSSL_SESSION* s1 = wolfSSL_get1_session(ssl);
|
|
|
|
if (s1 != NULL) {
|
|
WOLFSSL_SESSION* s2 = wolfSSL_SESSION_dup(s1);
|
|
|
|
if (s2 != NULL)
|
|
wolfSSL_SESSION_free(s2);
|
|
wolfSSL_SESSION_free(s1);
|
|
}
|
|
}
|
|
#endif
|
|
wolfSSL_free(ssl);
|
|
}
|
|
|
|
/* A second reach: the CertManager and its CRL/OCSP sub-objects allocate
|
|
* on their own paths, and every one of those allocations is an error arm
|
|
* that a working configuration never takes. */
|
|
{
|
|
WOLFSSL_CERT_MANAGER* cm = wolfSSL_CertManagerNew();
|
|
|
|
if (cm != NULL) {
|
|
(void)wolfSSL_CertManagerLoadCA(cm, caCertFile, NULL);
|
|
(void)wolfSSL_CertManagerVerify(cm, svrCertFile,
|
|
WOLFSSL_FILETYPE_PEM);
|
|
#ifdef HAVE_CRL
|
|
(void)wolfSSL_CertManagerEnableCRL(cm, WOLFSSL_CRL_CHECK);
|
|
#endif
|
|
#ifdef HAVE_OCSP
|
|
(void)wolfSSL_CertManagerEnableOCSP(cm, 0);
|
|
#endif
|
|
wolfSSL_CertManagerFree(cm);
|
|
}
|
|
}
|
|
|
|
/* And the chain/buffer loaders, which have their own allocation and
|
|
* error-propagation chains distinct from the file loaders above. */
|
|
(void)wolfSSL_CTX_use_certificate_chain_file(ctx, svrCertFile);
|
|
|
|
wolfSSL_CTX_free(ctx);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Result, recorded because a negative one is still a result: on this workload
|
|
* wolfSSL survives ALL of its allocation failures. The sweep drives 37
|
|
* allocation sites, fails each in turn, and the library returns an error and
|
|
* cleans up every time -- no crash, no leak-driven abort, no wedged state.
|
|
* Verified both here and with a standalone reproducer linked against the
|
|
* campaign's own libwolfssl.a.
|
|
*
|
|
* That is worth knowing for a safety case: the out-of-memory arms in this path
|
|
* are not merely present, they work. It also means the harness is safe to run
|
|
* in the campaign build, which is what makes those arms measurable at all.
|
|
*/
|
|
int test_wolfSSL_alloc_failure_sweep(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* wolfSSL_SetAllocators lives in wolfcrypt/src/memory.c under
|
|
* #ifdef USE_WOLFSSL_MEMORY; without it the symbol is declared and never
|
|
* defined. */
|
|
#if defined(WOLFSSL_MCDC_ALLOC_SWEEP) && defined(USE_WOLFSSL_MEMORY) && \
|
|
!defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_DEBUG_MEMORY) && \
|
|
!defined(NO_CERTS) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_FILESYSTEM) && \
|
|
!defined(NO_TLS)
|
|
int n;
|
|
int injected = 0;
|
|
int total;
|
|
wolfSSL_Malloc_cb prevMalloc = NULL;
|
|
wolfSSL_Free_cb prevFree = NULL;
|
|
wolfSSL_Realloc_cb prevRealloc = NULL;
|
|
|
|
/* Keep whatever was installed before, so the teardown below can put it
|
|
* back. Re-installing our own wrappers is NOT a restore: they would stay
|
|
* live for every later test in this binary, and unit.test runs threaded
|
|
* tests after this one. */
|
|
(void)wolfSSL_GetAllocators(&prevMalloc, &prevFree, &prevRealloc);
|
|
|
|
/* Install the wrappers with injection off, and count how many
|
|
* allocations the workload makes, so the sweep covers all of them and
|
|
* stops rather than running past the end. */
|
|
if (wolfSSL_SetAllocators(fi_malloc, fi_free, fi_realloc) != 0) {
|
|
/* the build does not allow overriding allocators here */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
fi_failAt = -1;
|
|
fi_count = 0;
|
|
fi_workload();
|
|
total = fi_count;
|
|
ExpectIntGT(total, 0);
|
|
/* The count is not stable between runs -- caches warm, session state
|
|
* persists -- so the sweep bound is taken from the first pass and the
|
|
* per-vector check below is 'did this one inject', not 'did all of
|
|
* them'. Asserting the totals match failed for exactly this reason. */
|
|
|
|
/* One run per allocation, failing that one and no other. */
|
|
for (n = 0; n < total; n++) {
|
|
fi_failAt = n;
|
|
fi_count = 0;
|
|
fi_failed = 0;
|
|
fi_workload();
|
|
if (fi_failed)
|
|
injected++;
|
|
}
|
|
|
|
/* The accepting partner: the same workload with nothing failing. */
|
|
fi_failAt = -1;
|
|
fi_count = 0;
|
|
fi_workload();
|
|
|
|
/* At least one vector must have injected, or the sweep silently measured
|
|
* the happy path N times over -- the no-op failure mode this campaign has
|
|
* hit repeatedly. */
|
|
ExpectIntGT(injected, 0);
|
|
|
|
/* Put the original allocators back, and prove the library still works
|
|
* afterwards -- any allocator of ours left installed would sit under every
|
|
* later test in this binary, including the threaded ones. */
|
|
fi_failAt = -1;
|
|
(void)wolfSSL_SetAllocators(prevMalloc, prevFree, prevRealloc);
|
|
{
|
|
WOLFSSL_CTX* ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
|
|
ExpectNotNull(ctx);
|
|
wolfSSL_CTX_free(ctx);
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* DTLS API argument guards, on a DTLS object.
|
|
*
|
|
* ssl_api_dtls.c is the weakest of the newly-visible files. Its guards are the
|
|
* usual NULL-and-zero pairs, but the ACCEPTING half of most of them needs a
|
|
* DTLS connection -- the same reason the file sat at 3/53 until one was
|
|
* supplied. These add the entry points the earlier pass did not reach.
|
|
*
|
|
* Guards were read from src/ssl_api_dtls.c before writing, not assumed from
|
|
* ssl.h: wolfSSL_dtls13_pending_work is compiled only under WOLFSSL_DTLS13,
|
|
* SetCookieSecret only under WOLFSSL_DTLS && !NO_WOLFSSL_SERVER, and several
|
|
* are additionally gated on !WOLFSSL_LEANPSK.
|
|
* ------------------------------------------------------------------------- */
|
|
int test_wolfSSL_dtls_api_more_guards(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_LEANPSK) && \
|
|
!defined(WOLFCRYPT_ONLY) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_CERTS) && \
|
|
!defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
|
|
WOLFSSL_CTX* dctx = NULL;
|
|
WOLFSSL* dssl = NULL;
|
|
byte peer[64];
|
|
unsigned int peerSz = (unsigned int)sizeof(peer);
|
|
const void* p0 = NULL;
|
|
unsigned int p0Sz = 0;
|
|
|
|
XMEMSET(peer, 0, sizeof(peer));
|
|
ExpectNotNull(dctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method()));
|
|
ExpectNotNull(dssl = wolfSSL_new(dctx));
|
|
/* A failed constructor leaves the later calls holding NULL. Those calls
|
|
* are not inside Expect(), so they would run anyway and dereference it. */
|
|
if (dctx == NULL || dssl == NULL) {
|
|
wolfSSL_free(dssl);
|
|
wolfSSL_CTX_free(dctx);
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* `peer == NULL || peerSz == NULL` -- one call per operand */
|
|
(void)wolfSSL_dtls_get0_peer(NULL, &p0, &p0Sz);
|
|
(void)wolfSSL_dtls_get0_peer(dssl, NULL, &p0Sz);
|
|
(void)wolfSSL_dtls_get0_peer(dssl, &p0, NULL);
|
|
(void)wolfSSL_dtls_get0_peer(dssl, &p0, &p0Sz);
|
|
|
|
/* `ssl && timeleft` -- both operands */
|
|
{
|
|
WOLFSSL_TIMEVAL tv;
|
|
|
|
XMEMSET(&tv, 0, sizeof(tv));
|
|
(void)wolfSSL_DTLSv1_get_timeout(NULL, &tv);
|
|
(void)wolfSSL_DTLSv1_get_timeout(dssl, NULL);
|
|
(void)wolfSSL_DTLSv1_get_timeout(dssl, &tv);
|
|
}
|
|
|
|
/* `ssl == NULL || timeout < 0` -- and the boundary at zero */
|
|
(void)wolfSSL_dtls_set_timeout_max(NULL, 5);
|
|
(void)wolfSSL_dtls_set_timeout_max(dssl, -1);
|
|
(void)wolfSSL_dtls_set_timeout_max(dssl, 0);
|
|
(void)wolfSSL_dtls_set_timeout_max(dssl, 5);
|
|
(void)wolfSSL_dtls_set_timeout_init(dssl, 0);
|
|
|
|
/* the peer setters/getters with a real DTLS object */
|
|
peerSz = (unsigned int)sizeof(peer);
|
|
(void)wolfSSL_dtls_get_peer(dssl, peer, &peerSz);
|
|
|
|
#ifdef WOLFSSL_DTLS13
|
|
/* `ssl != NULL && ssl->dtls13FastTimeout` -- both operands; the flag is
|
|
* never set on a connection that has not scheduled fast retransmission */
|
|
(void)wolfSSL_dtls13_use_quick_timeout(NULL);
|
|
(void)wolfSSL_dtls13_use_quick_timeout(dssl);
|
|
if (dssl != NULL) {
|
|
dssl->dtls13FastTimeout = 1;
|
|
(void)wolfSSL_dtls13_use_quick_timeout(dssl);
|
|
dssl->dtls13FastTimeout = 0;
|
|
}
|
|
|
|
/* `ssl == NULL || !Dtls13ScheduledWorkReady(ssl)` and the pending-work
|
|
* chain below it: output buffered, a key update owed, an ack owed. Each
|
|
* flag is set directly because a connection only reaches these states
|
|
* mid-flight, between a write that blocked and its retry. */
|
|
(void)wolfSSL_dtls13_pending_work(NULL);
|
|
(void)wolfSSL_dtls13_pending_work(dssl);
|
|
if (dssl != NULL) {
|
|
dssl->options.handShakeDone = 1;
|
|
(void)wolfSSL_dtls13_pending_work(dssl);
|
|
dssl->dtls13DoKeyUpdate = 1;
|
|
(void)wolfSSL_dtls13_pending_work(dssl);
|
|
dssl->dtls13DoKeyUpdate = 0;
|
|
dssl->options.sendKeyUpdate = 1;
|
|
(void)wolfSSL_dtls13_pending_work(dssl);
|
|
dssl->options.sendKeyUpdate = 0;
|
|
dssl->dtls13SendingAckOrRtx = 1;
|
|
(void)wolfSSL_dtls13_pending_work(dssl);
|
|
dssl->dtls13SendingAckOrRtx = 0;
|
|
dssl->options.handShakeDone = 0;
|
|
}
|
|
(void)wolfSSL_dtls13_has_pending_msg(dssl);
|
|
#endif
|
|
|
|
#if !defined(NO_WOLFSSL_SERVER)
|
|
{
|
|
byte secret[16];
|
|
|
|
XMEMSET(secret, 0xC0, sizeof(secret));
|
|
/* `secret != NULL && secretSz == 0` -- the "clear the secret" call is
|
|
* (NULL, 0); a buffer with a zero length is the operand pair no
|
|
* caller produces */
|
|
(void)wolfSSL_DTLS_SetCookieSecret(NULL, secret, 0);
|
|
(void)wolfSSL_DTLS_SetCookieSecret(NULL, NULL, 0);
|
|
(void)wolfSSL_DTLS_SetCookieSecret(NULL, secret,
|
|
(word32)sizeof(secret));
|
|
}
|
|
#endif
|
|
|
|
wolfSSL_free(dssl);
|
|
wolfSSL_CTX_free(dctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|