mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-10-24 04:09:07 +03:00
3586 lines
126 KiB
C
3586 lines
126 KiB
C
/* test_session.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 <wolfssl/error-ssl.h>
|
|
#include <tests/api/api.h>
|
|
#include <tests/utils.h>
|
|
#include <tests/api/test_session.h>
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* WOLFSSL_CTX_add_session / session resumption */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(HAVE_EXT_CACHE) && \
|
|
!defined(SINGLE_THREADED) && defined(WOLFSSL_TLS13) && \
|
|
!defined(NO_SESSION_CACHE)
|
|
|
|
/* Sessions to restore/store */
|
|
static WOLFSSL_SESSION* test_wolfSSL_CTX_add_session_client_sess;
|
|
static WOLFSSL_SESSION* test_wolfSSL_CTX_add_session_server_sess;
|
|
static WOLFSSL_CTX* test_wolfSSL_CTX_add_session_server_ctx;
|
|
|
|
static void test_wolfSSL_CTX_add_session_ctx_ready(WOLFSSL_CTX* ctx)
|
|
{
|
|
/* Don't store sessions. Lookup is still enabled. */
|
|
AssertIntEQ(wolfSSL_CTX_set_session_cache_mode(ctx,
|
|
WOLFSSL_SESS_CACHE_NO_INTERNAL_STORE), WOLFSSL_SUCCESS);
|
|
#ifdef OPENSSL_EXTRA
|
|
AssertIntEQ(wolfSSL_CTX_get_session_cache_mode(ctx) &
|
|
WOLFSSL_SESS_CACHE_NO_INTERNAL_STORE,
|
|
WOLFSSL_SESS_CACHE_NO_INTERNAL_STORE);
|
|
#endif
|
|
/* Require both peers to provide certs */
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL);
|
|
}
|
|
|
|
static void test_wolfSSL_CTX_add_session_on_result(WOLFSSL* ssl)
|
|
{
|
|
WOLFSSL_SESSION** sess;
|
|
#ifdef WOLFSSL_MUTEX_INITIALIZER
|
|
static wolfSSL_Mutex m = WOLFSSL_MUTEX_INITIALIZER(m);
|
|
|
|
(void)wc_LockMutex(&m);
|
|
#endif
|
|
if (wolfSSL_is_server(ssl))
|
|
sess = &test_wolfSSL_CTX_add_session_server_sess;
|
|
else
|
|
sess = &test_wolfSSL_CTX_add_session_client_sess;
|
|
if (*sess == NULL) {
|
|
#ifdef NO_SESSION_CACHE_REF
|
|
*sess = wolfSSL_get1_session(ssl);
|
|
AssertNotNull(*sess);
|
|
#else
|
|
/* Test for backwards compatibility */
|
|
if (wolfSSL_is_server(ssl)) {
|
|
*sess = wolfSSL_get1_session(ssl);
|
|
AssertNotNull(*sess);
|
|
}
|
|
else {
|
|
*sess = wolfSSL_get_session(ssl);
|
|
AssertNotNull(*sess);
|
|
}
|
|
#endif
|
|
/* Now save the session in the internal store to make it available
|
|
* for lookup. For TLS 1.3, we can't save the session without
|
|
* WOLFSSL_TICKET_HAVE_ID because there is no way to retrieve the
|
|
* session from cache. */
|
|
if (wolfSSL_is_server(ssl)
|
|
#ifndef WOLFSSL_TICKET_HAVE_ID
|
|
&& wolfSSL_version(ssl) != TLS1_3_VERSION
|
|
#endif
|
|
)
|
|
AssertIntEQ(wolfSSL_CTX_add_session(wolfSSL_get_SSL_CTX(ssl),
|
|
*sess), WOLFSSL_SUCCESS);
|
|
}
|
|
else {
|
|
/* If we have a session retrieved then remaining connections should be
|
|
* resuming on that session */
|
|
AssertIntEQ(wolfSSL_session_reused(ssl), 1);
|
|
}
|
|
#ifdef WOLFSSL_MUTEX_INITIALIZER
|
|
wc_UnLockMutex(&m);
|
|
#endif
|
|
|
|
/* Save CTX to be able to decrypt tickets */
|
|
if (wolfSSL_is_server(ssl) &&
|
|
test_wolfSSL_CTX_add_session_server_ctx == NULL) {
|
|
test_wolfSSL_CTX_add_session_server_ctx = wolfSSL_get_SSL_CTX(ssl);
|
|
AssertNotNull(test_wolfSSL_CTX_add_session_server_ctx);
|
|
AssertIntEQ(wolfSSL_CTX_up_ref(wolfSSL_get_SSL_CTX(ssl)),
|
|
WOLFSSL_SUCCESS);
|
|
}
|
|
#if defined(SESSION_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH)
|
|
#ifndef WOLFSSL_TICKET_HAVE_ID
|
|
if (wolfSSL_version(ssl) != TLS1_3_VERSION &&
|
|
wolfSSL_session_reused(ssl))
|
|
#endif
|
|
{
|
|
/* With WOLFSSL_TICKET_HAVE_ID the peer certs should be available
|
|
* for all connections. TLS 1.3 only has tickets so if we don't
|
|
* include the session id in the ticket then the certificates
|
|
* will not be available on resumption. */
|
|
#ifdef KEEP_PEER_CERT
|
|
WOLFSSL_X509* peer = wolfSSL_get_peer_certificate(ssl);
|
|
AssertNotNull(peer);
|
|
wolfSSL_X509_free(peer);
|
|
#endif
|
|
AssertNotNull(wolfSSL_SESSION_get_peer_chain(*sess));
|
|
#ifdef OPENSSL_EXTRA
|
|
AssertNotNull(SSL_SESSION_get0_peer(*sess));
|
|
#endif
|
|
}
|
|
#endif /* SESSION_CERTS && !WOLFSSL_NO_CLIENT_AUTH */
|
|
}
|
|
|
|
static void test_wolfSSL_CTX_add_session_ssl_ready(WOLFSSL* ssl)
|
|
{
|
|
/* Set the session to reuse for the client */
|
|
AssertIntEQ(wolfSSL_set_session(ssl,
|
|
test_wolfSSL_CTX_add_session_client_sess), WOLFSSL_SUCCESS);
|
|
}
|
|
#endif
|
|
|
|
int test_wolfSSL_CTX_add_session(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(HAVE_EXT_CACHE) && \
|
|
!defined(SINGLE_THREADED) && defined(WOLFSSL_TLS13) && \
|
|
!defined(NO_SESSION_CACHE)
|
|
tcp_ready ready;
|
|
func_args client_args;
|
|
func_args server_args;
|
|
THREAD_TYPE serverThread;
|
|
callback_functions client_cb;
|
|
callback_functions server_cb;
|
|
method_provider methods[][2] = {
|
|
#if !defined(NO_OLD_TLS) && ((!defined(NO_AES) && !defined(NO_AES_CBC)) || \
|
|
!defined(NO_DES3))
|
|
/* Without AES there are almost no ciphersuites available. This leads
|
|
* to no ciphersuites being available and an error. */
|
|
{ wolfTLSv1_1_client_method, wolfTLSv1_1_server_method },
|
|
#endif
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
{ wolfTLSv1_2_client_method, wolfTLSv1_2_server_method },
|
|
#endif
|
|
/* Needs the default ticket callback since it is tied to the
|
|
* connection context and this makes it easy to carry over the ticket
|
|
* crypto context between connections */
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
defined(HAVE_SESSION_TICKET)
|
|
{ wolfTLSv1_3_client_method, wolfTLSv1_3_server_method },
|
|
#endif
|
|
};
|
|
const size_t methodsLen = sizeof(methods)/sizeof(*methods);
|
|
size_t i, j;
|
|
|
|
for (i = 0; i < methodsLen; i++) {
|
|
/* First run creates a connection while the second+ run will attempt
|
|
* to resume the connection. The trick is that the internal cache
|
|
* is turned off. wolfSSL_CTX_add_session should put the session in
|
|
* the cache anyway. */
|
|
test_wolfSSL_CTX_add_session_client_sess = NULL;
|
|
test_wolfSSL_CTX_add_session_server_sess = NULL;
|
|
test_wolfSSL_CTX_add_session_server_ctx = NULL;
|
|
|
|
#ifdef NO_SESSION_CACHE_REF
|
|
for (j = 0; j < 4; j++) {
|
|
#else
|
|
/* The session may be overwritten in this case. Do only one resumption
|
|
* to stop this test from failing intermittently. */
|
|
for (j = 0; j < 2; j++) {
|
|
#endif
|
|
#ifdef WOLFSSL_TIRTOS
|
|
fdOpenSession(Task_self());
|
|
#endif
|
|
|
|
StartTCP();
|
|
InitTcpReady(&ready);
|
|
|
|
XMEMSET(&client_args, 0, sizeof(func_args));
|
|
XMEMSET(&server_args, 0, sizeof(func_args));
|
|
|
|
XMEMSET(&client_cb, 0, sizeof(callback_functions));
|
|
XMEMSET(&server_cb, 0, sizeof(callback_functions));
|
|
client_cb.method = methods[i][0];
|
|
server_cb.method = methods[i][1];
|
|
|
|
server_args.signal = &ready;
|
|
server_args.callbacks = &server_cb;
|
|
client_args.signal = &ready;
|
|
client_args.callbacks = &client_cb;
|
|
|
|
if (test_wolfSSL_CTX_add_session_server_ctx != NULL) {
|
|
server_cb.ctx = test_wolfSSL_CTX_add_session_server_ctx;
|
|
server_cb.isSharedCtx = 1;
|
|
}
|
|
server_cb.ctx_ready = test_wolfSSL_CTX_add_session_ctx_ready;
|
|
client_cb.ctx_ready = test_wolfSSL_CTX_add_session_ctx_ready;
|
|
if (j != 0)
|
|
client_cb.ssl_ready = test_wolfSSL_CTX_add_session_ssl_ready;
|
|
server_cb.on_result = test_wolfSSL_CTX_add_session_on_result;
|
|
client_cb.on_result = test_wolfSSL_CTX_add_session_on_result;
|
|
server_cb.ticNoInit = 1; /* Use default builtin */
|
|
|
|
start_thread(test_server_nofail, &server_args, &serverThread);
|
|
wait_tcp_ready(&server_args);
|
|
test_client_nofail(&client_args, NULL);
|
|
join_thread(serverThread);
|
|
|
|
ExpectTrue(client_args.return_code);
|
|
ExpectTrue(server_args.return_code);
|
|
|
|
FreeTcpReady(&ready);
|
|
|
|
if (EXPECT_FAIL())
|
|
break;
|
|
}
|
|
wolfSSL_SESSION_free(test_wolfSSL_CTX_add_session_client_sess);
|
|
wolfSSL_SESSION_free(test_wolfSSL_CTX_add_session_server_sess);
|
|
wolfSSL_CTX_free(test_wolfSSL_CTX_add_session_server_ctx);
|
|
|
|
if (EXPECT_FAIL())
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && defined(HAVE_EXT_CACHE) && \
|
|
defined(WOLFSSL_TLS13) && !defined(NO_SESSION_CACHE) && \
|
|
defined(OPENSSL_EXTRA) && defined(SESSION_CERTS) && \
|
|
defined(HAVE_SESSION_TICKET) && \
|
|
!defined(TITAN_SESSION_CACHE) && \
|
|
!defined(HUGE_SESSION_CACHE) && \
|
|
!defined(BIG_SESSION_CACHE) && \
|
|
!defined(MEDIUM_SESSION_CACHE)
|
|
|
|
/* twcase - prefix for test_wolfSSL_CTX_add_session_ext */
|
|
/* Sessions to restore/store */
|
|
static WOLFSSL_SESSION* twcase_server_first_session_ptr;
|
|
static WOLFSSL_SESSION* twcase_client_first_session_ptr;
|
|
static WOLFSSL_CTX* twcase_server_current_ctx_ptr;
|
|
static int twcase_new_session_called = 0;
|
|
static int twcase_remove_session_called = 0;
|
|
static int twcase_get_session_called = 0;
|
|
|
|
/* Test default, SESSIONS_PER_ROW*SESSION_ROWS = 3*11, see ssl.c */
|
|
#define SESSION_CACHE_SIZE 33
|
|
|
|
typedef struct {
|
|
const byte* key; /* key, altSessionID, session ID, NULL if empty */
|
|
WOLFSSL_SESSION* value;
|
|
} hashTable_entry;
|
|
|
|
typedef struct {
|
|
hashTable_entry entries[SESSION_CACHE_SIZE]; /* hash slots */
|
|
size_t capacity; /* size of entries */
|
|
size_t length; /* number of items in the hash table */
|
|
wolfSSL_Mutex htLock; /* lock */
|
|
}hashTable;
|
|
|
|
static hashTable server_sessionCache;
|
|
|
|
static int twcase_new_sessionCb(WOLFSSL *ssl, WOLFSSL_SESSION *sess)
|
|
{
|
|
int i;
|
|
unsigned int len;
|
|
(void)ssl;
|
|
|
|
/*
|
|
* This example uses a hash table.
|
|
* Steps you should take for a non-demo code:
|
|
* - acquire a lock for the file named according to the session id
|
|
* - open the file
|
|
* - encrypt and write the SSL_SESSION object to the file
|
|
* - release the lock
|
|
*
|
|
* Return:
|
|
* 0: The callback does not wish to hold a reference of the sess
|
|
* 1: The callback wants to hold a reference of the sess. The callback is
|
|
* now also responsible for calling wolfSSL_SESSION_free() on sess.
|
|
*/
|
|
if (sess == NULL)
|
|
return 0;
|
|
|
|
if (wc_LockMutex(&server_sessionCache.htLock) != 0) {
|
|
return 0;
|
|
}
|
|
for (i = 0; i < SESSION_CACHE_SIZE; i++) {
|
|
if (server_sessionCache.entries[i].value == NULL) {
|
|
server_sessionCache.entries[i].key = SSL_SESSION_get_id(sess, &len);
|
|
server_sessionCache.entries[i].value = sess;
|
|
server_sessionCache.length++;
|
|
break;
|
|
}
|
|
}
|
|
++twcase_new_session_called;
|
|
wc_UnLockMutex(&server_sessionCache.htLock);
|
|
fprintf(stderr, "\t\ttwcase_new_session_called %d\n",
|
|
twcase_new_session_called);
|
|
return 1;
|
|
}
|
|
|
|
static void twcase_remove_sessionCb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess)
|
|
{
|
|
int i;
|
|
(void)ctx;
|
|
(void)sess;
|
|
|
|
if (sess == NULL)
|
|
return;
|
|
/*
|
|
* This example uses a hash table.
|
|
* Steps you should take for a non-demo code:
|
|
* - acquire a lock for the file named according to the session id
|
|
* - remove the file
|
|
* - release the lock
|
|
*/
|
|
if (wc_LockMutex(&server_sessionCache.htLock) != 0) {
|
|
return;
|
|
}
|
|
for (i = 0; i < SESSION_CACHE_SIZE; i++) {
|
|
if (server_sessionCache.entries[i].key != NULL &&
|
|
XMEMCMP(server_sessionCache.entries[i].key,
|
|
sess->sessionID, SSL_MAX_SSL_SESSION_ID_LENGTH) == 0) {
|
|
wolfSSL_SESSION_free(server_sessionCache.entries[i].value);
|
|
server_sessionCache.entries[i].value = NULL;
|
|
server_sessionCache.entries[i].key = NULL;
|
|
server_sessionCache.length--;
|
|
break;
|
|
}
|
|
}
|
|
++twcase_remove_session_called;
|
|
wc_UnLockMutex(&server_sessionCache.htLock);
|
|
fprintf(stderr, "\t\ttwcase_remove_session_called %d\n",
|
|
twcase_remove_session_called);
|
|
}
|
|
|
|
static WOLFSSL_SESSION *twcase_get_sessionCb(WOLFSSL *ssl,
|
|
const unsigned char *id, int len, int *ref)
|
|
{
|
|
int i;
|
|
(void)ssl;
|
|
(void)id;
|
|
(void)len;
|
|
|
|
/*
|
|
* This example uses a hash table.
|
|
* Steps you should take for a non-demo code:
|
|
* - acquire a lock for the file named according to the session id in the
|
|
* 2nd arg
|
|
* - read and decrypt contents of file and create a new SSL_SESSION
|
|
* - object release the lock
|
|
* - return the new session object
|
|
*/
|
|
fprintf(stderr, "\t\ttwcase_get_session_called %d\n",
|
|
++twcase_get_session_called);
|
|
/* This cache keeps its own reference to the session. */
|
|
*ref = 1;
|
|
|
|
for (i = 0; i < SESSION_CACHE_SIZE; i++) {
|
|
if (server_sessionCache.entries[i].key != NULL &&
|
|
XMEMCMP(server_sessionCache.entries[i].key, id,
|
|
SSL_MAX_SSL_SESSION_ID_LENGTH) == 0) {
|
|
return server_sessionCache.entries[i].value;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
static int twcase_get_sessionCb_cleanup(void)
|
|
{
|
|
int i;
|
|
int cnt = 0;
|
|
|
|
/* If twcase_get_sessionCb sets *ref = 1, the application is responsible
|
|
* for freeing sessions */
|
|
|
|
for (i = 0; i < SESSION_CACHE_SIZE; i++) {
|
|
if (server_sessionCache.entries[i].value != NULL) {
|
|
wolfSSL_SESSION_free(server_sessionCache.entries[i].value);
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
fprintf(stderr, "\t\ttwcase_get_sessionCb_cleanup freed %d sessions\n",
|
|
cnt);
|
|
|
|
return TEST_SUCCESS;
|
|
}
|
|
|
|
static int twcase_cache_intOff_extOff(WOLFSSL_CTX* ctx)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* off - Disable internal cache */
|
|
ExpectIntEQ(wolfSSL_CTX_set_session_cache_mode(ctx,
|
|
WOLFSSL_SESS_CACHE_NO_INTERNAL_STORE), WOLFSSL_SUCCESS);
|
|
#ifdef OPENSSL_EXTRA
|
|
ExpectIntEQ(wolfSSL_CTX_get_session_cache_mode(ctx) &
|
|
WOLFSSL_SESS_CACHE_NO_INTERNAL_STORE,
|
|
WOLFSSL_SESS_CACHE_NO_INTERNAL_STORE);
|
|
#endif
|
|
/* off - Do not setup external cache */
|
|
|
|
/* Require both peers to provide certs */
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL);
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
static int twcase_cache_intOn_extOff(WOLFSSL_CTX* ctx)
|
|
{
|
|
/* on - internal cache is on by default */
|
|
/* off - Do not setup external cache */
|
|
/* Require both peers to provide certs */
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL);
|
|
return TEST_SUCCESS;
|
|
}
|
|
|
|
static int twcase_cache_intOff_extOn(WOLFSSL_CTX* ctx)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* off - Disable internal cache */
|
|
ExpectIntEQ(wolfSSL_CTX_set_session_cache_mode(ctx,
|
|
WOLFSSL_SESS_CACHE_NO_INTERNAL_STORE), WOLFSSL_SUCCESS);
|
|
#ifdef OPENSSL_EXTRA
|
|
ExpectIntEQ(wolfSSL_CTX_get_session_cache_mode(ctx) &
|
|
WOLFSSL_SESS_CACHE_NO_INTERNAL_STORE,
|
|
WOLFSSL_SESS_CACHE_NO_INTERNAL_STORE);
|
|
#endif
|
|
/* on - Enable external cache */
|
|
wolfSSL_CTX_sess_set_new_cb(ctx, twcase_new_sessionCb);
|
|
wolfSSL_CTX_sess_set_remove_cb(ctx, twcase_remove_sessionCb);
|
|
wolfSSL_CTX_sess_set_get_cb(ctx, twcase_get_sessionCb);
|
|
|
|
/* Require both peers to provide certs */
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL);
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
static int twcase_cache_intOn_extOn(WOLFSSL_CTX* ctx)
|
|
{
|
|
/* on - internal cache is on by default */
|
|
/* on - Enable external cache */
|
|
wolfSSL_CTX_sess_set_new_cb(ctx, twcase_new_sessionCb);
|
|
wolfSSL_CTX_sess_set_remove_cb(ctx, twcase_remove_sessionCb);
|
|
wolfSSL_CTX_sess_set_get_cb(ctx, twcase_get_sessionCb);
|
|
|
|
/* Require both peers to provide certs */
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL);
|
|
return TEST_SUCCESS;
|
|
}
|
|
static int twcase_cache_intOn_extOn_noTicket(WOLFSSL_CTX* ctx)
|
|
{
|
|
/* on - internal cache is on by default */
|
|
/* on - Enable external cache */
|
|
wolfSSL_CTX_sess_set_new_cb(ctx, twcase_new_sessionCb);
|
|
wolfSSL_CTX_sess_set_remove_cb(ctx, twcase_remove_sessionCb);
|
|
wolfSSL_CTX_sess_set_get_cb(ctx, twcase_get_sessionCb);
|
|
|
|
wolfSSL_CTX_set_options(ctx, WOLFSSL_OP_NO_TICKET);
|
|
/* Require both peers to provide certs */
|
|
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL);
|
|
return TEST_SUCCESS;
|
|
}
|
|
#ifdef WOLFSSL_EARLY_DATA
|
|
/* Early data on: ticket resumption consults the external cache. */
|
|
static int twcase_cache_intOn_extOn_earlyData(WOLFSSL_CTX* ctx)
|
|
{
|
|
EXPECT_DECLS;
|
|
ExpectIntEQ(twcase_cache_intOn_extOn(ctx), TEST_SUCCESS);
|
|
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx, MAX_EARLY_DATA_SZ), 0);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
static int twcase_server_sess_ctx_pre_shutdown(WOLFSSL* ssl)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_SESSION** sess;
|
|
if (wolfSSL_is_server(ssl))
|
|
sess = &twcase_server_first_session_ptr;
|
|
else
|
|
return TEST_SUCCESS;
|
|
|
|
if (*sess == NULL) {
|
|
ExpectNotNull(*sess = wolfSSL_get1_session(ssl));
|
|
/* Now save the session in the internal store to make it available
|
|
* for lookup. For TLS 1.3, we can't save the session without
|
|
* WOLFSSL_TICKET_HAVE_ID because there is no way to retrieve the
|
|
* session from cache. */
|
|
if (wolfSSL_is_server(ssl)
|
|
#ifndef WOLFSSL_TICKET_HAVE_ID
|
|
&& wolfSSL_version(ssl) != TLS1_3_VERSION
|
|
&& wolfSSL_version(ssl) != DTLS1_3_VERSION
|
|
#endif
|
|
) {
|
|
ExpectIntEQ(wolfSSL_CTX_add_session(wolfSSL_get_SSL_CTX(ssl),
|
|
*sess), WOLFSSL_SUCCESS);
|
|
}
|
|
}
|
|
/* Save CTX to be able to decrypt tickets */
|
|
if (twcase_server_current_ctx_ptr == NULL) {
|
|
ExpectNotNull(twcase_server_current_ctx_ptr = wolfSSL_get_SSL_CTX(ssl));
|
|
ExpectIntEQ(wolfSSL_CTX_up_ref(wolfSSL_get_SSL_CTX(ssl)),
|
|
WOLFSSL_SUCCESS);
|
|
}
|
|
#if defined(SESSION_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH)
|
|
#ifndef WOLFSSL_TICKET_HAVE_ID
|
|
if (wolfSSL_version(ssl) != TLS1_3_VERSION &&
|
|
wolfSSL_session_reused(ssl))
|
|
#endif
|
|
{
|
|
/* With WOLFSSL_TICKET_HAVE_ID the peer certs should be available
|
|
* for all connections. TLS 1.3 only has tickets so if we don't
|
|
* include the session id in the ticket then the certificates
|
|
* will not be available on resumption. */
|
|
#ifdef KEEP_PEER_CERT
|
|
WOLFSSL_X509* peer = NULL;
|
|
ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl));
|
|
wolfSSL_X509_free(peer);
|
|
#endif
|
|
ExpectNotNull(wolfSSL_SESSION_get_peer_chain(*sess));
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
static int twcase_client_sess_ctx_pre_shutdown(WOLFSSL* ssl)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_SESSION** sess;
|
|
sess = &twcase_client_first_session_ptr;
|
|
if (*sess == NULL) {
|
|
ExpectNotNull(*sess = wolfSSL_get1_session(ssl));
|
|
}
|
|
else {
|
|
/* If we have a session retrieved then remaining connections should be
|
|
* resuming on that session */
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl), 1);
|
|
}
|
|
|
|
#if defined(SESSION_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH)
|
|
#ifndef WOLFSSL_TICKET_HAVE_ID
|
|
if (wolfSSL_version(ssl) != TLS1_3_VERSION &&
|
|
wolfSSL_session_reused(ssl))
|
|
#endif
|
|
{
|
|
#ifdef KEEP_PEER_CERT
|
|
WOLFSSL_X509* peer = wolfSSL_get_peer_certificate(ssl);
|
|
ExpectNotNull(peer);
|
|
wolfSSL_X509_free(peer);
|
|
#endif
|
|
ExpectNotNull(wolfSSL_SESSION_get_peer_chain(*sess));
|
|
#ifdef OPENSSL_EXTRA
|
|
ExpectNotNull(wolfSSL_SESSION_get0_peer(*sess));
|
|
#endif
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
/* Client asks for a ticket so TLS 1.2 also resumes with one. */
|
|
static int twcase_client_use_ticket(WOLFSSL_CTX* ctx)
|
|
{
|
|
EXPECT_DECLS;
|
|
ExpectIntEQ(twcase_cache_intOff_extOff(ctx), TEST_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_UseSessionTicket(ctx), WOLFSSL_SUCCESS);
|
|
return EXPECT_RESULT();
|
|
}
|
|
static int twcase_resume_on_result(WOLFSSL* ssl)
|
|
{
|
|
EXPECT_DECLS;
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl), 1);
|
|
return EXPECT_RESULT();
|
|
}
|
|
static int twcase_client_set_sess_ssl_ready(WOLFSSL* ssl)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* Set the session to reuse for the client */
|
|
ExpectNotNull(ssl);
|
|
ExpectNotNull(twcase_client_first_session_ptr);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl,twcase_client_first_session_ptr),
|
|
WOLFSSL_SUCCESS);
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
struct test_add_session_ext_params {
|
|
method_provider client_meth;
|
|
method_provider server_meth;
|
|
const char* tls_version;
|
|
};
|
|
|
|
/* Marked WC_MAYBE_UNUSED: each registered test_wolfSSL_CTX_add_session_ext_*
|
|
* variant below calls this helper, but each variant is gated on a specific
|
|
* TLS/DTLS version combination. In builds where no version combination is
|
|
* enabled, the helper is defined but unused. */
|
|
static WC_MAYBE_UNUSED int test_wolfSSL_CTX_add_session_ext(
|
|
struct test_add_session_ext_params* param)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* Test the default 33 sessions */
|
|
int j;
|
|
int tls13 = XSTRSTR(param->tls_version, "TLSv1_3") != NULL;
|
|
int dtls = XSTRSTR(param->tls_version, "DTLS") != NULL;
|
|
#ifdef WOLFSSL_EARLY_DATA
|
|
/* Case 6 enables early data, which is TLS 1.3 only. */
|
|
const int cases = tls13 ? 7 : 6;
|
|
#else
|
|
const int cases = 6;
|
|
#endif
|
|
|
|
/* Clear cache before starting */
|
|
wolfSSL_CTX_flush_sessions(NULL, -1);
|
|
|
|
XMEMSET(&server_sessionCache, 0, sizeof(hashTable));
|
|
if (wc_InitMutex(&server_sessionCache.htLock) != 0)
|
|
return BAD_MUTEX_E;
|
|
server_sessionCache.capacity = SESSION_CACHE_SIZE;
|
|
|
|
fprintf(stderr, "\tBegin %s\n", param->tls_version);
|
|
for (j = 0; j < cases; j++) {
|
|
test_ssl_cbf client_cb;
|
|
test_ssl_cbf server_cb;
|
|
|
|
(void)dtls;
|
|
|
|
/* Test the cache configurations */
|
|
twcase_client_first_session_ptr = NULL;
|
|
twcase_server_first_session_ptr = NULL;
|
|
twcase_server_current_ctx_ptr = NULL;
|
|
twcase_new_session_called = 0;
|
|
twcase_remove_session_called = 0;
|
|
twcase_get_session_called = 0;
|
|
|
|
/* connection 1 - first connection */
|
|
fprintf(stderr, "\tconnect: %s: j=%d\n", param->tls_version, j);
|
|
|
|
XMEMSET(&client_cb, 0, sizeof(client_cb));
|
|
XMEMSET(&server_cb, 0, sizeof(server_cb));
|
|
client_cb.method = param->client_meth;
|
|
server_cb.method = param->server_meth;
|
|
|
|
if (dtls)
|
|
client_cb.doUdp = server_cb.doUdp = 1;
|
|
|
|
/* Setup internal and external cache */
|
|
switch (j) {
|
|
case 0:
|
|
/* SSL_OP_NO_TICKET stateful ticket case */
|
|
server_cb.ctx_ready = twcase_cache_intOn_extOn_noTicket;
|
|
break;
|
|
case 1:
|
|
server_cb.ctx_ready = twcase_cache_intOn_extOn;
|
|
break;
|
|
case 2:
|
|
server_cb.ctx_ready = twcase_cache_intOff_extOn;
|
|
break;
|
|
case 3:
|
|
server_cb.ctx_ready = twcase_cache_intOn_extOff;
|
|
break;
|
|
case 4:
|
|
server_cb.ctx_ready = twcase_cache_intOff_extOff;
|
|
break;
|
|
case 5:
|
|
/* ticket resumption for all versions */
|
|
server_cb.ctx_ready = twcase_cache_intOn_extOn;
|
|
break;
|
|
#ifdef WOLFSSL_EARLY_DATA
|
|
case 6:
|
|
server_cb.ctx_ready = twcase_cache_intOn_extOn_earlyData;
|
|
break;
|
|
#endif
|
|
}
|
|
if (j == 5)
|
|
client_cb.ctx_ready = twcase_client_use_ticket;
|
|
else
|
|
client_cb.ctx_ready = twcase_cache_intOff_extOff;
|
|
|
|
/* Add session to internal cache and save SSL session for testing */
|
|
server_cb.on_result = twcase_server_sess_ctx_pre_shutdown;
|
|
/* Save client SSL session for testing */
|
|
client_cb.on_result = twcase_client_sess_ctx_pre_shutdown;
|
|
server_cb.ticNoInit = 1; /* Use default builtin */
|
|
/* Don't free/release ctx */
|
|
server_cb.ctx = twcase_server_current_ctx_ptr;
|
|
server_cb.isSharedCtx = 1;
|
|
|
|
ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cb,
|
|
&server_cb, NULL), TEST_SUCCESS);
|
|
|
|
ExpectIntEQ(twcase_get_session_called, 0);
|
|
if (EXPECT_FAIL()) {
|
|
wolfSSL_SESSION_free(twcase_client_first_session_ptr);
|
|
wolfSSL_SESSION_free(twcase_server_first_session_ptr);
|
|
wolfSSL_CTX_free(twcase_server_current_ctx_ptr);
|
|
break;
|
|
}
|
|
|
|
switch (j) {
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 5:
|
|
case 6:
|
|
/* cache cannot be searched with out a connection */
|
|
/* Add a new session */
|
|
ExpectIntEQ(twcase_new_session_called, 1);
|
|
/* In twcase_server_sess_ctx_pre_shutdown
|
|
* wolfSSL_CTX_add_session which evicts the existing session
|
|
* in cache and adds it back in */
|
|
ExpectIntLE(twcase_remove_session_called, 1);
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
/* no external cache */
|
|
ExpectIntEQ(twcase_new_session_called, 0);
|
|
ExpectIntEQ(twcase_remove_session_called, 0);
|
|
break;
|
|
}
|
|
|
|
/* connection 2 - session resume */
|
|
fprintf(stderr, "\tresume: %s: j=%d\n", param->tls_version, j);
|
|
twcase_new_session_called = 0;
|
|
twcase_remove_session_called = 0;
|
|
twcase_get_session_called = 0;
|
|
server_cb.on_result = twcase_resume_on_result;
|
|
client_cb.on_result = twcase_resume_on_result;
|
|
server_cb.ticNoInit = 1; /* Use default builtin */
|
|
|
|
server_cb.ctx = twcase_server_current_ctx_ptr;
|
|
|
|
/* try session resumption */
|
|
client_cb.ssl_ready = twcase_client_set_sess_ssl_ready;
|
|
|
|
ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cb,
|
|
&server_cb, NULL), TEST_SUCCESS);
|
|
|
|
/* Clear cache before checking */
|
|
wolfSSL_CTX_flush_sessions(NULL, -1);
|
|
|
|
switch (j) {
|
|
case 0:
|
|
if (tls13) {
|
|
/* (D)TLSv1.3 stateful case */
|
|
/* cache hit */
|
|
/* DTLS accesses cache once for stateless parsing and
|
|
* once for stateful parsing */
|
|
ExpectIntEQ(twcase_get_session_called, !dtls ? 1 : 2);
|
|
|
|
/* (D)TLSv1.3 creates a new ticket,
|
|
* updates both internal and external cache */
|
|
ExpectIntEQ(twcase_new_session_called, 1);
|
|
/* A new session ID is created for a new ticket */
|
|
ExpectIntEQ(twcase_remove_session_called, 2);
|
|
|
|
}
|
|
else {
|
|
/* non (D)TLSv1.3 case, no update */
|
|
/* DTLS accesses cache once for stateless parsing and
|
|
* once for stateful parsing */
|
|
#ifdef WOLFSSL_DTLS_NO_HVR_ON_RESUME
|
|
ExpectIntEQ(twcase_get_session_called, !dtls ? 1 : 2);
|
|
#else
|
|
ExpectIntEQ(twcase_get_session_called, 1);
|
|
#endif
|
|
ExpectIntEQ(twcase_new_session_called, 0);
|
|
/* Called on session added in
|
|
* twcase_server_sess_ctx_pre_shutdown */
|
|
ExpectIntEQ(twcase_remove_session_called, 1);
|
|
}
|
|
break;
|
|
case 1:
|
|
if (tls13) {
|
|
/* (D)TLSv1.3 case */
|
|
/* Ticket resumption. The external cache is not
|
|
* consulted, as in OpenSSL. */
|
|
ExpectIntEQ(twcase_get_session_called, 0);
|
|
/* (D)TLSv1.3 creates a new ticket,
|
|
* updates both internal and external cache */
|
|
ExpectIntEQ(twcase_new_session_called, 1);
|
|
/* Called on session added in
|
|
* twcase_server_sess_ctx_pre_shutdown and by wolfSSL */
|
|
ExpectIntEQ(twcase_remove_session_called, 1);
|
|
}
|
|
else {
|
|
/* non (D)TLSv1.3 case, session ID resumption */
|
|
/* cache hit */
|
|
/* DTLS accesses cache once for stateless parsing and
|
|
* once for stateful parsing */
|
|
#ifdef WOLFSSL_DTLS_NO_HVR_ON_RESUME
|
|
ExpectIntEQ(twcase_get_session_called, !dtls ? 1 : 2);
|
|
#else
|
|
ExpectIntEQ(twcase_get_session_called, 1);
|
|
#endif
|
|
ExpectIntEQ(twcase_new_session_called, 0);
|
|
/* Called on session added in
|
|
* twcase_server_sess_ctx_pre_shutdown */
|
|
ExpectIntEQ(twcase_remove_session_called, 1);
|
|
}
|
|
break;
|
|
case 2:
|
|
if (tls13) {
|
|
/* (D)TLSv1.3 case */
|
|
/* Ticket resumption. The external cache is not
|
|
* consulted, as in OpenSSL. */
|
|
ExpectIntEQ(twcase_get_session_called, 0);
|
|
/* (D)TLSv1.3 creates a new ticket,
|
|
* updates both internal and external cache */
|
|
ExpectIntEQ(twcase_new_session_called, 1);
|
|
/* Called on session added in
|
|
* twcase_server_sess_ctx_pre_shutdown and by wolfSSL */
|
|
ExpectIntEQ(twcase_remove_session_called, 1);
|
|
}
|
|
else {
|
|
/* non (D)TLSv1.3 case, session ID resumption */
|
|
/* cache hit */
|
|
/* DTLS accesses cache once for stateless parsing and
|
|
* once for stateful parsing */
|
|
#ifdef WOLFSSL_DTLS_NO_HVR_ON_RESUME
|
|
ExpectIntEQ(twcase_get_session_called, !dtls ? 1 : 2);
|
|
#else
|
|
ExpectIntEQ(twcase_get_session_called, 1);
|
|
#endif
|
|
ExpectIntEQ(twcase_new_session_called, 0);
|
|
/* Called on session added in
|
|
* twcase_server_sess_ctx_pre_shutdown */
|
|
ExpectIntEQ(twcase_remove_session_called, 1);
|
|
}
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
/* no external cache */
|
|
ExpectIntEQ(twcase_get_session_called, 0);
|
|
ExpectIntEQ(twcase_new_session_called, 0);
|
|
ExpectIntEQ(twcase_remove_session_called, 0);
|
|
break;
|
|
case 5:
|
|
/* Ticket resumption. The external cache is not consulted,
|
|
* as in OpenSSL. */
|
|
ExpectIntEQ(twcase_get_session_called, 0);
|
|
if (tls13) {
|
|
ExpectIntEQ(twcase_new_session_called, 1);
|
|
ExpectIntEQ(twcase_remove_session_called, 1);
|
|
}
|
|
else {
|
|
/* The ticket is reused, no new session */
|
|
ExpectIntEQ(twcase_new_session_called, 0);
|
|
ExpectIntEQ(twcase_remove_session_called, 1);
|
|
}
|
|
break;
|
|
#ifdef WOLFSSL_EARLY_DATA
|
|
case 6:
|
|
/* Ticket resumption with early data on. The external cache
|
|
* takes part in anti-replay, so it is consulted. */
|
|
ExpectIntEQ(twcase_get_session_called, 1);
|
|
ExpectIntEQ(twcase_new_session_called, 1);
|
|
/* A 0-RTT capable ticket is cached under a new ID, so the
|
|
* flush removes the first and the reissued session. */
|
|
ExpectIntEQ(twcase_remove_session_called, 2);
|
|
break;
|
|
#endif
|
|
}
|
|
wolfSSL_SESSION_free(twcase_client_first_session_ptr);
|
|
wolfSSL_SESSION_free(twcase_server_first_session_ptr);
|
|
wolfSSL_CTX_free(twcase_server_current_ctx_ptr);
|
|
|
|
if (EXPECT_FAIL())
|
|
break;
|
|
}
|
|
twcase_get_sessionCb_cleanup();
|
|
XMEMSET(&server_sessionCache.entries, 0,
|
|
sizeof(server_sessionCache.entries));
|
|
fprintf(stderr, "\tEnd %s\n", param->tls_version);
|
|
|
|
wc_FreeMutex(&server_sessionCache.htLock);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
int test_wolfSSL_CTX_add_session_ext_tls13(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(HAVE_EXT_CACHE) && \
|
|
defined(WOLFSSL_TLS13) && !defined(NO_SESSION_CACHE) && \
|
|
defined(OPENSSL_EXTRA) && defined(SESSION_CERTS) && \
|
|
defined(HAVE_SESSION_TICKET) && \
|
|
!defined(TITAN_SESSION_CACHE) && \
|
|
!defined(HUGE_SESSION_CACHE) && \
|
|
!defined(BIG_SESSION_CACHE) && \
|
|
!defined(MEDIUM_SESSION_CACHE)
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID)
|
|
struct test_add_session_ext_params param[1] = {
|
|
{ wolfTLSv1_3_client_method, wolfTLSv1_3_server_method, "TLSv1_3" }
|
|
};
|
|
ExpectIntEQ(test_wolfSSL_CTX_add_session_ext(param), TEST_SUCCESS);
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
int test_wolfSSL_CTX_add_session_ext_dtls13(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(HAVE_EXT_CACHE) && \
|
|
defined(WOLFSSL_TLS13) && !defined(NO_SESSION_CACHE) && \
|
|
defined(OPENSSL_EXTRA) && defined(SESSION_CERTS) && \
|
|
defined(HAVE_SESSION_TICKET) && \
|
|
!defined(TITAN_SESSION_CACHE) && \
|
|
!defined(HUGE_SESSION_CACHE) && \
|
|
!defined(BIG_SESSION_CACHE) && \
|
|
!defined(MEDIUM_SESSION_CACHE)
|
|
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID)
|
|
#ifdef WOLFSSL_DTLS13
|
|
struct test_add_session_ext_params param[1] = {
|
|
{ wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method, "DTLSv1_3" }
|
|
};
|
|
ExpectIntEQ(test_wolfSSL_CTX_add_session_ext(param), TEST_SUCCESS);
|
|
#endif
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
int test_wolfSSL_CTX_add_session_ext_tls12(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(HAVE_EXT_CACHE) && \
|
|
defined(WOLFSSL_TLS13) && !defined(NO_SESSION_CACHE) && \
|
|
defined(OPENSSL_EXTRA) && defined(SESSION_CERTS) && \
|
|
defined(HAVE_SESSION_TICKET) && \
|
|
!defined(TITAN_SESSION_CACHE) && \
|
|
!defined(HUGE_SESSION_CACHE) && \
|
|
!defined(BIG_SESSION_CACHE) && \
|
|
!defined(MEDIUM_SESSION_CACHE)
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
struct test_add_session_ext_params param[1] = {
|
|
{ wolfTLSv1_2_client_method, wolfTLSv1_2_server_method, "TLSv1_2" }
|
|
};
|
|
ExpectIntEQ(test_wolfSSL_CTX_add_session_ext(param), TEST_SUCCESS);
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
int test_wolfSSL_CTX_add_session_ext_dtls12(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(HAVE_EXT_CACHE) && \
|
|
defined(WOLFSSL_TLS13) && !defined(NO_SESSION_CACHE) && \
|
|
defined(OPENSSL_EXTRA) && defined(SESSION_CERTS) && \
|
|
defined(HAVE_SESSION_TICKET) && \
|
|
!defined(TITAN_SESSION_CACHE) && \
|
|
!defined(HUGE_SESSION_CACHE) && \
|
|
!defined(BIG_SESSION_CACHE) && \
|
|
!defined(MEDIUM_SESSION_CACHE)
|
|
#ifndef WOLFSSL_NO_TLS12
|
|
#ifdef WOLFSSL_DTLS
|
|
struct test_add_session_ext_params param[1] = {
|
|
{ wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method, "DTLSv1_2" }
|
|
};
|
|
ExpectIntEQ(test_wolfSSL_CTX_add_session_ext(param), TEST_SUCCESS);
|
|
#endif
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
int test_wolfSSL_CTX_add_session_ext_tls11(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(HAVE_EXT_CACHE) && \
|
|
defined(WOLFSSL_TLS13) && !defined(NO_SESSION_CACHE) && \
|
|
defined(OPENSSL_EXTRA) && defined(SESSION_CERTS) && \
|
|
defined(HAVE_SESSION_TICKET) && \
|
|
!defined(TITAN_SESSION_CACHE) && \
|
|
!defined(HUGE_SESSION_CACHE) && \
|
|
!defined(BIG_SESSION_CACHE) && \
|
|
!defined(MEDIUM_SESSION_CACHE)
|
|
#if !defined(NO_OLD_TLS) && ((!defined(NO_AES) && !defined(NO_AES_CBC)) || \
|
|
!defined(NO_DES3))
|
|
struct test_add_session_ext_params param[1] = {
|
|
{ wolfTLSv1_1_client_method, wolfTLSv1_1_server_method, "TLSv1_1" }
|
|
};
|
|
ExpectIntEQ(test_wolfSSL_CTX_add_session_ext(param), TEST_SUCCESS);
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
int test_wolfSSL_CTX_add_session_ext_dtls1(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && defined(HAVE_EXT_CACHE) && \
|
|
defined(WOLFSSL_TLS13) && !defined(NO_SESSION_CACHE) && \
|
|
defined(OPENSSL_EXTRA) && defined(SESSION_CERTS) && \
|
|
defined(HAVE_SESSION_TICKET) && \
|
|
!defined(TITAN_SESSION_CACHE) && \
|
|
!defined(HUGE_SESSION_CACHE) && \
|
|
!defined(BIG_SESSION_CACHE) && \
|
|
!defined(MEDIUM_SESSION_CACHE)
|
|
#if !defined(NO_OLD_TLS) && ((!defined(NO_AES) && !defined(NO_AES_CBC)) || \
|
|
!defined(NO_DES3))
|
|
#ifdef WOLFSSL_DTLS
|
|
struct test_add_session_ext_params param[1] = {
|
|
{ wolfDTLSv1_client_method, wolfDTLSv1_server_method, "DTLSv1_0" }
|
|
};
|
|
ExpectIntEQ(test_wolfSSL_CTX_add_session_ext(param), TEST_SUCCESS);
|
|
#endif
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && defined(HAVE_EXT_CACHE) && \
|
|
!defined(NO_SESSION_CACHE) && defined(OPENSSL_EXTRA) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
|
|
/* tdcase - prefix for test_wolfSSL_CTX_sess_get_cb_default_copy */
|
|
static WOLFSSL_SESSION* tdcase_server_session_ptr = NULL;
|
|
static WOLFSSL_SESSION* tdcase_client_session_ptr = NULL;
|
|
static int tdcase_get_session_called = 0;
|
|
static int tdcase_clear_copy = 0;
|
|
|
|
static WOLFSSL_SESSION* tdcase_get_sessionCb(WOLFSSL* ssl,
|
|
const unsigned char* id, int len, int* copy)
|
|
{
|
|
(void)ssl;
|
|
(void)id;
|
|
(void)len;
|
|
|
|
/* Left alone, this cache keeps its session. Cleared, it hands the
|
|
* reference to wolfSSL. */
|
|
if (tdcase_clear_copy)
|
|
*copy = 0;
|
|
tdcase_get_session_called++;
|
|
return tdcase_server_session_ptr;
|
|
}
|
|
|
|
static int tdcase_server_ctx_ready(WOLFSSL_CTX* ctx)
|
|
{
|
|
EXPECT_DECLS;
|
|
|
|
/* Only the external cache, so nothing else can reach the session. */
|
|
ExpectIntEQ(wolfSSL_CTX_set_session_cache_mode(ctx,
|
|
WOLFSSL_SESS_CACHE_NO_INTERNAL_STORE), WOLFSSL_SUCCESS);
|
|
wolfSSL_CTX_sess_set_get_cb(ctx, tdcase_get_sessionCb);
|
|
wolfSSL_CTX_set_options(ctx, WOLFSSL_OP_NO_TICKET);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
static int tdcase_on_result(WOLFSSL* ssl)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_SESSION** sess;
|
|
|
|
sess = wolfSSL_is_server(ssl) ? &tdcase_server_session_ptr
|
|
: &tdcase_client_session_ptr;
|
|
if (*sess == NULL)
|
|
ExpectNotNull(*sess = wolfSSL_get1_session(ssl));
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
static int tdcase_client_ssl_ready(WOLFSSL* ssl)
|
|
{
|
|
EXPECT_DECLS;
|
|
|
|
ExpectNotNull(tdcase_client_session_ptr);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl, tdcase_client_session_ptr),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
int test_wolfSSL_CTX_sess_get_cb_default_copy(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && defined(HAVE_EXT_CACHE) && \
|
|
!defined(NO_SESSION_CACHE) && defined(OPENSSL_EXTRA) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
test_ssl_cbf client_cb;
|
|
test_ssl_cbf server_cb;
|
|
int refBefore = 0;
|
|
|
|
tdcase_server_session_ptr = NULL;
|
|
tdcase_client_session_ptr = NULL;
|
|
tdcase_get_session_called = 0;
|
|
tdcase_clear_copy = 0;
|
|
|
|
XMEMSET(&client_cb, 0, sizeof(client_cb));
|
|
XMEMSET(&server_cb, 0, sizeof(server_cb));
|
|
client_cb.method = wolfTLSv1_2_client_method;
|
|
server_cb.method = wolfTLSv1_2_server_method;
|
|
server_cb.ctx_ready = tdcase_server_ctx_ready;
|
|
server_cb.on_result = tdcase_on_result;
|
|
client_cb.on_result = tdcase_on_result;
|
|
|
|
/* connection 1 - full handshake */
|
|
ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cb,
|
|
&server_cb, NULL), TEST_SUCCESS);
|
|
ExpectNotNull(tdcase_server_session_ptr);
|
|
ExpectNotNull(tdcase_client_session_ptr);
|
|
ExpectIntEQ(tdcase_get_session_called, 0);
|
|
|
|
/* Hold a second reference so a dropped one is still observable. */
|
|
ExpectIntEQ(wolfSSL_SESSION_up_ref(tdcase_server_session_ptr),
|
|
WOLFSSL_SUCCESS);
|
|
if (tdcase_server_session_ptr != NULL)
|
|
refBefore = (int)wolfSSL_RefCur(tdcase_server_session_ptr->ref);
|
|
|
|
/* connection 2 - resume through the callback, which never writes copy */
|
|
server_cb.on_result = NULL;
|
|
client_cb.on_result = NULL;
|
|
client_cb.ssl_ready = tdcase_client_ssl_ready;
|
|
|
|
ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cb,
|
|
&server_cb, NULL), TEST_SUCCESS);
|
|
ExpectIntEQ(tdcase_get_session_called, 1);
|
|
|
|
if (tdcase_server_session_ptr != NULL) {
|
|
ExpectIntEQ((int)wolfSSL_RefCur(tdcase_server_session_ptr->ref),
|
|
refBefore);
|
|
}
|
|
|
|
/* connection 3 - same resume, but the callback clears copy */
|
|
tdcase_clear_copy = 1;
|
|
tdcase_get_session_called = 0;
|
|
|
|
ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cb,
|
|
&server_cb, NULL), TEST_SUCCESS);
|
|
ExpectIntEQ(tdcase_get_session_called, 1);
|
|
|
|
if (tdcase_server_session_ptr != NULL) {
|
|
ExpectIntEQ((int)wolfSSL_RefCur(tdcase_server_session_ptr->ref),
|
|
refBefore - 1);
|
|
}
|
|
|
|
/* The cleared copy consumed one of the two references held here. */
|
|
wolfSSL_SESSION_free(tdcase_server_session_ptr);
|
|
wolfSSL_SESSION_free(tdcase_client_session_ptr);
|
|
tdcase_server_session_ptr = NULL;
|
|
tdcase_client_session_ptr = NULL;
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* WOLFSSL_SESSION, ticket keys and session removal callbacks */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
int test_wolfSSL_SESSION(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
|
|
!defined(NO_RSA) && !defined(NO_SHA256) && \
|
|
defined(HAVE_IO_TESTS_DEPENDENCIES) && !defined(NO_SESSION_CACHE)
|
|
WOLFSSL* ssl = NULL;
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
WOLFSSL_SESSION* sess_copy = NULL;
|
|
#ifdef OPENSSL_EXTRA
|
|
#ifdef HAVE_EXT_CACHE
|
|
unsigned char* sessDer = NULL;
|
|
unsigned char* ptr = NULL;
|
|
int sz = 0;
|
|
#endif
|
|
const unsigned char context[] = "user app context";
|
|
unsigned int contextSz = (unsigned int)sizeof(context);
|
|
#endif
|
|
int ret = 0, err = 0;
|
|
SOCKET_T sockfd;
|
|
tcp_ready ready;
|
|
func_args server_args;
|
|
THREAD_TYPE serverThread;
|
|
char msg[80];
|
|
const char* sendGET = "GET";
|
|
|
|
/* TLS v1.3 requires session tickets */
|
|
/* CHACHA and POLY1305 required for myTicketEncCb */
|
|
#if !defined(WOLFSSL_NO_TLS12) && (!defined(WOLFSSL_TLS13) || \
|
|
!(defined(HAVE_SESSION_TICKET) && ((defined(HAVE_CHACHA) && \
|
|
defined(HAVE_POLY1305)) || defined(HAVE_AESGCM))))
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
|
|
#else
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
#endif
|
|
|
|
ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx, cliCertFile,
|
|
CERT_FILETYPE));
|
|
ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, cliKeyFile,
|
|
CERT_FILETYPE));
|
|
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx, caCertFile, 0),
|
|
WOLFSSL_SUCCESS);
|
|
#ifdef WOLFSSL_ENCRYPTED_KEYS
|
|
wolfSSL_CTX_set_default_passwd_cb(ctx, PasswordCallBack);
|
|
#endif
|
|
#ifdef HAVE_SESSION_TICKET
|
|
/* Use session tickets, for ticket tests below */
|
|
ExpectIntEQ(wolfSSL_CTX_UseSessionTicket(ctx), WOLFSSL_SUCCESS);
|
|
#endif
|
|
|
|
XMEMSET(&server_args, 0, sizeof(func_args));
|
|
#ifdef WOLFSSL_TIRTOS
|
|
fdOpenSession(Task_self());
|
|
#endif
|
|
|
|
StartTCP();
|
|
InitTcpReady(&ready);
|
|
|
|
server_args.signal = &ready;
|
|
start_thread(test_server_nofail, &server_args, &serverThread);
|
|
wait_tcp_ready(&server_args);
|
|
|
|
/* client connection */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
tcp_connect(&sockfd, wolfSSLIP, ready.port, 0, 0, ssl);
|
|
ExpectIntEQ(wolfSSL_set_fd(ssl, sockfd), WOLFSSL_SUCCESS);
|
|
|
|
WOLFSSL_ASYNC_WHILE_PENDING(ret = wolfSSL_connect(ssl),
|
|
ret != WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
|
|
|
|
WOLFSSL_ASYNC_WHILE_PENDING(
|
|
ret = wolfSSL_write(ssl, sendGET, (int)XSTRLEN(sendGET)),
|
|
ret <= 0);
|
|
ExpectIntEQ(ret, (int)XSTRLEN(sendGET));
|
|
|
|
WOLFSSL_ASYNC_WHILE_PENDING(ret = wolfSSL_read(ssl, msg, sizeof(msg)),
|
|
ret != 23);
|
|
ExpectIntEQ(ret, 23);
|
|
|
|
ExpectPtrNE((sess = wolfSSL_get1_session(ssl)), NULL); /* ref count 1 */
|
|
ExpectPtrNE((sess_copy = wolfSSL_get1_session(ssl)), NULL); /* ref count 2 */
|
|
ExpectIntEQ(wolfSSL_SessionIsSetup(sess), 1);
|
|
#ifdef HAVE_EXT_CACHE
|
|
ExpectPtrEq(sess, sess_copy); /* they should be the same pointer but without
|
|
* HAVE_EXT_CACHE we get new objects each time */
|
|
#endif
|
|
wolfSSL_SESSION_free(sess_copy); sess_copy = NULL;
|
|
wolfSSL_SESSION_free(sess); sess = NULL; /* free session ref */
|
|
|
|
sess = wolfSSL_get_session(ssl);
|
|
|
|
#ifdef OPENSSL_EXTRA
|
|
ExpectIntEQ(SSL_SESSION_is_resumable(NULL), 0);
|
|
ExpectIntEQ(SSL_SESSION_is_resumable(sess), 1);
|
|
|
|
ExpectIntEQ(wolfSSL_SESSION_has_ticket(NULL), 0);
|
|
ExpectIntEQ(wolfSSL_SESSION_get_ticket_lifetime_hint(NULL), 0);
|
|
#ifdef HAVE_SESSION_TICKET
|
|
ExpectIntEQ(wolfSSL_SESSION_has_ticket(sess), 1);
|
|
ExpectIntEQ(wolfSSL_SESSION_get_ticket_lifetime_hint(sess),
|
|
SESSION_TICKET_HINT_DEFAULT);
|
|
#else
|
|
ExpectIntEQ(wolfSSL_SESSION_has_ticket(sess), 0);
|
|
#endif
|
|
#else
|
|
(void)sess;
|
|
#endif /* OPENSSL_EXTRA */
|
|
|
|
/* Retain copy of the session for later testing */
|
|
ExpectNotNull(sess = wolfSSL_get1_session(ssl));
|
|
|
|
wolfSSL_shutdown(ssl);
|
|
wolfSSL_free(ssl); ssl = NULL;
|
|
|
|
CloseSocket(sockfd);
|
|
|
|
join_thread(serverThread);
|
|
|
|
FreeTcpReady(&ready);
|
|
|
|
#ifdef WOLFSSL_TIRTOS
|
|
fdOpenSession(Task_self());
|
|
#endif
|
|
|
|
#if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA)
|
|
{
|
|
X509 *x509 = NULL;
|
|
char buf[30];
|
|
int bufSz = 0;
|
|
|
|
ExpectNotNull(x509 = SSL_SESSION_get0_peer(sess));
|
|
ExpectIntGT((bufSz = X509_NAME_get_text_by_NID(
|
|
X509_get_subject_name(x509), NID_organizationalUnitName, buf,
|
|
sizeof(buf))), 0);
|
|
ExpectIntNE((bufSz == 7 || bufSz == 16), 0); /* should be one of these*/
|
|
if (bufSz == 7) {
|
|
ExpectIntEQ(XMEMCMP(buf, "Support", bufSz), 0);
|
|
}
|
|
if (bufSz == 16) {
|
|
ExpectIntEQ(XMEMCMP(buf, "Programming-2048", bufSz), 0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_EXT_CACHE
|
|
ExpectNotNull(sess_copy = wolfSSL_SESSION_dup(sess));
|
|
wolfSSL_SESSION_free(sess_copy); sess_copy = NULL;
|
|
sess_copy = NULL;
|
|
#endif
|
|
|
|
#if defined(OPENSSL_EXTRA) && defined(HAVE_EXT_CACHE)
|
|
/* get session from DER and update the timeout */
|
|
ExpectIntEQ(wolfSSL_i2d_SSL_SESSION(NULL, &sessDer), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntGT((sz = wolfSSL_i2d_SSL_SESSION(sess, &sessDer)), 0);
|
|
wolfSSL_SESSION_free(sess); sess = NULL;
|
|
sess = NULL;
|
|
ptr = sessDer;
|
|
ExpectNull(sess = wolfSSL_d2i_SSL_SESSION(NULL, NULL, sz));
|
|
ExpectNotNull(sess = wolfSSL_d2i_SSL_SESSION(NULL,
|
|
(const unsigned char**)&ptr, sz));
|
|
XFREE(sessDer, NULL, DYNAMIC_TYPE_OPENSSL);
|
|
sessDer = NULL;
|
|
|
|
ExpectIntGT(wolfSSL_SESSION_get_time(sess), 0);
|
|
ExpectIntEQ(wolfSSL_SSL_SESSION_set_timeout(sess, 500), SSL_SUCCESS);
|
|
#endif
|
|
|
|
/* successful set session test */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(wolfSSL_set_session(ssl, sess), WOLFSSL_SUCCESS);
|
|
|
|
#ifdef HAVE_SESSION_TICKET
|
|
/* Test set/get session ticket */
|
|
{
|
|
const char* ticket = "This is a session ticket";
|
|
char buf[64] = {0};
|
|
word32 bufSz = (word32)sizeof(buf);
|
|
word32 retSz = 0;
|
|
|
|
ExpectIntEQ(WOLFSSL_SUCCESS,
|
|
wolfSSL_set_SessionTicket(ssl, (byte *)ticket,
|
|
(word32)XSTRLEN(ticket)));
|
|
ExpectIntEQ(WOLFSSL_SUCCESS,
|
|
wolfSSL_get_SessionTicket(ssl, (byte *)buf, &bufSz));
|
|
ExpectStrEQ(ticket, buf);
|
|
|
|
/* return ticket length if buffer parameter is null */
|
|
wolfSSL_get_SessionTicket(ssl, NULL, &retSz);
|
|
ExpectIntEQ(bufSz, retSz);
|
|
}
|
|
#endif
|
|
|
|
#ifdef OPENSSL_EXTRA
|
|
/* session timeout case */
|
|
/* make the session to be expired */
|
|
ExpectIntEQ(SSL_SESSION_set_timeout(sess,1), SSL_SUCCESS);
|
|
XSLEEP_MS(1200);
|
|
|
|
/* SSL_set_session should reject specified session but return success
|
|
* if WOLFSSL_ERROR_CODE_OPENSSL macro is defined for OpenSSL compatibility.
|
|
*/
|
|
#if defined(WOLFSSL_ERROR_CODE_OPENSSL)
|
|
ExpectIntEQ(wolfSSL_set_session(ssl,sess), SSL_SUCCESS);
|
|
#else
|
|
ExpectIntEQ(wolfSSL_set_session(ssl,sess), WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_SSL_SESSION_set_timeout(sess, 500), SSL_SUCCESS);
|
|
|
|
#ifdef WOLFSSL_SESSION_ID_CTX
|
|
/* fail case with miss match session context IDs (use compatibility API) */
|
|
ExpectIntEQ(SSL_set_session_id_context(ssl, context, contextSz),
|
|
SSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_set_session(ssl, sess), WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
wolfSSL_free(ssl); ssl = NULL;
|
|
|
|
ExpectIntEQ(SSL_CTX_set_session_id_context(NULL, context, contextSz),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(SSL_CTX_set_session_id_context(ctx, context, contextSz),
|
|
SSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
|
ExpectIntEQ(wolfSSL_set_session(ssl, sess), WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
#endif
|
|
#endif /* OPENSSL_EXTRA */
|
|
|
|
wolfSSL_free(ssl);
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
|
|
!defined(NO_RSA) && defined(HAVE_IO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_SESSION_CACHE) && defined(OPENSSL_EXTRA) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
static WOLFSSL_SESSION* test_wolfSSL_SESSION_expire_sess = NULL;
|
|
|
|
static void test_wolfSSL_SESSION_expire_downgrade_ctx_ready(WOLFSSL_CTX* ctx)
|
|
{
|
|
#ifdef WOLFSSL_ERROR_CODE_OPENSSL
|
|
/* returns previous timeout value */
|
|
AssertIntEQ(wolfSSL_CTX_set_timeout(ctx, 1), 500);
|
|
#else
|
|
AssertIntEQ(wolfSSL_CTX_set_timeout(ctx, 1), WOLFSSL_SUCCESS);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* set the session to timeout in a second */
|
|
static void test_wolfSSL_SESSION_expire_downgrade_ssl_ready(WOLFSSL* ssl)
|
|
{
|
|
AssertIntEQ(wolfSSL_set_timeout(ssl, 2), 1);
|
|
}
|
|
|
|
|
|
/* store the client side session from the first successful connection */
|
|
static void test_wolfSSL_SESSION_expire_downgrade_ssl_result(WOLFSSL* ssl)
|
|
{
|
|
AssertPtrNE((test_wolfSSL_SESSION_expire_sess = wolfSSL_get1_session(ssl)),
|
|
NULL); /* ref count 1 */
|
|
}
|
|
|
|
|
|
/* wait till session is expired then set it in the WOLFSSL struct for use */
|
|
static void test_wolfSSL_SESSION_expire_downgrade_ssl_ready_wait(WOLFSSL* ssl)
|
|
{
|
|
AssertIntEQ(wolfSSL_set_timeout(ssl, 1), 1);
|
|
AssertIntEQ(wolfSSL_set_session(ssl, test_wolfSSL_SESSION_expire_sess),
|
|
WOLFSSL_SUCCESS);
|
|
XSLEEP_MS(2000); /* wait 2 seconds for session to expire */
|
|
}
|
|
|
|
|
|
/* set expired session in the WOLFSSL struct for use */
|
|
static void test_wolfSSL_SESSION_expire_downgrade_ssl_ready_set(WOLFSSL* ssl)
|
|
{
|
|
XSLEEP_MS(1200); /* wait a second for session to expire */
|
|
|
|
/* set the expired session, call to set session fails but continuing on
|
|
after failure should be handled here */
|
|
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_ERROR_CODE_OPENSSL)
|
|
AssertIntEQ(wolfSSL_set_session(ssl, test_wolfSSL_SESSION_expire_sess),
|
|
WOLFSSL_SUCCESS);
|
|
#else
|
|
AssertIntNE(wolfSSL_set_session(ssl, test_wolfSSL_SESSION_expire_sess),
|
|
WOLFSSL_SUCCESS);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* check that the expired session was not reused */
|
|
static void test_wolfSSL_SESSION_expire_downgrade_ssl_result_reuse(WOLFSSL* ssl)
|
|
{
|
|
/* since the session has expired it should not have been reused */
|
|
AssertIntEQ(wolfSSL_session_reused(ssl), 0);
|
|
}
|
|
#endif
|
|
|
|
int test_wolfSSL_SESSION_expire_downgrade(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
|
|
!defined(NO_RSA) && defined(HAVE_IO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_SESSION_CACHE) && defined(OPENSSL_EXTRA) && \
|
|
!defined(WOLFSSL_NO_TLS12)
|
|
callback_functions server_cbf, client_cbf;
|
|
|
|
XMEMSET(&server_cbf, 0, sizeof(callback_functions));
|
|
XMEMSET(&client_cbf, 0, sizeof(callback_functions));
|
|
|
|
/* force server side to use TLS 1.2 */
|
|
server_cbf.method = wolfTLSv1_2_server_method;
|
|
|
|
client_cbf.method = wolfSSLv23_client_method;
|
|
server_cbf.ctx_ready = test_wolfSSL_SESSION_expire_downgrade_ctx_ready;
|
|
client_cbf.ssl_ready = test_wolfSSL_SESSION_expire_downgrade_ssl_ready;
|
|
client_cbf.on_result = test_wolfSSL_SESSION_expire_downgrade_ssl_result;
|
|
|
|
test_wolfSSL_client_server_nofail(&client_cbf, &server_cbf);
|
|
ExpectIntEQ(client_cbf.return_code, TEST_SUCCESS);
|
|
ExpectIntEQ(server_cbf.return_code, TEST_SUCCESS);
|
|
|
|
client_cbf.method = wolfSSLv23_client_method;
|
|
server_cbf.ctx_ready = test_wolfSSL_SESSION_expire_downgrade_ctx_ready;
|
|
client_cbf.ssl_ready = test_wolfSSL_SESSION_expire_downgrade_ssl_ready_wait;
|
|
client_cbf.on_result =
|
|
test_wolfSSL_SESSION_expire_downgrade_ssl_result_reuse;
|
|
|
|
test_wolfSSL_client_server_nofail(&client_cbf, &server_cbf);
|
|
ExpectIntEQ(client_cbf.return_code, TEST_SUCCESS);
|
|
ExpectIntEQ(server_cbf.return_code, TEST_SUCCESS);
|
|
|
|
client_cbf.method = wolfSSLv23_client_method;
|
|
server_cbf.ctx_ready = test_wolfSSL_SESSION_expire_downgrade_ctx_ready;
|
|
client_cbf.ssl_ready = test_wolfSSL_SESSION_expire_downgrade_ssl_ready_set;
|
|
client_cbf.on_result =
|
|
test_wolfSSL_SESSION_expire_downgrade_ssl_result_reuse;
|
|
|
|
test_wolfSSL_client_server_nofail(&client_cbf, &server_cbf);
|
|
ExpectIntEQ(client_cbf.return_code, TEST_SUCCESS);
|
|
ExpectIntEQ(server_cbf.return_code, TEST_SUCCESS);
|
|
|
|
wolfSSL_SESSION_free(test_wolfSSL_SESSION_expire_sess);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(OPENSSL_EXTRA) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_EX_DATA) && !defined(NO_SESSION_CACHE)
|
|
#ifdef WOLFSSL_ATOMIC_OPS
|
|
typedef wolfSSL_Atomic_Int SessRemCounter_t;
|
|
#else
|
|
typedef int SessRemCounter_t;
|
|
#endif
|
|
static SessRemCounter_t clientSessRemCountMalloc;
|
|
static SessRemCounter_t serverSessRemCountMalloc;
|
|
static SessRemCounter_t clientSessRemCountFree;
|
|
static SessRemCounter_t serverSessRemCountFree;
|
|
|
|
static WOLFSSL_CTX* serverSessCtx = NULL;
|
|
static WOLFSSL_SESSION* serverSess = NULL;
|
|
#if (defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)) || \
|
|
!defined(NO_SESSION_CACHE_REF)
|
|
static WOLFSSL_CTX* clientSessCtx = NULL;
|
|
static WOLFSSL_SESSION* clientSess = NULL;
|
|
#endif
|
|
static int serverSessRemIdx = 3;
|
|
static int sessRemCtx_Server = WOLFSSL_SERVER_END;
|
|
static int sessRemCtx_Client = WOLFSSL_CLIENT_END;
|
|
|
|
static void SessRemCtxCb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess)
|
|
{
|
|
int* side;
|
|
|
|
(void)ctx;
|
|
|
|
side = (int*)SSL_SESSION_get_ex_data(sess, serverSessRemIdx);
|
|
if (side != NULL) {
|
|
if (*side == WOLFSSL_CLIENT_END)
|
|
(void)wolfSSL_Atomic_Int_FetchAdd(&clientSessRemCountFree, 1);
|
|
else
|
|
(void)wolfSSL_Atomic_Int_FetchAdd(&serverSessRemCountFree, 1);
|
|
|
|
SSL_SESSION_set_ex_data(sess, serverSessRemIdx, NULL);
|
|
}
|
|
}
|
|
|
|
static int SessRemCtxSetupCb(WOLFSSL_CTX* ctx)
|
|
{
|
|
SSL_CTX_sess_set_remove_cb(ctx, SessRemCtxCb);
|
|
#if defined(WOLFSSL_TLS13) && !defined(HAVE_SESSION_TICKET) && \
|
|
!defined(NO_SESSION_CACHE_REF)
|
|
{
|
|
EXPECT_DECLS;
|
|
/* Allow downgrade, set min version, and disable TLS 1.3.
|
|
* Do this because without NO_SESSION_CACHE_REF we will want to return a
|
|
* reference to the session cache. But with WOLFSSL_TLS13 and without
|
|
* HAVE_SESSION_TICKET we won't have a session ID to be able to place
|
|
* the session in the cache. In this case we need to downgrade to
|
|
* previous versions to just use the legacy session ID field. */
|
|
ExpectIntEQ(SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION),
|
|
SSL_SUCCESS);
|
|
ExpectIntEQ(SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION),
|
|
SSL_SUCCESS);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#else
|
|
return TEST_SUCCESS;
|
|
#endif
|
|
}
|
|
|
|
static int SessRemSslSetupCb(WOLFSSL* ssl)
|
|
{
|
|
EXPECT_DECLS;
|
|
int* side;
|
|
|
|
if (SSL_is_server(ssl)) {
|
|
side = &sessRemCtx_Server;
|
|
(void)wolfSSL_Atomic_Int_FetchAdd(&serverSessRemCountMalloc, 1);
|
|
ExpectNotNull(serverSess = SSL_get1_session(ssl));
|
|
ExpectIntEQ(SSL_CTX_up_ref(serverSessCtx = SSL_get_SSL_CTX(ssl)),
|
|
SSL_SUCCESS);
|
|
}
|
|
else {
|
|
side = &sessRemCtx_Client;
|
|
(void)wolfSSL_Atomic_Int_FetchAdd(&clientSessRemCountMalloc, 1);
|
|
#if (defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)) || \
|
|
!defined(NO_SESSION_CACHE_REF)
|
|
ExpectNotNull(clientSess = SSL_get1_session(ssl));
|
|
ExpectIntEQ(SSL_CTX_up_ref(clientSessCtx = SSL_get_SSL_CTX(ssl)),
|
|
SSL_SUCCESS);
|
|
#endif
|
|
}
|
|
ExpectIntEQ(SSL_SESSION_set_ex_data(SSL_get_session(ssl),
|
|
serverSessRemIdx, side), SSL_SUCCESS);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
#endif
|
|
|
|
int test_wolfSSL_CTX_sess_set_remove_cb(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(OPENSSL_EXTRA) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
defined(HAVE_EX_DATA) && !defined(NO_SESSION_CACHE)
|
|
/* Check that the remove callback gets called for external data in a
|
|
* session object */
|
|
test_ssl_cbf func_cb;
|
|
|
|
wolfSSL_Atomic_Int_Init(&clientSessRemCountMalloc, 0);
|
|
wolfSSL_Atomic_Int_Init(&serverSessRemCountMalloc, 0);
|
|
wolfSSL_Atomic_Int_Init(&clientSessRemCountFree, 0);
|
|
wolfSSL_Atomic_Int_Init(&serverSessRemCountFree, 0);
|
|
|
|
XMEMSET(&func_cb, 0, sizeof(func_cb));
|
|
func_cb.ctx_ready = SessRemCtxSetupCb;
|
|
func_cb.on_result = SessRemSslSetupCb;
|
|
|
|
ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&func_cb, &func_cb,
|
|
NULL), TEST_SUCCESS);
|
|
|
|
/* Both should have been allocated */
|
|
ExpectIntEQ(clientSessRemCountMalloc, 1);
|
|
ExpectIntEQ(serverSessRemCountMalloc, 1);
|
|
|
|
/* This should not be called yet. Session wasn't evicted from cache yet. */
|
|
ExpectIntEQ(clientSessRemCountFree, 0);
|
|
#if (defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)) || \
|
|
!defined(NO_SESSION_CACHE_REF)
|
|
/* Force a cache lookup */
|
|
ExpectNotNull(SSL_SESSION_get_ex_data(clientSess, serverSessRemIdx));
|
|
/* Force a cache update */
|
|
ExpectNotNull(SSL_SESSION_set_ex_data(clientSess, serverSessRemIdx - 1, 0));
|
|
/* This should set the timeout to 0 and call the remove callback from within
|
|
* the session cache. Returns 1 per OpenSSL semantics (session was
|
|
* present in the cache and removed). */
|
|
ExpectIntEQ(SSL_CTX_remove_session(clientSessCtx, clientSess), 1);
|
|
ExpectNull(SSL_SESSION_get_ex_data(clientSess, serverSessRemIdx));
|
|
ExpectIntEQ(clientSessRemCountFree, 1);
|
|
#endif
|
|
/* Server session is in the cache so ex_data isn't free'd with the SSL
|
|
* object */
|
|
ExpectIntEQ(serverSessRemCountFree, 0);
|
|
/* Force a cache lookup */
|
|
ExpectNotNull(SSL_SESSION_get_ex_data(serverSess, serverSessRemIdx));
|
|
/* Force a cache update */
|
|
ExpectNotNull(SSL_SESSION_set_ex_data(serverSess, serverSessRemIdx - 1, 0));
|
|
/* This should set the timeout to 0 and call the remove callback from within
|
|
* the session cache. Returns 1 per OpenSSL semantics (session was
|
|
* present in the cache and removed). */
|
|
ExpectIntEQ(SSL_CTX_remove_session(serverSessCtx, serverSess), 1);
|
|
ExpectNull(SSL_SESSION_get_ex_data(serverSess, serverSessRemIdx));
|
|
ExpectIntEQ(serverSessRemCountFree, 1);
|
|
/* Need to free the references that we kept */
|
|
SSL_CTX_free(serverSessCtx);
|
|
SSL_SESSION_free(serverSess);
|
|
#if (defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET)) || \
|
|
!defined(NO_SESSION_CACHE_REF)
|
|
SSL_CTX_free(clientSessCtx);
|
|
SSL_SESSION_free(clientSess);
|
|
#endif
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_wolfSSL_ticket_keys(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS)
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
byte keys[WOLFSSL_TICKET_KEYS_SZ];
|
|
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_server_method()));
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_get_tlsext_ticket_keys(NULL, NULL, 0),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_get_tlsext_ticket_keys(ctx, NULL, 0),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_get_tlsext_ticket_keys(ctx, keys, 0),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_get_tlsext_ticket_keys(NULL, keys, 0),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_get_tlsext_ticket_keys(NULL, NULL, sizeof(keys)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_get_tlsext_ticket_keys(ctx, NULL, sizeof(keys)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_get_tlsext_ticket_keys(NULL, keys, sizeof(keys)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_set_tlsext_ticket_keys(NULL, NULL, 0),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_set_tlsext_ticket_keys(ctx, NULL, 0),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_set_tlsext_ticket_keys(ctx, keys, 0),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_set_tlsext_ticket_keys(NULL, keys, 0),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_set_tlsext_ticket_keys(NULL, NULL, sizeof(keys)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_set_tlsext_ticket_keys(ctx, NULL, sizeof(keys)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
ExpectIntEQ(wolfSSL_CTX_set_tlsext_ticket_keys(NULL, keys, sizeof(keys)),
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
|
|
|
|
ExpectIntEQ(wolfSSL_CTX_get_tlsext_ticket_keys(ctx, keys, sizeof(keys)),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_CTX_set_tlsext_ticket_keys(ctx, keys, sizeof(keys)),
|
|
WOLFSSL_SUCCESS);
|
|
|
|
wolfSSL_CTX_free(ctx);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) && !defined(SINGLE_THREADED)
|
|
|
|
#define TEST_TICKET_KEYS_ROUNDS 20000
|
|
#define TEST_TICKET_KEYS_WRITER_MAX 1000000
|
|
#define TEST_TICKET_KEYS_BURST 256
|
|
#define TEST_TICKET_KEYS_WAIT_TRIES 200
|
|
#define TEST_TICKET_KEYS_WAIT_MS 1
|
|
|
|
static WOLFSSL_CTX* ticket_keys_ctx = NULL;
|
|
static byte ticket_keys_a[WOLFSSL_TICKET_KEYS_SZ];
|
|
static byte ticket_keys_b[WOLFSSL_TICKET_KEYS_SZ];
|
|
static int ticket_keys_set_err = 0;
|
|
|
|
/* Guards the fields below, which both threads touch while running. */
|
|
static wolfSSL_Mutex ticket_keys_lock;
|
|
static int ticket_keys_writer_started = 0;
|
|
static int ticket_keys_reader_done = 0;
|
|
|
|
static THREAD_RETURN WOLFSSL_THREAD test_ticket_keys_writer(void* args)
|
|
{
|
|
int done = 0;
|
|
int i;
|
|
long rounds = 0;
|
|
word32 lcg = 1;
|
|
|
|
(void)args;
|
|
|
|
if (wc_LockMutex(&ticket_keys_lock) == 0) {
|
|
ticket_keys_writer_started = 1;
|
|
wc_UnLockMutex(&ticket_keys_lock);
|
|
}
|
|
|
|
/* Keep rotating until the reader is done so the loops overlap. The
|
|
* rotations run in bursts to keep the bookkeeping out of the loop. */
|
|
while ((!done) && (rounds < TEST_TICKET_KEYS_WRITER_MAX)) {
|
|
for (i = 0; i < TEST_TICKET_KEYS_BURST; i++) {
|
|
/* Pick the key set pseudo-randomly rather than strictly
|
|
* alternating: a strictly alternating burst always ends on the
|
|
* same key set, so a reader that only gets to run between bursts
|
|
* (see below) would see that one key set only. */
|
|
const byte* keys;
|
|
lcg = lcg * 1103515245U + 12345U;
|
|
keys = ((lcg >> 16) & 1) ? ticket_keys_a : ticket_keys_b;
|
|
if (wolfSSL_CTX_set_tlsext_ticket_keys(ticket_keys_ctx, keys,
|
|
WOLFSSL_TICKET_KEYS_SZ) != WOLFSSL_SUCCESS) {
|
|
ticket_keys_set_err = 1;
|
|
}
|
|
}
|
|
rounds += TEST_TICKET_KEYS_BURST;
|
|
|
|
if (wc_LockMutex(&ticket_keys_lock) == 0) {
|
|
done = ticket_keys_reader_done;
|
|
wc_UnLockMutex(&ticket_keys_lock);
|
|
}
|
|
|
|
/* Block briefly between bursts. A serializing scheduler with unfair
|
|
* hand-off (valgrind without --fair-sched) otherwise lets a thread
|
|
* that never blocks run to completion before the other thread gets a
|
|
* turn, so the reader would see only the final key set. */
|
|
if (!done) {
|
|
XSLEEP_MS(TEST_TICKET_KEYS_WAIT_MS);
|
|
}
|
|
}
|
|
|
|
WOLFSSL_RETURN_FROM_THREAD(0);
|
|
}
|
|
|
|
/* Read the keys once and record which key set came back. */
|
|
static int test_ticket_keys_read(int* sawA, int* sawB, int* mixed)
|
|
{
|
|
byte keys[WOLFSSL_TICKET_KEYS_SZ];
|
|
int ret = 0;
|
|
|
|
if (wolfSSL_CTX_get_tlsext_ticket_keys(ticket_keys_ctx, keys,
|
|
WOLFSSL_TICKET_KEYS_SZ) != WOLFSSL_SUCCESS) {
|
|
ret = -1;
|
|
}
|
|
else if (XMEMCMP(keys, ticket_keys_a, sizeof(keys)) == 0) {
|
|
*sawA = 1;
|
|
}
|
|
else if (XMEMCMP(keys, ticket_keys_b, sizeof(keys)) == 0) {
|
|
*sawB = 1;
|
|
}
|
|
/* Every read returns one key set whole, never part of each. */
|
|
else {
|
|
*mixed = 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
int test_wolfSSL_ticket_keys_threaded(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && \
|
|
!defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) && !defined(SINGLE_THREADED)
|
|
THREAD_TYPE thread;
|
|
func_args args;
|
|
int lockInit = 0;
|
|
int started = 0;
|
|
int sawA = 0;
|
|
int sawB = 0;
|
|
int getErr = 0;
|
|
int mixed = 0;
|
|
int i;
|
|
|
|
XMEMSET(&args, 0, sizeof(args));
|
|
XMEMSET(ticket_keys_a, 0xa5, sizeof(ticket_keys_a));
|
|
XMEMSET(ticket_keys_b, 0x5c, sizeof(ticket_keys_b));
|
|
ticket_keys_writer_started = 0;
|
|
ticket_keys_reader_done = 0;
|
|
ticket_keys_set_err = 0;
|
|
|
|
if (wc_InitMutex(&ticket_keys_lock) == 0) {
|
|
lockInit = 1;
|
|
}
|
|
ExpectIntEQ(lockInit, 1);
|
|
ExpectNotNull(ticket_keys_ctx =
|
|
wolfSSL_CTX_new(wolfSSLv23_server_method()));
|
|
ExpectIntEQ(wolfSSL_CTX_set_tlsext_ticket_keys(ticket_keys_ctx,
|
|
ticket_keys_a, WOLFSSL_TICKET_KEYS_SZ), WOLFSSL_SUCCESS);
|
|
|
|
if ((lockInit == 1) && (ticket_keys_ctx != NULL)) {
|
|
start_thread(test_ticket_keys_writer, &args, &thread);
|
|
|
|
/* Start the reads only once the writer is rotating keys. Sleep
|
|
* rather than spin so the writer gets scheduled on one core. */
|
|
for (i = 0; (i < TEST_TICKET_KEYS_WAIT_TRIES) && (!started); i++) {
|
|
if (wc_LockMutex(&ticket_keys_lock) == 0) {
|
|
started = ticket_keys_writer_started;
|
|
wc_UnLockMutex(&ticket_keys_lock);
|
|
}
|
|
if (!started) {
|
|
XSLEEP_MS(TEST_TICKET_KEYS_WAIT_MS);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < TEST_TICKET_KEYS_ROUNDS; i++) {
|
|
if (test_ticket_keys_read(&sawA, &sawB, &mixed) != 0) {
|
|
getErr = 1;
|
|
break;
|
|
}
|
|
if (mixed) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Top up until both key sets have been seen, so a clean run
|
|
* above cannot be one where the loops never overlapped. */
|
|
for (i = 0; (i < TEST_TICKET_KEYS_WAIT_TRIES) && (!getErr) &&
|
|
(!mixed) && ((!sawA) || (!sawB)); i++) {
|
|
XSLEEP_MS(TEST_TICKET_KEYS_WAIT_MS);
|
|
if (test_ticket_keys_read(&sawA, &sawB, &mixed) != 0) {
|
|
getErr = 1;
|
|
}
|
|
}
|
|
|
|
if (wc_LockMutex(&ticket_keys_lock) == 0) {
|
|
ticket_keys_reader_done = 1;
|
|
wc_UnLockMutex(&ticket_keys_lock);
|
|
}
|
|
join_thread(thread);
|
|
}
|
|
|
|
ExpectIntEQ(getErr, 0);
|
|
ExpectIntEQ(mixed, 0);
|
|
ExpectIntEQ(ticket_keys_set_err, 0);
|
|
|
|
ExpectIntEQ(started, 1);
|
|
ExpectIntEQ(sawA, 1);
|
|
ExpectIntEQ(sawB, 1);
|
|
|
|
wolfSSL_CTX_free(ticket_keys_ctx);
|
|
ticket_keys_ctx = NULL;
|
|
if (lockInit == 1) {
|
|
wc_FreeMutex(&ticket_keys_lock);
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* SESSION ex_data new index */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
#if defined(HAVE_EX_DATA_CRYPTO) && defined(OPENSSL_EXTRA)
|
|
|
|
#define SESSION_NEW_IDX_LONG 0xDEADBEEF
|
|
#define SESSION_NEW_IDX_VAL ((void*)0xAEADAEAD)
|
|
#define SESSION_DUP_IDX_VAL ((void*)0xDEDEDEDE)
|
|
#define SESSION_NEW_IDX_PTR "Testing"
|
|
|
|
static void test_wolfSSL_SESSION_get_ex_new_index_new_cb(void* p, void* ptr,
|
|
CRYPTO_EX_DATA* a, int idx, long argValue, void* arg)
|
|
{
|
|
AssertNotNull(p);
|
|
AssertNull(ptr);
|
|
AssertIntEQ(CRYPTO_set_ex_data(a, idx, SESSION_NEW_IDX_VAL), SSL_SUCCESS);
|
|
AssertIntEQ(argValue, SESSION_NEW_IDX_LONG);
|
|
AssertStrEQ(arg, SESSION_NEW_IDX_PTR);
|
|
}
|
|
|
|
static int test_wolfSSL_SESSION_get_ex_new_index_dup_cb(CRYPTO_EX_DATA* out,
|
|
const CRYPTO_EX_DATA* in, void* inPtr, int idx, long argV,
|
|
void* arg)
|
|
{
|
|
EXPECT_DECLS;
|
|
|
|
ExpectNotNull(out);
|
|
ExpectNotNull(in);
|
|
ExpectPtrEq(*(void**)inPtr, SESSION_NEW_IDX_VAL);
|
|
ExpectPtrEq(CRYPTO_get_ex_data(in, idx), SESSION_NEW_IDX_VAL);
|
|
ExpectPtrEq(CRYPTO_get_ex_data(out, idx), SESSION_NEW_IDX_VAL);
|
|
ExpectIntEQ(argV, SESSION_NEW_IDX_LONG);
|
|
ExpectStrEQ(arg, SESSION_NEW_IDX_PTR);
|
|
*(void**)inPtr = SESSION_DUP_IDX_VAL;
|
|
if (EXPECT_SUCCESS()) {
|
|
return SSL_SUCCESS;
|
|
}
|
|
else {
|
|
return SSL_FAILURE;
|
|
}
|
|
}
|
|
|
|
static int test_wolfSSL_SESSION_get_ex_new_index_free_cb_called = 0;
|
|
static void test_wolfSSL_SESSION_get_ex_new_index_free_cb(void* p, void* ptr,
|
|
CRYPTO_EX_DATA* a, int idx, long argValue, void* arg)
|
|
{
|
|
EXPECT_DECLS;
|
|
|
|
ExpectNotNull(p);
|
|
ExpectNull(ptr);
|
|
ExpectPtrNE(CRYPTO_get_ex_data(a, idx), 0);
|
|
ExpectIntEQ(argValue, SESSION_NEW_IDX_LONG);
|
|
ExpectStrEQ(arg, SESSION_NEW_IDX_PTR);
|
|
if (EXPECT_SUCCESS()) {
|
|
test_wolfSSL_SESSION_get_ex_new_index_free_cb_called++;
|
|
}
|
|
}
|
|
|
|
int test_wolfSSL_SESSION_get_ex_new_index(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
int idx = SSL_SESSION_get_ex_new_index(SESSION_NEW_IDX_LONG,
|
|
(void*)SESSION_NEW_IDX_PTR,
|
|
test_wolfSSL_SESSION_get_ex_new_index_new_cb,
|
|
test_wolfSSL_SESSION_get_ex_new_index_dup_cb,
|
|
test_wolfSSL_SESSION_get_ex_new_index_free_cb);
|
|
SSL_SESSION* s = SSL_SESSION_new();
|
|
SSL_SESSION* d = NULL;
|
|
|
|
ExpectNotNull(s);
|
|
ExpectPtrEq(SSL_SESSION_get_ex_data(s, idx), SESSION_NEW_IDX_VAL);
|
|
ExpectNotNull(d = SSL_SESSION_dup(s));
|
|
ExpectPtrEq(SSL_SESSION_get_ex_data(d, idx), SESSION_DUP_IDX_VAL);
|
|
SSL_SESSION_free(s);
|
|
ExpectIntEQ(test_wolfSSL_SESSION_get_ex_new_index_free_cb_called, 1);
|
|
SSL_SESSION_free(d);
|
|
ExpectIntEQ(test_wolfSSL_SESSION_get_ex_new_index_free_cb_called, 2);
|
|
|
|
crypto_ex_cb_free(crypto_ex_cb_ctx_session);
|
|
crypto_ex_cb_ctx_session = NULL;
|
|
return EXPECT_RESULT();
|
|
}
|
|
#else
|
|
int test_wolfSSL_SESSION_get_ex_new_index(void)
|
|
{
|
|
return TEST_SKIPPED;
|
|
}
|
|
#endif
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* wolfSSL_SESSION_dup */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
#if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) && \
|
|
defined(USE_WOLFSSL_MEMORY) && !defined(WOLFSSL_STATIC_MEMORY) && \
|
|
!defined(WOLFSSL_DEBUG_MEMORY) && !defined(WOLFSSL_CHECK_MEM_ZERO) && \
|
|
!defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_RSA)
|
|
static int session_dup_fail_x509_alloc = 0;
|
|
|
|
static void* session_dup_fail_malloc(size_t size)
|
|
{
|
|
if (session_dup_fail_x509_alloc && size == sizeof(WOLFSSL_X509))
|
|
return NULL;
|
|
return malloc(size);
|
|
}
|
|
|
|
static void session_dup_fail_free(void* ptr)
|
|
{
|
|
free(ptr);
|
|
}
|
|
|
|
static void* session_dup_fail_realloc(void* ptr, size_t size)
|
|
{
|
|
return realloc(ptr, size);
|
|
}
|
|
|
|
/* wolfSSL_SESSION_dup must fail, not return a copy without the peer
|
|
* certificate, when copying the source session's peer certificate fails. */
|
|
int test_wolfSSL_SESSION_dup_peer_fail(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
WOLFSSL_SESSION* dup = NULL;
|
|
wolfSSL_Malloc_cb prevM = NULL;
|
|
wolfSSL_Free_cb prevF = NULL;
|
|
wolfSSL_Realloc_cb prevR = NULL;
|
|
|
|
ExpectNotNull(sess = wolfSSL_SESSION_new());
|
|
if (sess != NULL) {
|
|
ExpectNotNull(sess->peer = wolfSSL_X509_load_certificate_file(
|
|
svrCertFile, CERT_FILETYPE));
|
|
}
|
|
#ifdef HAVE_SESSION_TICKET
|
|
/* Long ticket so the failed copy must not free sess's buffer. */
|
|
if (EXPECT_SUCCESS()) {
|
|
ExpectNotNull(sess->ticket = (byte*)XMALLOC(SESSION_TICKET_LEN + 1,
|
|
NULL, DYNAMIC_TYPE_SESSION_TICK));
|
|
if (sess->ticket == NULL)
|
|
sess->ticket = sess->staticTicket;
|
|
}
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMSET(sess->ticket, 0xC3, SESSION_TICKET_LEN + 1);
|
|
sess->ticketLen = SESSION_TICKET_LEN + 1;
|
|
sess->ticketLenAlloc = SESSION_TICKET_LEN + 1;
|
|
}
|
|
#endif
|
|
if (EXPECT_SUCCESS()) {
|
|
/* Take the deep-copy branch, which allocates a new certificate. */
|
|
sess->peer->dynamicMemory = 0;
|
|
|
|
ExpectIntEQ(wolfSSL_GetAllocators(&prevM, &prevF, &prevR), 0);
|
|
ExpectIntEQ(wolfSSL_SetAllocators(session_dup_fail_malloc,
|
|
session_dup_fail_free, session_dup_fail_realloc), 0);
|
|
session_dup_fail_x509_alloc = 1;
|
|
dup = wolfSSL_SESSION_dup(sess);
|
|
session_dup_fail_x509_alloc = 0;
|
|
(void)wolfSSL_SetAllocators(prevM, prevF, prevR);
|
|
|
|
ExpectNull(dup);
|
|
sess->peer->dynamicMemory = 1;
|
|
}
|
|
|
|
wolfSSL_SESSION_free(dup);
|
|
wolfSSL_SESSION_free(sess);
|
|
return EXPECT_RESULT();
|
|
}
|
|
#else
|
|
int test_wolfSSL_SESSION_dup_peer_fail(void)
|
|
{
|
|
return TEST_SKIPPED;
|
|
}
|
|
#endif
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* wolfSSL_GetSessionAtIndex */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
#if defined(SESSION_INDEX) && defined(HAVE_SESSION_TICKET) && \
|
|
!defined(NO_SESSION_CACHE) && !defined(NO_WOLFSSL_CLIENT) && \
|
|
!defined(NO_TLS)
|
|
|
|
/* Cache a client session under id with a ticLen byte ticket of fill bytes.
|
|
* ticLen over SESSION_TICKET_LEN makes the cache allocate a ticket buffer. */
|
|
static int test_session_at_index_add(WOLFSSL_CTX* ctx, const byte* id,
|
|
word16 ticLen, byte fill, int* idx)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
byte* tic = NULL;
|
|
|
|
ExpectNotNull(tic = (byte*)XMALLOC(ticLen, NULL, DYNAMIC_TYPE_TMP_BUFFER));
|
|
ExpectNotNull(sess = wolfSSL_SESSION_new());
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMSET(tic, fill, ticLen);
|
|
XMEMCPY(sess->sessionID, id, ID_LEN);
|
|
sess->sessionIDSz = ID_LEN;
|
|
sess->side = WOLFSSL_CLIENT_END;
|
|
sess->isSetup = 1;
|
|
/* Borrowed buffer - ticketLenAlloc stays 0 so sess does not free it. */
|
|
sess->ticket = tic;
|
|
sess->ticketLen = ticLen;
|
|
}
|
|
ExpectIntEQ(AddSessionToCache(ctx, sess, id, ID_LEN, idx,
|
|
WOLFSSL_CLIENT_END, 1, NULL), 0);
|
|
|
|
if (sess != NULL) {
|
|
sess->ticket = sess->staticTicket;
|
|
sess->ticketLen = 0;
|
|
wolfSSL_SESSION_free(sess);
|
|
}
|
|
XFREE(tic, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
static int test_session_at_index_ticket_is(const WOLFSSL_SESSION* sess,
|
|
word16 ticLen, byte fill)
|
|
{
|
|
word16 i;
|
|
|
|
if ((sess == NULL) || (sess->ticket == NULL) || (sess->ticketLen != ticLen))
|
|
return 0;
|
|
for (i = 0; i < ticLen; i++) {
|
|
if (sess->ticket[i] != fill)
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int test_wolfSSL_GetSessionAtIndex(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL_SESSION* copy = NULL;
|
|
WOLFSSL_SESSION* copy2 = NULL;
|
|
byte id[ID_LEN];
|
|
word16 ticLen = (word16)(SESSION_TICKET_LEN + 128);
|
|
int idx = -1;
|
|
|
|
XMEMSET(id, 0x5A, sizeof(id));
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectIntEQ(test_session_at_index_add(ctx, id, ticLen, 0xA1, &idx),
|
|
TEST_SUCCESS);
|
|
ExpectIntGE(idx, 0);
|
|
|
|
ExpectNotNull(copy = wolfSSL_SESSION_new());
|
|
#ifdef HAVE_EX_DATA
|
|
if (copy != NULL) {
|
|
copy->ex_data.ex_data[0] = (void*)copy;
|
|
}
|
|
#endif
|
|
ExpectIntEQ(wolfSSL_GetSessionAtIndex(idx, copy), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_session_at_index_ticket_is(copy, ticLen, 0xA1), 1);
|
|
#ifdef HAVE_EX_DATA
|
|
/* The copy keeps its own ex_data and the right to free it. */
|
|
if (copy != NULL) {
|
|
ExpectPtrEq(copy->ex_data.ex_data[0], (void*)copy);
|
|
ExpectIntEQ(copy->ownExData, 1);
|
|
}
|
|
#endif
|
|
|
|
/* Each copy owns its ticket rather than pointing into the cache. */
|
|
ExpectNotNull(copy2 = wolfSSL_SESSION_new());
|
|
ExpectIntEQ(wolfSSL_GetSessionAtIndex(idx, copy2), WOLFSSL_SUCCESS);
|
|
if ((copy != NULL) && (copy2 != NULL)) {
|
|
ExpectPtrNE(copy->ticket, copy2->ticket);
|
|
}
|
|
|
|
/* Same length overwrite reuses the cache buffer - copies keep their
|
|
* bytes. */
|
|
ExpectIntEQ(test_session_at_index_add(ctx, id, ticLen, 0xB2, NULL),
|
|
TEST_SUCCESS);
|
|
ExpectIntEQ(test_session_at_index_ticket_is(copy, ticLen, 0xA1), 1);
|
|
ExpectIntEQ(test_session_at_index_ticket_is(copy2, ticLen, 0xA1), 1);
|
|
|
|
/* Longer ticket makes the cache free its buffer, and releasing a copy must
|
|
* not free a buffer the cache still uses. */
|
|
wolfSSL_SESSION_free(copy2);
|
|
copy2 = NULL;
|
|
ExpectIntEQ(test_session_at_index_add(ctx, id, (word16)(ticLen + 512),
|
|
0xC3, NULL), TEST_SUCCESS);
|
|
ExpectIntEQ(test_session_at_index_ticket_is(copy, ticLen, 0xA1), 1);
|
|
|
|
wolfSSL_SESSION_free(copy);
|
|
wolfSSL_CTX_free(ctx);
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A short ticket cached over a slot that kept a long ticket's buffer must be
|
|
* read back as the short ticket, not the long ticket's leftover bytes. */
|
|
int test_wolfSSL_session_cache_short_ticket_reuse(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
WOLFSSL_CTX* ctx = NULL;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
WOLFSSL_SESSION* copy = NULL;
|
|
byte id[ID_LEN];
|
|
word16 shortLen = 32;
|
|
int idx = -1;
|
|
|
|
XMEMSET(id, 0x5A, sizeof(id));
|
|
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
|
ExpectIntEQ(test_session_at_index_add(ctx, id,
|
|
(word16)(SESSION_TICKET_LEN + 128), 0xA1, NULL), TEST_SUCCESS);
|
|
|
|
/* Same ID, short ticket in the session's static buffer. */
|
|
ExpectNotNull(sess = wolfSSL_SESSION_new());
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(sess->sessionID, id, ID_LEN);
|
|
sess->sessionIDSz = ID_LEN;
|
|
sess->side = WOLFSSL_CLIENT_END;
|
|
sess->isSetup = 1;
|
|
XMEMSET(sess->staticTicket, 0xB2, shortLen);
|
|
sess->ticketLen = shortLen;
|
|
}
|
|
ExpectIntEQ(AddSessionToCache(ctx, sess, id, ID_LEN, &idx,
|
|
WOLFSSL_CLIENT_END, 1, NULL), 0);
|
|
ExpectIntGE(idx, 0);
|
|
|
|
ExpectNotNull(copy = wolfSSL_SESSION_new());
|
|
ExpectIntEQ(wolfSSL_GetSessionAtIndex(idx, copy), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_session_at_index_ticket_is(copy, shortLen, 0xB2), 1);
|
|
|
|
wolfSSL_SESSION_free(copy);
|
|
wolfSSL_SESSION_free(sess);
|
|
wolfSSL_CTX_free(ctx);
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#else
|
|
|
|
int test_wolfSSL_GetSessionAtIndex(void)
|
|
{
|
|
return TEST_SKIPPED;
|
|
}
|
|
|
|
int test_wolfSSL_session_cache_short_ticket_reuse(void)
|
|
{
|
|
return TEST_SKIPPED;
|
|
}
|
|
|
|
#endif /* SESSION_INDEX && HAVE_SESSION_TICKET && !NO_SESSION_CACHE &&
|
|
* !NO_WOLFSSL_CLIENT && !NO_TLS */
|
|
|
|
/* RFC 9846 Appendix C.4: client applications should not offer tickets across
|
|
* connections meant to be uncorrelated. wolfSSL_SetServerID() is how an
|
|
* application keeps such connections apart, so a shorter ID must not match a
|
|
* cached entry that merely starts with the same bytes. */
|
|
int test_wolfSSL_client_cache_id_prefix(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_SESSION_CACHE) && !defined(NO_CLIENT_CACHE) && \
|
|
!defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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;
|
|
static const byte prefix[] = { 'w', 'o', 'l', 'f', 'S', 'S', 'L', ':' };
|
|
byte id[sizeof(prefix) + 4];
|
|
byte sessId[ID_LEN];
|
|
word32 i;
|
|
/* The cache row is picked from a hash of the ID, so the prefix and any
|
|
* one long ID rarely share a row. Every long ID here starts with the
|
|
* prefix, and there are enough of them to cover all rows, so the prefix
|
|
* lookup lands on a row holding one of them. */
|
|
const word32 fill = 4096;
|
|
|
|
/* TLS 1.2 so the client has a complete session to cache as soon as the
|
|
* handshake is done, with or without session tickets. */
|
|
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);
|
|
ExpectIntEQ(ssl_c->session->isSetup, 1);
|
|
|
|
XMEMCPY(id, prefix, sizeof(prefix));
|
|
XMEMSET(sessId, 0, sizeof(sessId));
|
|
for (i = 0; i < fill && EXPECT_SUCCESS(); i++) {
|
|
ClientSession* entry = NULL;
|
|
|
|
c32toa(i, id + sizeof(prefix));
|
|
c32toa(i, sessId);
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl_c, id, (int)sizeof(id), 1),
|
|
WOLFSSL_SUCCESS);
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(ssl_c->session->sessionID, sessId, ID_LEN);
|
|
XMEMCPY(ssl_c->session->altSessionID, sessId, ID_LEN);
|
|
ssl_c->session->sessionIDSz = ID_LEN;
|
|
}
|
|
ExpectIntEQ(AddSessionToCache(ctx_c, ssl_c->session, sessId, ID_LEN,
|
|
NULL, WOLFSSL_CLIENT_END, 0, &entry), 0);
|
|
ExpectNotNull(entry);
|
|
}
|
|
|
|
/* The prefix is a distinct partition key: no cached session for it. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(ssl->session->isSetup, 0);
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl, prefix, (int)sizeof(prefix), 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl->session->isSetup, 0);
|
|
ExpectIntEQ(ssl->session->idLen, (int)sizeof(prefix));
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
|
|
/* Control: the ID it was cached under still finds it. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl, id, (int)sizeof(id), 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl->session->isSetup, 1);
|
|
wolfSSL_free(ssl);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A persisted image whose ClientCache geometry differs from this build's must
|
|
* be rejected, not copied in: the rows are a different stride. */
|
|
int test_wolfSSL_session_cache_client_geometry(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(PERSIST_SESSION_CACHE) && \
|
|
!defined(SESSION_CACHE_DYNAMIC_MEM) && !defined(NO_SESSION_CACHE) && \
|
|
!defined(NO_CLIENT_CACHE)
|
|
byte* mem = NULL;
|
|
int sz = 0;
|
|
int saved;
|
|
|
|
ExpectIntGT(sz = wolfSSL_get_session_cache_memsize(), 0);
|
|
ExpectNotNull(mem = (byte*)XMALLOC((size_t)sz, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
ExpectIntEQ(wolfSSL_memsave_session_cache(mem, sz), WOLFSSL_SUCCESS);
|
|
|
|
/* Control: the image this build wrote restores. */
|
|
ExpectIntEQ(wolfSSL_memrestore_session_cache(mem, sz), WOLFSSL_SUCCESS);
|
|
|
|
/* cache_header_t is { version, rows, columns, sessionSz, clientRows,
|
|
* clientColumns }, so index 4 is the client row count. */
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(&saved, mem + 4 * sizeof(int), sizeof(saved));
|
|
saved *= 8;
|
|
XMEMCPY(mem + 4 * sizeof(int), &saved, sizeof(saved));
|
|
}
|
|
ExpectIntEQ(wolfSSL_memrestore_session_cache(mem, sz), CACHE_MATCH_ERROR);
|
|
|
|
XFREE(mem, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The default wolfSSL_get_session() return value is ssl->session itself, not a
|
|
* ClientCache handle, and it dies with the WOLFSSL that produced it. */
|
|
int test_wolfSSL_get_session_default_ref(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_SESSION_CACHE) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
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;
|
|
|
|
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(sess = wolfSSL_get_session(ssl_c));
|
|
#ifdef NO_SESSION_CACHE_REF
|
|
ExpectPtrEq(sess, ssl_c->session);
|
|
#else
|
|
ExpectPtrNE(sess, ssl_c->session);
|
|
#endif
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The documented non-OpenSSL client resumption flow: record a serverID before
|
|
* the handshake, then resume by that ID on the next connection. It has to
|
|
* work either way, since NO_SESSION_CACHE_REF governs only what
|
|
* wolfSSL_get_session() returns. */
|
|
int test_wolfSSL_SetServerID_resume(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_SESSION_CACHE) && !defined(NO_CLIENT_CACHE) && \
|
|
!defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
WOLFSSL* ssl_c2 = NULL;
|
|
WOLFSSL* ssl_s2 = NULL;
|
|
WOLFSSL* ssl_c3 = NULL;
|
|
WOLFSSL* ssl_s3 = NULL;
|
|
WOLFSSL* ssl_c4 = NULL;
|
|
WOLFSSL* ssl_s4 = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
struct test_memio_ctx test_ctx2;
|
|
struct test_memio_ctx test_ctx3;
|
|
struct test_memio_ctx test_ctx4;
|
|
static const byte id[] = { 'a', '.', 'e', 'x', 'a', 'm', 'p', 'l', 'e' };
|
|
static const byte other[] = { 'b', '.', 'e', 'x', 'a', 'm', 'p', 'l', 'e' };
|
|
/* Longer than SERVER_ID_LEN, so SetServerID hashes it instead. */
|
|
static const byte longId[SERVER_ID_LEN + 8] = { 'l', 'o', 'n', 'g' };
|
|
byte secret[SECRET_LEN];
|
|
byte longSecret[SECRET_LEN];
|
|
|
|
XMEMSET(secret, 0, sizeof(secret));
|
|
XMEMSET(longSecret, 0, sizeof(longSecret));
|
|
|
|
/* TLS 1.2 so the client holds a complete session as soon as the handshake
|
|
* is done, with or without session tickets. */
|
|
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(wolfSSL_SetServerID(ssl_c, id, (int)sizeof(id), 1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
|
|
ExpectIntEQ(ssl_c->session->isSetup, 1);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0);
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(secret, ssl_c->session->masterSecret, SECRET_LEN);
|
|
}
|
|
|
|
/* A second connection on the same contexts asks for that serverID. The
|
|
* lookup reads the ClientCache, so the handshake above must have written
|
|
* it. */
|
|
XMEMSET(&test_ctx2, 0, sizeof(test_ctx2));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx2, &ctx_c, &ctx_s, &ssl_c2, &ssl_s2,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl_c2, id, (int)sizeof(id), 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl_c2->session->isSetup, 1);
|
|
ExpectBufEQ(ssl_c2->session->masterSecret, secret, SECRET_LEN);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c2, ssl_s2, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c2), 1);
|
|
|
|
/* Negative control: an ID that was never cached finds nothing, so the
|
|
* assertions above cannot pass on an unconditional hit. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl, other, (int)sizeof(other), 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl->session->isSetup, 0);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
|
|
/* newSession 1 must not resume even though this ID is cached now. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl, id, (int)sizeof(id), 1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl->session->isSetup, 0);
|
|
ExpectIntEQ(ssl->session->idLen, (int)sizeof(id));
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
|
|
/* An ID over SERVER_ID_LEN is stored and looked up through its hash, so
|
|
* it is held at the hashed length rather than the one passed in. */
|
|
XMEMSET(&test_ctx3, 0, sizeof(test_ctx3));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx3, &ctx_c, &ctx_s, &ssl_c3, &ssl_s3,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl_c3, longId, (int)sizeof(longId), 1),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl_c3->session->idLen, SERVER_ID_LEN);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c3, ssl_s3, 10, NULL), 0);
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(longSecret, ssl_c3->session->masterSecret, SECRET_LEN);
|
|
}
|
|
ExpectIntNE(XMEMCMP(longSecret, secret, SECRET_LEN), 0);
|
|
|
|
XMEMSET(&test_ctx4, 0, sizeof(test_ctx4));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx4, &ctx_c, &ctx_s, &ssl_c4, &ssl_s4,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(wolfSSL_SetServerID(ssl_c4, longId, (int)sizeof(longId), 0),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl_c4->session->isSetup, 1);
|
|
ExpectBufEQ(ssl_c4->session->masterSecret, longSecret, SECRET_LEN);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c4, ssl_s4, 10, NULL), 0);
|
|
ExpectIntEQ(wolfSSL_session_reused(ssl_c4), 1);
|
|
|
|
wolfSSL_free(ssl_c4);
|
|
wolfSSL_free(ssl_s4);
|
|
wolfSSL_free(ssl_c3);
|
|
wolfSSL_free(ssl_s3);
|
|
wolfSSL_free(ssl_c2);
|
|
wolfSSL_free(ssl_s2);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
ForceZero(secret, sizeof(secret));
|
|
ForceZero(longSecret, sizeof(longSecret));
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_wolfSSL_client_cache_id_overwrite(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if !defined(NO_SESSION_CACHE) && !defined(NO_CLIENT_CACHE) && \
|
|
!defined(NO_SESSION_CACHE_REF) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
WOLFSSL_CTX* ctx_c2 = NULL;
|
|
WOLFSSL_CTX* ctx_s2 = NULL;
|
|
WOLFSSL* ssl_c2 = NULL;
|
|
WOLFSSL* ssl_s2 = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
WOLFSSL_SESSION* sessA = NULL;
|
|
struct test_memio_ctx test_ctx;
|
|
struct test_memio_ctx test_ctx2;
|
|
byte idA[ID_LEN];
|
|
byte secretA[SECRET_LEN];
|
|
byte secretB[SECRET_LEN];
|
|
|
|
XMEMSET(idA, 0, sizeof(idA));
|
|
XMEMSET(secretA, 0, sizeof(secretA));
|
|
XMEMSET(secretB, 0, sizeof(secretB));
|
|
|
|
/* A second server, on its own certificate and its own context. It runs
|
|
* first so no unrelated cache write falls between the handle below being
|
|
* issued and the overwrite it has to survive. */
|
|
XMEMSET(&test_ctx2, 0, sizeof(test_ctx2));
|
|
ExpectIntEQ(test_memio_setup(&test_ctx2, &ctx_c2, &ctx_s2, &ssl_c2, &ssl_s2,
|
|
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
|
|
ExpectIntEQ(test_memio_do_handshake(ssl_c2, ssl_s2, 10, NULL), 0);
|
|
ExpectIntEQ(ssl_c2->session->isSetup, 1);
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(secretB, ssl_c2->session->masterSecret, SECRET_LEN);
|
|
}
|
|
|
|
/* TLS 1.2 so the session ID names the cache entry and the client has a
|
|
* complete session as soon as the handshake is done. */
|
|
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);
|
|
ExpectIntEQ(ssl_c->session->isSetup, 1);
|
|
ExpectIntEQ(ssl_c->session->haveAltSessionID, 0);
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(idA, ssl_c->session->sessionID, ID_LEN);
|
|
XMEMCPY(secretA, ssl_c->session->masterSecret, SECRET_LEN);
|
|
}
|
|
ExpectIntNE(XMEMCMP(secretA, secretB, SECRET_LEN), 0);
|
|
|
|
/* The handle the legacy resumption flow hands out. */
|
|
ExpectNotNull(sessA = wolfSSL_get_session(ssl_c));
|
|
|
|
/* Control: it resolves to the session it was issued for. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(wolfSSL_set_session(ssl, sessA), WOLFSSL_SUCCESS);
|
|
ExpectBufEQ(ssl->session->masterSecret, secretA, SECRET_LEN);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
|
|
/* Control: re-adding the same session leaves the handle usable. */
|
|
ExpectIntEQ(AddSessionToCache(ctx_c, ssl_c->session, idA, ID_LEN, NULL,
|
|
WOLFSSL_CLIENT_END, 0, NULL), 0);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(wolfSSL_set_session(ssl, sessA), WOLFSSL_SUCCESS);
|
|
ExpectBufEQ(ssl->session->masterSecret, secretA, SECRET_LEN);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
|
|
/* The second server names the session ID it saw on the first connection.
|
|
* A server picks its own session ID and sends it in clear, so this needs
|
|
* no special capability. */
|
|
if (EXPECT_SUCCESS()) {
|
|
XMEMCPY(ssl_c2->session->sessionID, idA, ID_LEN);
|
|
ssl_c2->session->sessionIDSz = ID_LEN;
|
|
}
|
|
|
|
/* Positive control: the handle still resolves here, so the assertion
|
|
* below cannot pass on an already-recycled slot. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(wolfSSL_set_session(ssl, sessA), WOLFSSL_SUCCESS);
|
|
ExpectBufEQ(ssl->session->masterSecret, secretA, SECRET_LEN);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
|
|
ExpectIntEQ(AddSessionToCache(ctx_c2, ssl_c2->session, idA, ID_LEN, NULL,
|
|
WOLFSSL_CLIENT_END, 0, NULL), 0);
|
|
|
|
/* The handle must not resume the second server's session: an abbreviated
|
|
* handshake carries no certificate, so nothing else identifies the peer. */
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx_c));
|
|
if (wolfSSL_set_session(ssl, sessA) == WOLFSSL_SUCCESS) {
|
|
ExpectBufEQ(ssl->session->masterSecret, secretA, SECRET_LEN);
|
|
}
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
|
|
wolfSSL_CTX_flush_sessions(ctx_c, (long)-1);
|
|
|
|
wolfSSL_free(ssl_c2);
|
|
wolfSSL_free(ssl_s2);
|
|
wolfSSL_CTX_free(ctx_c2);
|
|
wolfSSL_CTX_free(ctx_s2);
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
int test_wolfSSL_client_cache_set1_id(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if (defined(OPENSSL_ALL) || defined(WOLFSSL_HAPROXY) || \
|
|
defined(WOLFSSL_NGINX)) && \
|
|
!defined(NO_SESSION_CACHE) && !defined(NO_CLIENT_CACHE) && \
|
|
!defined(NO_SESSION_CACHE_REF) && !defined(NO_TLS) && \
|
|
!defined(WOLFSSL_NO_TLS12) && \
|
|
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
|
|
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
|
|
WOLFSSL_CTX* ctx_c = NULL;
|
|
WOLFSSL_CTX* ctx_s = NULL;
|
|
WOLFSSL* ssl_c = NULL;
|
|
WOLFSSL* ssl_s = NULL;
|
|
WOLFSSL* ssl = NULL;
|
|
WOLFSSL_SESSION* sess = NULL;
|
|
WOLFSSL_SESSION* owned = NULL;
|
|
const byte* cachedId = NULL;
|
|
unsigned int idLen = 0;
|
|
struct test_memio_ctx test_ctx;
|
|
static const byte sidCtx[] = { 's', 'i', 'd', 'c', 't', 'x' };
|
|
byte newId[ID_LEN];
|
|
byte origId[ID_LEN];
|
|
|
|
XMEMSET(newId, 0xA5, sizeof(newId));
|
|
XMEMSET(origId, 0, sizeof(origId));
|
|
|
|
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);
|
|
ExpectIntEQ(ssl_c->session->haveAltSessionID, 0);
|
|
|
|
/* A client-side handle is a ClientCache reference, not a session. */
|
|
ExpectNotNull(sess = wolfSSL_get_session(ssl_c));
|
|
ExpectNotNull(cachedId = wolfSSL_SESSION_get_id(sess, &idLen));
|
|
ExpectIntEQ(idLen, ID_LEN);
|
|
ExpectIntNE(XMEMCMP(cachedId, newId, ID_LEN), 0);
|
|
if (cachedId != NULL) {
|
|
XMEMCPY(origId, cachedId, ID_LEN);
|
|
}
|
|
|
|
ExpectIntEQ(wolfSSL_SESSION_set1_id_context(sess, sidCtx, sizeof(sidCtx)),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(wolfSSL_set_session(ssl, sess), WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(ssl->session->sessionCtxSz, sizeof(sidCtx));
|
|
ExpectBufEQ(ssl->session->sessionCtx, sidCtx, sizeof(sidCtx));
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
|
|
/* Re-keying a cache entry would orphan the handle, so only a no-op is
|
|
* accepted. */
|
|
ExpectIntEQ(wolfSSL_SESSION_set1_id(sess, newId, ID_LEN),
|
|
WOLFSSL_FAILURE);
|
|
ExpectIntEQ(wolfSSL_SESSION_set1_id(sess, newId, ID_LEN - 1),
|
|
WOLFSSL_FAILURE);
|
|
ExpectBufEQ(cachedId, origId, ID_LEN);
|
|
ExpectIntEQ(wolfSSL_SESSION_set1_id(sess, origId, ID_LEN),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectIntEQ(wolfSSL_SESSION_set1_id(sess, cachedId, ID_LEN),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectNotNull(ssl = wolfSSL_new(ctx_c));
|
|
ExpectIntEQ(wolfSSL_set_session(ssl, sess), WOLFSSL_SUCCESS);
|
|
ExpectBufEQ(ssl->session->sessionID, origId, ID_LEN);
|
|
wolfSSL_free(ssl);
|
|
ssl = NULL;
|
|
|
|
/* An owned session is not a cache entry and takes a new ID. */
|
|
ExpectNotNull(owned = wolfSSL_get1_session(ssl_c));
|
|
ExpectIntEQ(wolfSSL_SESSION_set1_id(owned, newId, ID_LEN),
|
|
WOLFSSL_SUCCESS);
|
|
ExpectBufEQ(owned->sessionID, newId, ID_LEN);
|
|
ExpectBufEQ(cachedId, origId, ID_LEN);
|
|
wolfSSL_SESSION_free(owned);
|
|
|
|
wolfSSL_CTX_flush_sessions(ctx_c, (long)-1);
|
|
/* The handle no longer resolves once its entry is evicted. */
|
|
ExpectIntEQ(wolfSSL_SESSION_set1_id(sess, origId, ID_LEN),
|
|
WOLFSSL_FAILURE);
|
|
ExpectIntEQ(wolfSSL_SESSION_set1_id_context(sess, sidCtx, sizeof(sidCtx)),
|
|
WOLFSSL_FAILURE);
|
|
|
|
wolfSSL_free(ssl_c);
|
|
wolfSSL_free(ssl_s);
|
|
wolfSSL_CTX_free(ctx_c);
|
|
wolfSSL_CTX_free(ctx_s);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(PERSIST_SESSION_CACHE) && !defined(NO_SESSION_CACHE) && \
|
|
!defined(SESSION_CACHE_DYNAMIC_MEM) && !defined(TITAN_SESSION_CACHE) && \
|
|
!defined(HUGE_SESSION_CACHE)
|
|
/* Several tests to ensure persistent session cache is saved and restored
|
|
* with expected behavior. */
|
|
#include <wolfssl/ssl_sess.h>
|
|
|
|
#if (defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA))
|
|
static int test_rem_sess_cb_count = 0;
|
|
|
|
static void test_rem_sess_cb(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* sess)
|
|
{
|
|
(void)ctx; (void)sess;
|
|
WOLFSSL_MSG_EX("info: test test_rem_sess_cb called: %d",
|
|
test_rem_sess_cb_count);
|
|
test_rem_sess_cb_count++;
|
|
}
|
|
#endif /* HAVE_EXT_CACHE || HAVE_EX_DATA */
|
|
|
|
struct sess_cache_t {
|
|
cache_header_t hdr;
|
|
SessionRow s_rows[SESSION_ROWS];
|
|
#ifndef NO_CLIENT_CACHE
|
|
ClientRow c_rows[CLIENT_SESSION_ROWS];
|
|
#endif /* !NO_CLIENT_CACHE */
|
|
};
|
|
|
|
typedef struct sess_cache_t sess_cache_t;
|
|
|
|
/* Set the session cache to canary values for testing.
|
|
* Will be checked later in test_sanity_sessions(). */
|
|
static void test_set_sessions(sess_cache_t * cache_mem)
|
|
{
|
|
size_t i = 0;
|
|
size_t j = 0;
|
|
|
|
for (i = 0; i < SESSION_ROWS; ++i) {
|
|
SessionRow * row = &cache_mem->s_rows[i];
|
|
for (j = 0; j < SESSIONS_PER_ROW; ++j) {
|
|
WOLFSSL_SESSION * s = &row->Sessions[j];
|
|
s->timeout = 1;
|
|
s->bornOn = 0;
|
|
/* label sessions with something predictable */
|
|
s->sessionIDSz = 2;
|
|
s->sessionID[0] = (byte)i;
|
|
s->sessionID[1] = (byte)j;
|
|
/* set type, ticketLen, rem_sess_cb, heap, peer, etc to test
|
|
* canary values. These should be sanitized on session restore. */
|
|
s->type = WOLFSSL_SESSION_TYPE_UNKNOWN;
|
|
#ifdef HAVE_SESSION_TICKET
|
|
s->ticketLen = SESSION_TICKET_LEN + 1;
|
|
s->ticketLenAlloc = 1;
|
|
#endif /* HAVE_SESSION_TICKET */
|
|
#if defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA)
|
|
s->rem_sess_cb = test_rem_sess_cb;
|
|
#endif /* HAVE_EXT_CACHE || HAVE_EX_DATA */
|
|
s->heap = (void *)0x77;
|
|
#if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA)
|
|
s->peer = (WOLFSSL_X509 *)0x77;
|
|
#endif /* SESSION_CERTS && OPENSSL_EXTRA */
|
|
#ifdef HAVE_EX_DATA
|
|
XMEMSET(&s->ex_data, 0x77, sizeof(WOLFSSL_CRYPTO_EX_DATA));
|
|
s->ownExData = 1;
|
|
#endif /* HAVE_EX_DATA */
|
|
}
|
|
}
|
|
}
|
|
|
|
#define field_null_or_fail(what, s, name, fld) do { \
|
|
if ((s)->fld != NULL) { \
|
|
WOLFSSL_MSG_EX("error: %s: s = %p, id = 0x%02x%02x, %s = %p", \
|
|
(what), (s), (s)->sessionID[0], (s)->sessionID[1], \
|
|
(name), (s)->fld); \
|
|
ret = -1; \
|
|
goto sanity_fail; \
|
|
} \
|
|
} while (0)
|
|
|
|
/* sanity check the restored session cache.
|
|
* return 0 on success
|
|
* return -1 on error */
|
|
static int test_sanity_sessions(const sess_cache_t * cache_mem,
|
|
const char * what)
|
|
{
|
|
int ret = -1;
|
|
size_t i = 0;
|
|
size_t j = 0;
|
|
(void)what; /* only used in WOLFSSL_MSG_EX */
|
|
|
|
/* walk sessions, check for expected values */
|
|
for (i = 0; i < SESSION_ROWS; ++i) {
|
|
const SessionRow * row = &cache_mem->s_rows[i];
|
|
for (j = 0; j < SESSIONS_PER_ROW; ++j) {
|
|
const WOLFSSL_SESSION * s = &row->Sessions[j];
|
|
#ifdef HAVE_EX_DATA
|
|
const WOLFSSL_CRYPTO_EX_DATA * ex_data = &s->ex_data;
|
|
size_t k = 0;
|
|
#endif /* HAVE_EX_DATA */
|
|
|
|
#ifdef HAVE_SESSION_TICKET
|
|
if (s->ticketLenAlloc != 0 ||
|
|
s->ticketLen > SESSION_TICKET_LEN) {
|
|
WOLFSSL_MSG_EX("error: %s: s = %p, id = 0x%02x%02x: "
|
|
"ticketLenAlloc = %d, ticketLen = %d\n",
|
|
what, s,
|
|
s->sessionID[0], s->sessionID[1],
|
|
s->ticketLenAlloc, s->ticketLen);
|
|
ret = -1;
|
|
goto sanity_fail;
|
|
}
|
|
#endif /* HAVE_SESSION_TICKET */
|
|
|
|
if (s->type != WOLFSSL_SESSION_TYPE_CACHE) {
|
|
WOLFSSL_MSG_EX("error: %s: s = %p, id = 0x%02x%02x: "
|
|
"type not cache: 0x%02x", what, s,
|
|
s->sessionID[0], s->sessionID[1],
|
|
s->type);
|
|
ret = -1;
|
|
goto sanity_fail;
|
|
}
|
|
|
|
if (s->sessionIDSz != 2) {
|
|
WOLFSSL_MSG_EX("error: %s: s = %p: got id sz = %d"
|
|
", expected id sz = 2", what,
|
|
s, s->sessionIDSz);
|
|
ret = -1;
|
|
goto sanity_fail;
|
|
}
|
|
|
|
if (s->sessionID[0] != (byte)i ||
|
|
s->sessionID[1] != (byte)j) {
|
|
WOLFSSL_MSG_EX("error: %s: s = %p, got id = 0x%02x%02x:"
|
|
", expected id = 0x%02x%02x", what, s,
|
|
s->sessionID[0], s->sessionID[1],
|
|
(byte)i, (byte)j);
|
|
ret = -1;
|
|
goto sanity_fail;
|
|
}
|
|
|
|
/* all the remaining fields should have been sanitized as null */
|
|
field_null_or_fail(what, s, "heap", heap);
|
|
#if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA)
|
|
field_null_or_fail(what, s, "peer", peer);
|
|
#endif /* SESSION_CERTS && OPENSSL_EXTRA */
|
|
#if defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA)
|
|
field_null_or_fail(what, s, "rem_sess_cb", rem_sess_cb);
|
|
#endif /* HAVE_EXT_CACHE || HAVE_EX_DATA */
|
|
|
|
#ifdef HAVE_EX_DATA
|
|
/* The sane default ex_data state is:
|
|
* - s->ownExData == 1
|
|
* - s->ex_data* zeroed. */
|
|
if (s->ownExData != 1) {
|
|
WOLFSSL_MSG_EX("error: s = %p, id = 0x%02x%02x, ownExData = %d",
|
|
s, s->sessionID[0], s->sessionID[1],
|
|
s->ownExData);
|
|
ret = -1;
|
|
goto sanity_fail;
|
|
}
|
|
|
|
for (k = 0; k < MAX_EX_DATA; ++k) {
|
|
if (ex_data->ex_data[k]
|
|
#ifdef HAVE_EX_DATA_CLEANUP_HOOKS
|
|
|| ex_data->ex_data_cleanup_routines[k]
|
|
#endif /* HAVE_EX_DATA_CLEANUP_HOOKS */
|
|
) {
|
|
WOLFSSL_MSG_EX("error: s = %p, id = 0x%02x%02x:"
|
|
"ex_data[%zu] not null", s,
|
|
s->sessionID[0], s->sessionID[1], k);
|
|
ret = -1;
|
|
goto sanity_fail;
|
|
}
|
|
} /* for k */
|
|
#endif /* HAVE_EX_DATA */
|
|
} /* for j */
|
|
} /* for i */
|
|
|
|
ret = 0;
|
|
sanity_fail:
|
|
return ret;
|
|
}
|
|
|
|
/* Tests mem[save, restore]_session_cache.
|
|
*
|
|
* returns -1 on err
|
|
* returns 0 on success
|
|
* */
|
|
static int test_mem_session_cache(void)
|
|
{
|
|
int ret = -1;
|
|
int mem_sz = 0;
|
|
sess_cache_t * cache_mem = NULL;
|
|
int expected_mem_sz = 0;
|
|
#ifndef NO_CLIENT_CACHE
|
|
ClientRow * c_rows = NULL;
|
|
#endif /* !NO_CLIENT_CACHE */
|
|
int rst_err = 0;
|
|
#if !defined(BIG_SESSION_CACHE) && !defined(MEDIUM_SESSION_CACHE)
|
|
int i = 0;
|
|
#endif /* !BIG_SESSION_CACHE && !MEDIUM_SESSION_CACHE */
|
|
|
|
#if (defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA))
|
|
/* reset callback count */
|
|
test_rem_sess_cb_count = 0;
|
|
#endif /* HAVE_EXT_CACHE || HAVE_EX_DATA */
|
|
|
|
/* allocate scratch copy */
|
|
cache_mem = (sess_cache_t *)XMALLOC(sizeof(sess_cache_t), NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (cache_mem == NULL) {
|
|
WOLFSSL_MSG_EX("error: xmalloc(%zu) failed", (size_t) mem_sz);
|
|
return -1;
|
|
}
|
|
XMEMSET(cache_mem, 0, sizeof(sess_cache_t));
|
|
|
|
expected_mem_sz = (int)(sizeof(cache_mem->s_rows) + sizeof(cache_mem->hdr));
|
|
#ifndef NO_CLIENT_CACHE
|
|
expected_mem_sz += (int)sizeof(cache_mem->c_rows);
|
|
#endif /* NO_CLIENT_CACHE */
|
|
|
|
/* get session cache size, compare to expected result */
|
|
mem_sz = wolfSSL_get_session_cache_memsize();
|
|
if (mem_sz != expected_mem_sz) {
|
|
WOLFSSL_MSG_EX("error: got mem_sz %d, expected %d", mem_sz,
|
|
expected_mem_sz);
|
|
goto cleanup;
|
|
}
|
|
|
|
#ifndef NO_CLIENT_CACHE
|
|
/* allocate scratch ClientCache */
|
|
c_rows = (ClientRow *)XMALLOC(sizeof(ClientRow) * CLIENT_SESSION_ROWS,
|
|
NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (c_rows == NULL) {
|
|
WOLFSSL_MSG_EX("error: xmalloc(%zu) failed",
|
|
sizeof(ClientRow) * CLIENT_SESSION_ROWS);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
#endif /* !NO_CLIENT_CACHE */
|
|
|
|
/* save session cache to cache_mem struct */
|
|
if (wolfSSL_memsave_session_cache(cache_mem, mem_sz) != WOLFSSL_SUCCESS) {
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* setup test sessions */
|
|
test_set_sessions(cache_mem);
|
|
|
|
#ifndef NO_CLIENT_CACHE
|
|
/* copy current client cache */
|
|
XMEMCPY(c_rows, &cache_mem->c_rows, sizeof(cache_mem->c_rows));
|
|
#endif /* !NO_CLIENT_CACHE */
|
|
|
|
/* restore session cache from mem */
|
|
if (wolfSSL_memrestore_session_cache(cache_mem, mem_sz) != WOLFSSL_SUCCESS) {
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* wipe our session struct in memory */
|
|
XMEMSET(cache_mem, 0, sizeof(sess_cache_t));
|
|
|
|
/* save session cache back to cache_mem struct */
|
|
if (wolfSSL_memsave_session_cache(cache_mem, mem_sz) != WOLFSSL_SUCCESS) {
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* sanity check values */
|
|
ret = test_sanity_sessions(cache_mem, "mem");
|
|
if (ret) {
|
|
goto cleanup;
|
|
}
|
|
|
|
#ifndef NO_CLIENT_CACHE
|
|
/* verify we got back the exact client cache */
|
|
ret = XMEMCMP(c_rows, &cache_mem->c_rows, sizeof(cache_mem->c_rows));
|
|
if (ret) {
|
|
WOLFSSL_MSG_EX("error: mem restore c_rows diff: %d", ret);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
#endif /* !NO_CLIENT_CACHE */
|
|
|
|
/* eviction: flush sessions older than time 2 (0 + 1 < 2). */
|
|
wolfSSL_CTX_flush_sessions(NULL, 2);
|
|
|
|
#if (defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA))
|
|
/* if all else succeeded, finally check the callback count. it should not
|
|
* fire for sessions restored from persistent storage. */
|
|
if (test_rem_sess_cb_count != 0) {
|
|
ret = -1;
|
|
}
|
|
#endif /* HAVE_EXT_CACHE || HAVE_EX_DATA */
|
|
|
|
/* verify that invalid cache gives CACHE_MATCH_ERROR */
|
|
cache_mem->hdr.version = WOLFSSL_CACHE_VERSION - 1;
|
|
rst_err = wolfSSL_memrestore_session_cache(cache_mem, mem_sz);
|
|
if (rst_err != WC_NO_ERR_TRACE(CACHE_MATCH_ERROR)) {
|
|
WOLFSSL_MSG_EX("error: memrestore: hdr.version: got %d, expected %d",
|
|
rst_err, CACHE_MATCH_ERROR);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
cache_mem->hdr.version = WOLFSSL_CACHE_VERSION;
|
|
cache_mem->hdr.rows = SESSION_ROWS - 1;
|
|
rst_err = wolfSSL_memrestore_session_cache(cache_mem, mem_sz);
|
|
if (rst_err != WC_NO_ERR_TRACE(CACHE_MATCH_ERROR)) {
|
|
WOLFSSL_MSG_EX("error: memrestore: hdr.rows: got %d, expected %d",
|
|
rst_err, CACHE_MATCH_ERROR);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
cache_mem->hdr.rows = SESSION_ROWS;
|
|
cache_mem->hdr.columns = SESSIONS_PER_ROW - 1;
|
|
rst_err = wolfSSL_memrestore_session_cache(cache_mem, mem_sz);
|
|
if (rst_err != WC_NO_ERR_TRACE(CACHE_MATCH_ERROR)) {
|
|
WOLFSSL_MSG_EX("error: memrestore: hdr.columns: got %d, expected %d",
|
|
rst_err, CACHE_MATCH_ERROR);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
cache_mem->hdr.columns = SESSIONS_PER_ROW;
|
|
cache_mem->hdr.sessionSz = (int)(sizeof(WOLFSSL_SESSION) - 1);
|
|
rst_err = wolfSSL_memrestore_session_cache(cache_mem, mem_sz);
|
|
if (rst_err != WC_NO_ERR_TRACE(CACHE_MATCH_ERROR)) {
|
|
WOLFSSL_MSG_EX("error: memrestore: hdr.sessionSz: got %d, expected %d",
|
|
rst_err, CACHE_MATCH_ERROR);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
cache_mem->hdr.sessionSz = (int)(sizeof(WOLFSSL_SESSION));
|
|
|
|
#if !defined(BIG_SESSION_CACHE) && !defined(MEDIUM_SESSION_CACHE)
|
|
/* test rejection of invalid session row */
|
|
for (i = 0; i < SESSION_ROWS; ++i) {
|
|
/* nextIdx is [0, SESSIONS_PER_ROW - 1] */
|
|
int nextIdx = cache_mem->s_rows[i].nextIdx;
|
|
cache_mem->s_rows[i].nextIdx = -1;
|
|
|
|
rst_err = wolfSSL_memrestore_session_cache(cache_mem, mem_sz);
|
|
if (rst_err != WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) {
|
|
WOLFSSL_MSG_EX("error: restore: nextIdx: got %d, expected %d",
|
|
rst_err, WOLFSSL_FAILURE);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
cache_mem->s_rows[i].nextIdx = SESSIONS_PER_ROW;
|
|
|
|
rst_err = wolfSSL_memrestore_session_cache(cache_mem, mem_sz);
|
|
if (rst_err != WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) {
|
|
WOLFSSL_MSG_EX("error: restore: nextIdx: got %d, expected %d",
|
|
rst_err, WOLFSSL_FAILURE);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* restore original value */
|
|
cache_mem->s_rows[i].nextIdx = nextIdx;
|
|
|
|
rst_err = wolfSSL_memrestore_session_cache(cache_mem, mem_sz);
|
|
if (rst_err != WC_NO_ERR_TRACE(WOLFSSL_SUCCESS)) {
|
|
WOLFSSL_MSG_EX("error: restore: nextIdx: got %d, expected %d",
|
|
rst_err, WOLFSSL_SUCCESS);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < SESSION_ROWS; ++i) {
|
|
/* totalCount is [0, SESSIONS_PER_ROW] */
|
|
int totalCount = cache_mem->s_rows[i].totalCount;
|
|
cache_mem->s_rows[i].totalCount = -1;
|
|
|
|
rst_err = wolfSSL_memrestore_session_cache(cache_mem, mem_sz);
|
|
if (rst_err != WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) {
|
|
WOLFSSL_MSG_EX("error: restore: totalCount: got %d, expected %d",
|
|
rst_err, WOLFSSL_FAILURE);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
cache_mem->s_rows[i].totalCount = SESSIONS_PER_ROW + 1;
|
|
|
|
rst_err = wolfSSL_memrestore_session_cache(cache_mem, mem_sz);
|
|
if (rst_err != WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) {
|
|
WOLFSSL_MSG_EX("error: restore: totalCount: got %d, expected %d",
|
|
rst_err, WOLFSSL_FAILURE);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* restore original value */
|
|
cache_mem->s_rows[i].totalCount = totalCount;
|
|
|
|
rst_err = wolfSSL_memrestore_session_cache(cache_mem, mem_sz);
|
|
if (rst_err != WC_NO_ERR_TRACE(WOLFSSL_SUCCESS)) {
|
|
WOLFSSL_MSG_EX("error: restore: totalCount: got %d, expected %d",
|
|
rst_err, WOLFSSL_SUCCESS);
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
#endif /* !BIG_SESSION_CACHE && !MEDIUM_SESSION_CACHE */
|
|
|
|
cleanup:
|
|
if (cache_mem != NULL) {
|
|
XFREE(cache_mem, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
cache_mem = NULL;
|
|
}
|
|
|
|
#ifndef NO_CLIENT_CACHE
|
|
if (c_rows != NULL) {
|
|
XFREE(c_rows, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
c_rows = NULL;
|
|
}
|
|
#endif /* !NO_CLIENT_CACHE */
|
|
|
|
/* always clean up cache after test */
|
|
wolfSSL_CTX_flush_sessions(NULL, -1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if !defined(NO_FILESYSTEM)
|
|
/* write cache_mem to file.
|
|
*
|
|
* returns 0 on success
|
|
* returns < 0 on error */
|
|
static int test_write_file(const char * fname,
|
|
const sess_cache_t * cache_mem)
|
|
{
|
|
XFILE file = XBADFILE;
|
|
size_t n_write = 0;
|
|
int ret = -1;
|
|
size_t i = 0;
|
|
|
|
file = XFOPEN(fname, "w+b");
|
|
if (file == XBADFILE) {
|
|
WOLFSSL_MSG_EX("error: open(%s, w+b)", fname);
|
|
goto write_file_cleanup;
|
|
}
|
|
|
|
n_write = (int)XFWRITE(&cache_mem->hdr, sizeof(cache_header_t), 1, file);
|
|
if (n_write != 1) {
|
|
WOLFSSL_MSG_EX("error: write %s: %zu", fname, n_write);
|
|
goto write_file_cleanup;
|
|
}
|
|
|
|
/* we need to write SIZEOF_SESSION_ROW, one row at a time, so the row lock
|
|
* fields are skipped. */
|
|
for (i = 0; i < SESSION_ROWS; ++i) {
|
|
n_write = (int)XFWRITE(&cache_mem->s_rows[i], SIZEOF_SESSION_ROW, 1, file);
|
|
if (n_write != 1) {
|
|
WOLFSSL_MSG_EX("error: write %s: %zu", fname, n_write);
|
|
goto write_file_cleanup;
|
|
}
|
|
}
|
|
|
|
#ifndef NO_CLIENT_CACHE
|
|
n_write = (int)XFWRITE(&cache_mem->c_rows, sizeof(cache_mem->c_rows), 1, file);
|
|
if (n_write != 1) {
|
|
WOLFSSL_MSG_EX("error: write %s: %zu", fname, n_write);
|
|
goto write_file_cleanup;
|
|
}
|
|
#endif /* !NO_CLIENT_CACHE */
|
|
|
|
ret = 0;
|
|
write_file_cleanup:
|
|
if (file != XBADFILE) {
|
|
XFCLOSE(file);
|
|
file = XBADFILE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* read cache_mem from file.
|
|
*
|
|
* returns 0 on success
|
|
* returns < 0 on error */
|
|
static int test_read_file(const char * fname,
|
|
sess_cache_t * cache_mem)
|
|
{
|
|
XFILE file = XBADFILE;
|
|
size_t n_read = 0;
|
|
int ret = -1;
|
|
size_t i = 0;
|
|
|
|
file = XFOPEN(fname, "rb");
|
|
if (file == XBADFILE) {
|
|
WOLFSSL_MSG_EX("error: open(%s, rb)", fname);
|
|
goto read_file_cleanup;
|
|
}
|
|
|
|
n_read = (int)XFREAD(&cache_mem->hdr, sizeof(cache_header_t), 1, file);
|
|
if (n_read != 1) {
|
|
WOLFSSL_MSG_EX("error: read %s: %zu", fname, n_read);
|
|
goto read_file_cleanup;
|
|
}
|
|
|
|
/* we need to read SIZEOF_SESSION_ROW, one row at a time, so the row lock
|
|
* fields are skipped. */
|
|
for (i = 0; i < SESSION_ROWS; ++i) {
|
|
n_read = (int)XFREAD(&cache_mem->s_rows[i], SIZEOF_SESSION_ROW, 1, file);
|
|
if (n_read != 1) {
|
|
WOLFSSL_MSG_EX("error: read %s: %zu", fname, n_read);
|
|
goto read_file_cleanup;
|
|
}
|
|
}
|
|
|
|
#ifndef NO_CLIENT_CACHE
|
|
n_read = (int)XFREAD(&cache_mem->c_rows, sizeof(cache_mem->c_rows), 1, file);
|
|
if (n_read != 1) {
|
|
WOLFSSL_MSG_EX("error: read %s: %zu", fname, n_read);
|
|
goto read_file_cleanup;
|
|
}
|
|
#endif /* !NO_CLIENT_CACHE */
|
|
|
|
ret = 0;
|
|
|
|
read_file_cleanup:
|
|
if (file != XBADFILE) {
|
|
XFCLOSE(file);
|
|
file = XBADFILE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Tests file [save, restore]_session_cache.
|
|
*
|
|
* returns -1 on err
|
|
* returns 0 on success
|
|
* */
|
|
static int test_file_session_cache(void)
|
|
{
|
|
int ret = -1;
|
|
int mem_sz = 0;
|
|
int expected_mem_sz = 0;
|
|
sess_cache_t * cache_mem = NULL;
|
|
const char * fname = "tmp_test_session_cache.bin";
|
|
#ifndef NO_CLIENT_CACHE
|
|
ClientRow * c_rows = NULL;
|
|
#endif /* !NO_CLIENT_CACHE */
|
|
int rst_err = 0;
|
|
#if !defined(BIG_SESSION_CACHE) && !defined(MEDIUM_SESSION_CACHE)
|
|
int i = 0;
|
|
#endif /* !BIG_SESSION_CACHE && !MEDIUM_SESSION_CACHE */
|
|
|
|
#if (defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA))
|
|
/* reset callback count */
|
|
test_rem_sess_cb_count = 0;
|
|
#endif /* HAVE_EXT_CACHE || HAVE_EX_DATA */
|
|
|
|
/* allocate scratch copy */
|
|
cache_mem = (sess_cache_t *)XMALLOC(sizeof(sess_cache_t), NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (cache_mem == NULL) {
|
|
WOLFSSL_MSG_EX("error: xmalloc(%zu) failed", (size_t) mem_sz);
|
|
return -1;
|
|
}
|
|
XMEMSET(cache_mem, 0, sizeof(sess_cache_t));
|
|
|
|
expected_mem_sz = (int)(sizeof(cache_mem->s_rows) + sizeof(cache_mem->hdr));
|
|
#ifndef NO_CLIENT_CACHE
|
|
expected_mem_sz += (int)sizeof(cache_mem->c_rows);
|
|
#endif /* NO_CLIENT_CACHE */
|
|
|
|
/* get session cache size, compare to expected result */
|
|
mem_sz = wolfSSL_get_session_cache_memsize();
|
|
if (mem_sz != expected_mem_sz) {
|
|
WOLFSSL_MSG_EX("error: got mem_sz %d, expected %d", mem_sz,
|
|
expected_mem_sz);
|
|
goto file_cleanup;
|
|
}
|
|
|
|
#ifndef NO_CLIENT_CACHE
|
|
/* allocate scratch ClientCache */
|
|
c_rows = (ClientRow *)XMALLOC(sizeof(ClientRow) * CLIENT_SESSION_ROWS,
|
|
NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (c_rows == NULL) {
|
|
WOLFSSL_MSG_EX("error: xmalloc(%zu) failed",
|
|
sizeof(ClientRow) * CLIENT_SESSION_ROWS);
|
|
goto file_cleanup;
|
|
}
|
|
#endif /* !NO_CLIENT_CACHE */
|
|
|
|
/* save session cache to file fname */
|
|
if (wolfSSL_save_session_cache(fname) != WOLFSSL_SUCCESS) {
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
|
|
/* read it into cache_mem struct */
|
|
ret = test_read_file(fname, cache_mem);
|
|
if (ret) {
|
|
goto file_cleanup;
|
|
}
|
|
|
|
/* setup test sessions */
|
|
test_set_sessions(cache_mem);
|
|
|
|
#ifndef NO_CLIENT_CACHE
|
|
/* copy current client cache */
|
|
XMEMCPY(c_rows, &cache_mem->c_rows, sizeof(cache_mem->c_rows));
|
|
#endif /* !NO_CLIENT_CACHE */
|
|
|
|
/* write test sessions back to file */
|
|
ret = test_write_file(fname, cache_mem);
|
|
if (ret) {
|
|
goto file_cleanup;
|
|
}
|
|
|
|
/* restore session cache from file */
|
|
if (wolfSSL_restore_session_cache(fname) != WOLFSSL_SUCCESS) {
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
|
|
/* save session cache back to file fname */
|
|
if (wolfSSL_save_session_cache(fname) != WOLFSSL_SUCCESS) {
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
|
|
/* wipe our session struct in memory */
|
|
XMEMSET(cache_mem, 0, sizeof(sess_cache_t));
|
|
|
|
/* read it into cache_mem struct */
|
|
ret = test_read_file(fname, cache_mem);
|
|
if (ret) {
|
|
goto file_cleanup;
|
|
}
|
|
|
|
/* sanity check values */
|
|
ret = test_sanity_sessions(cache_mem, "file");
|
|
if (ret) {
|
|
goto file_cleanup;
|
|
}
|
|
|
|
#ifndef NO_CLIENT_CACHE
|
|
/* verify we got back the exact client cache */
|
|
ret = XMEMCMP(c_rows, &cache_mem->c_rows, sizeof(cache_mem->c_rows));
|
|
if (ret) {
|
|
WOLFSSL_MSG_EX("error: file restore c_rows diff: %d", ret);
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
#endif /* !NO_CLIENT_CACHE */
|
|
|
|
/* eviction: flush sessions older than time 2 (0 + 1 < 2). */
|
|
wolfSSL_CTX_flush_sessions(NULL, 2);
|
|
|
|
/* verify that invalid cache gives CACHE_MATCH_ERROR */
|
|
cache_mem->hdr.version = WOLFSSL_CACHE_VERSION - 1;
|
|
ret = test_write_file(fname, cache_mem);
|
|
if (ret) { goto file_cleanup; }
|
|
rst_err = wolfSSL_restore_session_cache(fname);
|
|
if (rst_err != WC_NO_ERR_TRACE(CACHE_MATCH_ERROR)) {
|
|
WOLFSSL_MSG_EX("error: restore: hdr.version: got %d, expected %d",
|
|
rst_err, CACHE_MATCH_ERROR);
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
|
|
cache_mem->hdr.version = WOLFSSL_CACHE_VERSION;
|
|
cache_mem->hdr.rows = SESSION_ROWS - 1;
|
|
ret = test_write_file(fname, cache_mem);
|
|
if (ret) { goto file_cleanup; }
|
|
rst_err = wolfSSL_restore_session_cache(fname);
|
|
if (rst_err != WC_NO_ERR_TRACE(CACHE_MATCH_ERROR)) {
|
|
WOLFSSL_MSG_EX("error: restore: hdr.rows: got %d, expected %d",
|
|
rst_err, CACHE_MATCH_ERROR);
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
|
|
cache_mem->hdr.rows = SESSION_ROWS;
|
|
cache_mem->hdr.columns = SESSIONS_PER_ROW - 1;
|
|
ret = test_write_file(fname, cache_mem);
|
|
if (ret) { goto file_cleanup; }
|
|
rst_err = wolfSSL_restore_session_cache(fname);
|
|
if (rst_err != WC_NO_ERR_TRACE(CACHE_MATCH_ERROR)) {
|
|
WOLFSSL_MSG_EX("error: restore: hdr.columns: got %d, expected %d",
|
|
rst_err, CACHE_MATCH_ERROR);
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
|
|
cache_mem->hdr.columns = SESSIONS_PER_ROW;
|
|
cache_mem->hdr.sessionSz = (int)(sizeof(WOLFSSL_SESSION) - 1);
|
|
ret = test_write_file(fname, cache_mem);
|
|
if (ret) { goto file_cleanup; }
|
|
rst_err = wolfSSL_restore_session_cache(fname);
|
|
if (rst_err != WC_NO_ERR_TRACE(CACHE_MATCH_ERROR)) {
|
|
WOLFSSL_MSG_EX("error: restore: hdr.sessionSz: got %d, expected %d",
|
|
rst_err, CACHE_MATCH_ERROR);
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
|
|
cache_mem->hdr.sessionSz = (int)(sizeof(WOLFSSL_SESSION));
|
|
|
|
#if !defined(BIG_SESSION_CACHE) && !defined(MEDIUM_SESSION_CACHE)
|
|
/* test rejection of invalid session row */
|
|
for (i = 0; i < SESSION_ROWS; ++i) {
|
|
/* nextIdx is [0, SESSIONS_PER_ROW - 1] */
|
|
int nextIdx = cache_mem->s_rows[i].nextIdx;
|
|
cache_mem->s_rows[i].nextIdx = -1;
|
|
|
|
ret = test_write_file(fname, cache_mem);
|
|
if (ret) { goto file_cleanup; }
|
|
rst_err = wolfSSL_restore_session_cache(fname);
|
|
if (rst_err != WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) {
|
|
WOLFSSL_MSG_EX("error: restore: nextIdx: got %d, expected %d",
|
|
rst_err, WOLFSSL_FAILURE);
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
|
|
cache_mem->s_rows[i].nextIdx = SESSIONS_PER_ROW;
|
|
|
|
ret = test_write_file(fname, cache_mem);
|
|
if (ret) { goto file_cleanup; }
|
|
rst_err = wolfSSL_restore_session_cache(fname);
|
|
if (rst_err != WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) {
|
|
WOLFSSL_MSG_EX("error: restore: nextIdx: got %d, expected %d",
|
|
rst_err, WOLFSSL_FAILURE);
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
|
|
/* restore original value */
|
|
cache_mem->s_rows[i].nextIdx = nextIdx;
|
|
ret = test_write_file(fname, cache_mem);
|
|
if (ret) { goto file_cleanup; }
|
|
|
|
rst_err = wolfSSL_restore_session_cache(fname);
|
|
if (rst_err != WC_NO_ERR_TRACE(WOLFSSL_SUCCESS)) {
|
|
WOLFSSL_MSG_EX("error: restore: nextIdx: got %d, expected %d",
|
|
rst_err, WOLFSSL_SUCCESS);
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < SESSION_ROWS; ++i) {
|
|
int totalCount = cache_mem->s_rows[i].totalCount;
|
|
/* totalCount is [0, SESSIONS_PER_ROW] */
|
|
cache_mem->s_rows[i].totalCount = -1;
|
|
|
|
ret = test_write_file(fname, cache_mem);
|
|
if (ret) { goto file_cleanup; }
|
|
rst_err = wolfSSL_restore_session_cache(fname);
|
|
if (rst_err != WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) {
|
|
WOLFSSL_MSG_EX("error: restore: totalCount: got %d, expected %d",
|
|
rst_err, WOLFSSL_FAILURE);
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
|
|
cache_mem->s_rows[i].totalCount = SESSIONS_PER_ROW + 1;
|
|
|
|
ret = test_write_file(fname, cache_mem);
|
|
if (ret) { goto file_cleanup; }
|
|
rst_err = wolfSSL_restore_session_cache(fname);
|
|
if (rst_err != WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) {
|
|
WOLFSSL_MSG_EX("error: restore: totalCount: got %d, expected %d",
|
|
rst_err, WOLFSSL_FAILURE);
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
|
|
/* restore original value */
|
|
cache_mem->s_rows[i].totalCount = totalCount;
|
|
ret = test_write_file(fname, cache_mem);
|
|
if (ret) { goto file_cleanup; }
|
|
|
|
rst_err = wolfSSL_restore_session_cache(fname);
|
|
if (rst_err != WC_NO_ERR_TRACE(WOLFSSL_SUCCESS)) {
|
|
WOLFSSL_MSG_EX("error: restore: totalCount: got %d, expected %d",
|
|
rst_err, WOLFSSL_SUCCESS);
|
|
ret = -1;
|
|
goto file_cleanup;
|
|
}
|
|
}
|
|
#endif /* !BIG_SESSION_CACHE && !MEDIUM_SESSION_CACHE */
|
|
|
|
file_cleanup:
|
|
/* remove session cache file. the file existing and being removed
|
|
* is part of the expected result. */
|
|
{
|
|
int rc = remove(fname);
|
|
if (rc) {
|
|
fprintf(stderr, "remove(%s) failed: %d\n", fname, rc);
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
#if (defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA))
|
|
/* if all else succeeded, finally check the callback count. it should not
|
|
* fire for sessions restored from persistent storage. */
|
|
if (test_rem_sess_cb_count != 0) {
|
|
ret = -1;
|
|
}
|
|
#endif /* HAVE_EXT_CACHE || HAVE_EX_DATA */
|
|
|
|
if (cache_mem != NULL) {
|
|
XFREE(cache_mem, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
cache_mem = NULL;
|
|
}
|
|
|
|
#ifndef NO_CLIENT_CACHE
|
|
if (c_rows != NULL) {
|
|
XFREE(c_rows, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
c_rows = NULL;
|
|
}
|
|
#endif /* !NO_CLIENT_CACHE */
|
|
|
|
/* always clean up cache after test */
|
|
wolfSSL_CTX_flush_sessions(NULL, -1);
|
|
|
|
return ret;
|
|
}
|
|
#endif /* !NO_FILESYSTEM */
|
|
|
|
int test_wolfSSL_session_cache_restore(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
ExpectIntEQ(test_mem_session_cache(), 0);
|
|
#if !defined(NO_FILESYSTEM)
|
|
ExpectIntEQ(test_file_session_cache(), 0);
|
|
#endif /* NO_FILESYSTEM */
|
|
return EXPECT_RESULT();
|
|
}
|
|
#else
|
|
int test_wolfSSL_session_cache_restore(void)
|
|
{
|
|
return TEST_SKIPPED;
|
|
}
|
|
#endif /* PERSIST_SESSION_CACHE && !NO_SESSION_CACHE &&
|
|
* !SESSION_CACHE_DYNAMIC_MEM && !TITAN_SESSION_CACHE &&
|
|
* !HUGE_SESSION_CACHE */
|