On the client side wolfSSL_get_session() returns a ClientSession locator
into the static ClientCache rather than a full WOLFSSL_SESSION, unless
NO_SESSION_CACHE_REF is defined. Every session accessor maps that locator
to the real cache entry through ClientSessionToSession(), but
wolfSSL_SESSION_set1_id() and wolfSSL_SESSION_set1_id_context() wrote
the session ID and context straight through it. The writes landed at
WOLFSSL_SESSION offsets well past the locator, corrupting neighbouring
ClientCache entries and, for the last entries, running past the array.
Resolve the pointer first in both functions and fail when it no longer
maps to a cache entry. A cache entry is keyed by its session ID, so
rewriting it in place would stop the handle from resolving and strand
the entry in the row of its old ID. set1_id() on a client handle
therefore only accepts the ID the entry already holds, and then writes
nothing. Callers that want a new ID can use wolfSSL_get1_session(),
which returns the connection's own session.
Add a regression test that drives the documented get_session,
set1_id_context and set1_id flow on a client handle: the context reaches
the cached session, re-keying is refused and the handle keeps resolving,
an owned session still takes a new ID, and both setters reject a handle
whose entry was evicted.
Fixes F-9049.
- GetSessionFromCache(), GetSesionFromCacheOrExt() and
TlsSessionIdIsValid() initialize to 1 the copy flag passed to
ctx->get_sess_cb().
- GetSesionFromCacheOrExt() takes a reference with
wolfSSL_SESSION_up_ref() when the callback leaves copy set and
treats a session it cannot reference as a lookup miss. It sets
freeCtx->extCache and freeCtx->freeSess together, so
FreeSessionFromCacheOrExt() releases one reference.
- Comments restating the copy flag's contract are removed, or
replaced with one naming the reference released.
- ssl.h dox comments, English and Japanese, document
wolfSSL_CTX_sess_set_get_cb() and both values of its copy flag.
- tests/api/test_session.c and test_session.h add
test_wolfSSL_CTX_sess_get_cb_default_copy(): a TLS 1.2 resume
with copy left alone checks the reference count is unchanged, a
second with copy cleared checks wolfSSL releases exactly one.
Issue: F-14055
PR #11295 made early data acceptable only once per ticket and caches each
ticket that allows early data under a new session ID. Two PRs merged after
it had tests written against the old behaviour, and master now fails
scripts/unit.test with --enable-all:
- test_wolfSSL_CTX_add_session_ext_tls13/dtls13 (from #11498): case 6
enables early data, so the reissued ticket gets its own cache entry and
the test's flush calls the remove callback twice, not once.
- test_wolfSSL_accept_early_data_pending (from #11532): the second round
reused the first ticket for 0-RTT, which the server now rejects as a
replay. Take the reissued ticket from the first resumption instead
Without NO_SESSION_CACHE_REF, wolfSSL_get_session() returns a reference into
the process-global client cache rather than a session object. That reference
is valid only while the slot it names still holds the session it was issued
for, which the caller cannot observe, so the safe use of the API is the one
the macro selects. Only the integration recipes defined it, and none of them
for that reason: --enable-opensslextra and --enable-opensslall, which is what
an application written against SSL_get_session() reaches for, left it unset.
settings.h now defines NO_SESSION_CACHE_REF unless WOLFSSL_SESSION_CACHE_REF
is defined, so every build selects it, including the user_settings.h, IDE and
bare-metal builds that have no configure step. Putting the rule in the header
rather than only in configure and CMake means no project has to add a macro to
keep the safe behaviour, and the ones that want the deprecated reference ask
for it instead. --enable-session-cache-ref and -DWOLFSSL_SESSION_CACHE_REF=yes
define that opt-in macro, and options.h records it alongside the negative one
so an application cannot disagree with the library it links.
A build that opts in warns at compile time and the warning names
WOLFSSL_SESSION_CACHE_REF_WARNED, which silences it while keeping the path. It
sits inside the NO_SESSION_CACHE region, so a build without a session cache
and a wolfCrypt-only build stay quiet, and it takes the #warning / #pragma
message form already used for WOLFSSL_KEYLOG_EXPORT_WARNED.
The recipes that define NO_SESSION_CACHE_REF on their own keep doing so and
still win over the option. Both build systems say so rather than leaving a
reported option disagreeing with the build: configure warns and clears its
summary line, CMake warns and syncs the cached option. configure also treats
TITAN_SESSION_CACHE as a conflict, because settings.h derives the macro from
it and that never reaches the compile line for a flag scan to find.
The os-check session-cache-ref entry existed because --enable-opensslextra
left the macro unset. It now asks for the reference path explicitly, or
test_wolfSSL_client_cache_id_overwrite would skip and the generation check
added with it would be covered nowhere.
Anything migrating has one lifetime difference to mind: the deprecated
reference stayed valid after wolfSSL_free(), the session object does not, so
code holding the result of wolfSSL_get_session() past the WOLFSSL it came from
has to move to wolfSSL_get1_session() and wolfSSL_SESSION_free().
NO_SESSION_CACHE_REF is documented as choosing what wolfSSL_get_session()
returns, but it also stopped AddSession() from writing the ClientCache. One
field, ssl->clientSession, is both the handle handed back and the
out-parameter whose presence makes AddSessionToCache() call
AddSessionToClientCache(), so suppressing the handle suppressed the write.
Nothing then populated that cache while wolfSSL_GetSessionClient() went on
searching it, and it is the only consumer, reached from wolfSSL_SetServerID()
with newSession 0. Resumption by server ID therefore never resumed in a build
defining the macro: no error, no log, just a full handshake every time. That
covers --enable-curl, --enable-nginx, --enable-all and the other
OpenSSL-compatibility recipes, all of which have defined it for years.
Gate the write on NO_CLIENT_CACHE, which is what declares the field, and leave
only the wolfSSL_get_session() return value behind NO_SESSION_CACHE_REF. No
code reads ssl->clientSession when the macro is defined, so filling it in is
invisible to callers. CLIENT_SESSIONS_MULTIPLIER stays at 1 under the macro:
its value of 8 exists to make a stale handle less likely to resolve, which
cannot happen when no handle is issued, so the footprint does not change.
The test drives the documented flow rather than the cache API, since the
existing client-cache tests call AddSessionToCache() themselves with a
non-NULL entry and so pass either way. It records a server ID before the
handshake, then asks a second connection on the same contexts to resume by
that ID, and checks the master secret and wolfSSL_session_reused(). An
uncached ID is the negative control. It is not gated on NO_SESSION_CACHE_REF
and runs in both configurations.
Without NO_SESSION_CACHE_REF, wolfSSL_get_session() hands back a ClientSession
reference into the process-global cache rather than a session object, and
ClientSessionToSession() resolves it against nothing but a hash of the entry's
session ID. That ID does not change when the entry is overwritten, so a
reference kept across an overwrite still resolved, to contents it was never
issued for.
Give every cache write a generation number and record it in the ClientSession,
so a reference resolves only while the entry still holds the contents it was
issued for. The numbers come from a counter on the SessionRow rather than from
the entry, so that under SESSION_CACHE_DYNAMIC_MEM a freed and reallocated
entry cannot restart at a value an outstanding reference still holds.
The generation is not advanced when an overwrite carries the same master
secret, the benign re-add of an unchanged session; without that an older
reference would silently fall back to a full handshake. The comparison runs
through ConstantCompare so its timing says nothing about the cached secret.
WOLFSSL_CACHE_VERSION goes to 3: the ClientCache, the SessionRow prefix and
the WOLFSSL_SESSION layouts written by the session-cache save functions all
change.
The test builds the sequence out of two real handshakes on separate contexts,
behind two controls so it cannot pass by refusing everything. Every profile in
the os-check matrix that builds the unit tests defines NO_SESSION_CACHE_REF,
which compiles the reference path out, so a session-cache-ref entry configures
one that does not.
Fixes needed by the OSP OpenResty 1.31.1.1 port, plus a workflow that
runs the bundled SSL tests against wolfSSL.
d2i_X509_bio(): read one DER object per call, as OpenSSL does. ngx_lua's
set_der_cert() decodes a chain from one BIO one certificate at a time.
MAX_EX_DATA: default to 10 slots for --enable-nginx unless
--enable-context-extra-user-data gives a number. nginx 1.31 allocates six
SSL_CTX ex_data indexes and the OpenResty Lua modules use five SSL ones.
wolfSSL_OCSP_resp_find_status() and wolfSSL_OCSP_single_get0_status():
return NULL for an absent thisUpdate or nextUpdate instead of a pointer
to a zeroed WOLFSSL_ASN1_TIME. ngx.ocsp.validate_ocsp_response() failed
with "invalid nextUpdate time" for responses without nextUpdate.
Session cache: with WOLFSSL_TICKET_HAVE_ID, DoClientTicketFinalize()
called the external cache callback on every ticket resumption because
useTicket is only set after DoClientTicket() returns. Add
wolfSSL_GetSessionFromInternalCache() and use it unless the server
accepts early data (maxEarlyDataSz > 0), where the external cache takes
part in the 0-RTT anti-replay eviction. OpenSSL does the same: stateful
tickets and a cache lookup only with max_early_data set. ngx_lua expects
ssl_session_fetch_by_lua* to be skipped for ticket resumptions.
.github/workflows/openresty.yml: build OpenResty 1.31.1.1 with the OSP
port and tests patches against --enable-openresty and run the SSL test
files of ngx_lua, ngx_stream_lua and lua-resty-core, plus the
encrypted-session and set-misc suites, with Test::Nginx. Files that need
APIs wolfSSL does not provide or the network are not run. The OSP
checkout points at julek-dev/osp openresty-1.31.1.1 until the patches are
merged into wolfSSL/osp. memcached is a new test dependency.
Tests: d2i_X509_bio chain decoding, absent OCSP times, session cache
callback counts with tickets and early data.
wolfSSL_GetSessionClient() compared only the requested number of bytes, so a
short server ID could match a cached entry that merely started with the same
bytes and landed in the same cache row. Applications partitioning the cache
with wolfSSL_SetServerID() could then offer a ticket across partitions,
against RFC 9846 Appendix C.4.
Fixes https://github.com/wolfSSL/wolfssl/issues/11132
- wolfSSL_CTX_get_tlsext_ticket_keys() and
wolfSSL_CTX_set_tlsext_ticket_keys() hold ticketKeyCtx.mutex across
the name, key and expirary copy and return WOLFSSL_FAILURE when the
lock fails, under !SINGLE_THREADED. The @return line of each names
that failure.
- The #if around both functions in src/ssl_api_ext.c and around their
prototypes in wolfssl/ssl.h adds !defined(NO_TLS).
- tests/api/test_session.c gains test_wolfSSL_ticket_keys_threaded(),
a writer thread rotating two key blobs against a reader asserting
each get returns one blob whole and that both blobs were seen.
Declared and registered in tests/api/test_session.h.
Issue: F-13440
Move out DTLS 1.3 specific tests into test_dtls13.c. (Also move out from
test_dtls.c)
Move out DTLS tests into test_dtls.c.
Move out LMS and XMSS tests into test_lms_xmss.c.
Move out SSL session tests into test_session.c.
Move out remaining ML-DSA/Dilithium tests in api.c into test_mldsa.c.