Commit Graph
18 Commits
Author SHA1 Message Date
Claude 87940631e2 Check the client certificate in the PEM memory test and skip the CA dir
PemMemoryClientCertPresent now checks that the server saw the client's
certificate, as ClientCertPresent does. Before, it also passed when the
server built from PemMemory did not ask for a certificate at all.

The ClientCertPresent and ClientCertMissing helpers no longer pass
CLIENT_CA_CERT_DIR, which is "." and makes Mbed TLS parse every file in
the test directory, test binaries included, on each server they build.
The CA file is all they need. That makes TrustDirOptional, which built
the same server and client, a duplicate, so it goes. The tests that
still pass the directory keep the constructor taking one covered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-17 05:00:23 +00:00
Claude 4836eb0fa5 Check both outcomes in the update_certs_pem client cert test
Make ClientCertStillRequired take the setup to apply, so the
update_certs_pem test runs the same pair as the verify callback tests:
a client without a certificate is rejected and one with a certificate
is still accepted. Rejection alone would also pass if the update broke
the server for every client.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-17 04:41:00 +00:00
Claude d00af3f4e6 Test client cert rejection for PEM memory servers and update_certs_pem
Split the rejection scenario out of ClientCertMissing so it can run
against any server, then use it to check that a server built from
PemMemory with a client CA, and a server whose certificates were
replaced with update_certs_pem, both reject a client without a
certificate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-17 04:41:00 +00:00
Claude a1d4ccedea Reset the client's TLS error per attempt and read it under the request lock
Reset last_ssl_error_ and last_backend_error_ at the start of each
attempt in send_() rather than once in send(), so a request that
succeeds on the SSLPeerCouldBeClosed_ retry does not report the TLS
error of the attempt before it.

Now that every request writes those members, hold request_mutex_ until
the Result has read them, so another thread's request on the same
client cannot reset or replace them in between.

Also pick the TLS error up in a lambda in SSLClient::process_socket()
instead of through an out-parameter on process_client_socket_ssl(),
which has no other caller.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-17 02:40:23 +00:00
Claude 28741cb9d1 Keep the TLS error behind a failed read or write in the client's Result
A request that fails after the TLS handshake reports Error::Read or
Error::Write, and SSLSocketStream dropped the TLS error behind it. On
TLS 1.3, where a server rejects a client without a certificate only
after the handshake, ssl_error() and ssl_backend_error() were 0 instead
of showing, for example, OpenSSL's "tlsv13 alert certificate required".

SSLSocketStream now keeps the error from its last failed TLS read or
write, and SSLClient copies it into the values Result reports. Error
itself stays Read or Write.

Those values are also reset at the start of each request, so a later
successful request no longer reports the TLS error of an earlier one. A
client whose TLS context failed to set up keeps reporting that error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-17 01:54:23 +00:00
Claude b4ef313cc7 Accept a write error for a client rejected after the TLS 1.3 handshake
On TLS 1.3 the server rejects a client without a certificate only after
the client has finished its side of the handshake, so the client notices
either when it writes the request or when it reads the response. CI saw
the write fail on Windows (OpenSSL) and once on macOS (Mbed TLS), which
ClientCertMissing did not accept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-17 01:04:13 +00:00
Claude 6529de1a91 Declare tls::set_verify_callback() for the split test build
The ClientCertStillRequired tests call tls::set_verify_callback(), whose
declaration lives below the split border, so test_split failed to compile.
Declare it next to create_ca_store(), which the split build needs for the
same reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-16 23:29:35 +00:00
Claude 8f21292d16 Tidy the client certificate test helpers
Use ASSERT_FALSE and FAIL() instead of their hand-written forms, drop an
unused using-directive and a comment that restated the code, and fix the
section comment that still referred to get_peer_cert().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-16 23:02:50 +00:00
Claude 589b028022 Build the client-cert-still-required tests on the existing helpers
ClientCertPresent() and ClientCertMissing() now take a setup step for the
server, bind to any port instead of the legacy PORT, and the two new tests
are just calls to them.

ClientCertMissing also no longer checks the server certificate: with it
checked, the request failed because cert.pem is not trusted, whether or not
the server asked for a client certificate, so the test never exercised the
client certificate requirement. A rejected client is reported as
SSLConnection, or as Read on TLS 1.3, where the client only notices on its
first read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-16 22:48:39 +00:00
Claude aa3aeb8ec3 Name the Mbed TLS verify-mode helper like the other backends
Rename apply_authmode() to apply_verify_mode() to match the OpenSSL and
wolfSSL helpers, and shorten the comments on the helpers and on the
verify_client fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-16 22:25:51 +00:00
Claude 300c084d6f Derive the TLS verify mode from one place per backend
set_verify_client() and set_verify_callback() each set the verify mode on
their own, and set_verify_callback() ignored whether the peer's certificate
was required. A server that required client certificates therefore stopped
requiring them as soon as a verify callback was set on its context (OpenSSL,
Mbed TLS, wolfSSL) or set and then cleared again (OpenSSL, Mbed TLS).

Compute the mode from "peer certificate required" and "callback set" in one
helper per backend and call it from set_verify_client(),
set_verify_callback() and the context constructors. The context now records
that a client verifies the server by default, so clearing a callback on a
client context restores that default.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-16 21:12:47 +00:00
Claude a445ed23bf Tidy the Mbed TLS verify callback after the per-session change
Remove the now-unused forward declaration and the null checks that can no
longer fire, since create_session() always sets the session and its
context before installing the callback, and shorten the comments.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-16 19:57:26 +00:00
Claude f6420979b2 Drop the redundant has_verify_callback flag from the wolfSSL context
It only mirrored whether verify_callback was set, so test verify_callback
directly, as the Mbed TLS context now does.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-16 19:39:00 +00:00
Claude 5d10095944 Install one verify callback on every Mbed TLS session
Every session now gets mbedtls_verify_callback(), which reads the user's
callback from the owning context when the handshake runs. The
has_verify_callback flag and the separate hostname-masking callback are no
longer needed, and a callback set after a session was created is still
honoured.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGAYpQgXt95LdDQcJYgjB8
2026-10-16 18:57:00 +00:00
Claude 65dbca0afe Merge origin/master into claude/sweet-keller-2fba4t
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wxevjmRqzpy8qDAD3vdwr
2026-10-16 18:23:37 +00:00
Claude 5056242a4b Simplify per-context verify callback bookkeeping
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wxevjmRqzpy8qDAD3vdwr
2026-10-16 18:21:18 +00:00
Claude 0c583ac9b5 Drop no-op mbedtls_ssl_conf_verify reset in set_verify_callback
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wxevjmRqzpy8qDAD3vdwr
2026-10-16 18:15:10 +00:00
Claude e225d1876b Store the TLS verify callback per context instead of thread_local
tls::set_verify_callback() kept the callback in a thread_local slot, so two
SSLClients configured on one thread overwrote each other's verifier, and a
client used from another thread ran none. Keep it with the context instead
(OpenSSL SSL_CTX ex_data, MbedTlsContext/WolfSSLContext member) and have each
backend trampoline look it up from the session's context.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013wxevjmRqzpy8qDAD3vdwr
2026-10-16 17:25:43 +00:00