New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
wolfssl: Add SSLKEYLOGFILE support #5327
Conversation
42ca65a
to
9b3883a
Compare
Definitely. The SSLKEYLOGFILE code in ngtcp2 needs to be changed for thread safety, you can see an attempt in #4311 that I abandoned. I suggest a separate file in vtls sslkeylogfile.c and then put everything there. |
9b3883a
to
ed3e1aa
Compare
I've added a new keylog.c implementation and adapted OpenSSL and WolfSSL to use it. Test instructions can be found in the commit messages. None of the documentation seem to require updates, I could update libcurl-env.3 with version numbers and backends, but maybe that is too much detail? I've a patch to add support for ngtcp2 as well but still have to test it. It could be done as part of this PR or a new PR once the patch is ready. With the multiple TLS backends feature, only one of them can every be loaded it seems. So there is no concern about opening/closing the keylog file multiple times. However with addition of the feature to vquic, |
ed3e1aa
to
55d72f8
Compare
Rebased on master and tested against ngtcp2 + openssl (built using CMake - patches for this will follow in a separate PR). |
55d72f8
to
3c4ed37
Compare
b3a7991
to
98f0681
Compare
Rebased again on master (no changes). The previous tests failed due to ngtcp2 build errors, that should have been fixed in master now. Feature freeze is coming up :) |
Create a set of routines for TLS key log file handling to enable reuse with other TLS backends. Simplify the OpenSSL backend as follows: - Drop the ENABLE_SSLKEYLOGFILE macro as it is unconditionally enabled. - Do not perform dynamic memory allocation when preparing a log entry. Unless the TLS specifications change we can suffice with a reasonable fixed-size buffer. - Simplify state tracking when SSL_CTX_set_keylog_callback is unavailable. My original sslkeylog.c code included this tracking in order to handle multiple calls to SSL_connect and detect new keys after renegotiation (via SSL_read/SSL_write). For curl however we can be sure that a single master secret eventually becomes available after SSL_connect, so a simple flag is sufficient. An alternative to the flag is examining SSL_state(), but this seems more complex and is not pursued. Capturing keys after server renegotiation was already unsupported in curl and remains unsupported. Tested with curl built against OpenSSL 0.9.8zh, 1.0.2u, and 1.1.1f (`SSLKEYLOGFILE=keys.txt curl -vkso /dev/null https://localhost:4433`) against an OpenSSL 1.1.1f server configured with: # Force non-TLSv1.3, use TLSv1.0 since 0.9.8 fails with 1.1 or 1.2 openssl s_server -www -tls1 # Likewise, but fail the server handshake. openssl s_server -www -tls1 -Verify 2 # TLS 1.3 test. No need to test the failing server handshake. openssl s_server -www -tls1_3 Verify that all secrets (1 for TLS 1.0, 4 for TLS 1.3) are correctly written using Wireshark. For the first and third case, expect four matches per connection (decrypted Server Finished, Client Finished, HTTP Request, HTTP Response). For the second case where the handshake fails, expect a decrypted Server Finished only. tshark -i lo -pf tcp -otls.keylog_file:keys.txt -Tfields \ -eframe.number -eframe.time -etcp.stream -e_ws.col.Info \ -dtls.port==4433,http -ohttp.desegment_body:FALSE \ -Y 'tls.handshake.verify_data or http' A single connection can easily be identified via the `tcp.stream` field.
Hmm, a previous build with Warnings:
This is tricky. I see there is Based on the git logs for wolfssl src/tls13.c:
I'll assume it is acceptable not to support wolfSSL 3.12 where TLS 1.3 is enabled.
Fixed by using a different API with the same result. After fixing that, I ran into yet another issue. The public header make |
Tested following the same curl and tshark commands as in commit "vtls: Extract and simplify key log file handling from OpenSSL" using WolfSSL v4.4.0-stable-128-g5179503e8 from git master built with `./configure --enable-all --enable-debug CFLAGS=-DHAVE_SECRET_CALLBACK`. Full support for this feature requires certain wolfSSL build options, see "Availability note" in lib/vtls/wolfssl.c for details. Closes curl#5327
Tested with ngtcp2 built against the OpenSSL library. Additionally tested with MultiSSL (NSS for TLS and ngtcp2+OpenSSL for QUIC). The TLS backend (independent of QUIC) may or may not already have opened the keylog file before. Therefore Curl_tls_keylog_open is always called to ensure the file is open.
98f0681
to
19f001d
Compare
I amended the existing wolfssl patch with the following and referenced the "Availability note" in the commit message: diff--- a/lib/vtls/wolfssl.c
+++ b/lib/vtls/wolfssl.c
@@ -100,12 +100,15 @@ struct ssl_backend_data {
static Curl_recv wolfssl_recv;
static Curl_send wolfssl_send;
+#ifdef OPENSSL_EXTRA
/*
* Availability note:
* The TLS 1.3 secret callback (wolfSSL_set_tls13_secret_cb) was added in
* WolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that
* option is not set, then TLS 1.3 will not be logged.
* For TLS 1.2 and before, we use wolfSSL_get_keys().
+ * SSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA
+ * (--enable-opensslextra or --enable-all).
*/
#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13)
static int
@@ -162,17 +165,23 @@ wolfssl_log_tls12_secret(SSL *ssl)
unsigned char *ms, *sr, *cr;
unsigned int msLen, srLen, crLen, i, x = 0;
- switch(SSL_version(ssl)) {
- case SSL3_VERSION:
- case TLS1_VERSION:
- case TLS1_1_VERSION:
- case TLS1_2_VERSION:
+#if LIBWOLFSSL_VERSION_HEX >= 0x0300d000 /* >= 3.13.0 */
+ /* wolfSSL_GetVersion is available since 3.13, we use it instead of
+ * SSL_version since the latter relies on OPENSSL_ALL (--enable-opensslall or
+ * --enable-all). Failing to perform this check could result in an unusable
+ * key log line when TLS 1.3 is actually negotiated. */
+ switch(wolfSSL_GetVersion(ssl)) {
+ case WOLFSSL_SSLV3:
+ case WOLFSSL_TLSV1:
+ case WOLFSSL_TLSV1_1:
+ case WOLFSSL_TLSV1_2:
break;
default:
/* TLS 1.3 does not use this mechanism, the "master secret" returned below
* is not directly usable. */
return;
}
+#endif
if(SSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != SSL_SUCCESS) {
return;
@@ -190,6 +199,7 @@ wolfssl_log_tls12_secret(SSL *ssl)
Curl_tls_keylog_write("CLIENT_RANDOM", cr, ms, msLen);
}
+#endif /* OPENSSL_EXTRA */
static int do_file_type(const char *type)
{
@@ -477,6 +487,7 @@ wolfssl_connect_step1(struct connectdata *conn,
}
#endif /* HAVE_ALPN */
+#ifdef OPENSSL_EXTRA
if(Curl_tls_keylog_enabled()) {
/* Ensure the Client Random is preserved. */
wolfSSL_KeepArrays(backend->handle);
@@ -485,6 +496,7 @@ wolfssl_connect_step1(struct connectdata *conn,
wolfssl_tls13_secret_callback, NULL);
#endif
}
+#endif /* OPENSSL_EXTRA */
/* Check if there's a cached ID we can/should use here! */
if(SSL_SET_OPTION(primary.sessionid)) {
@@ -546,23 +558,29 @@ wolfssl_connect_step2(struct connectdata *conn,
ret = SSL_connect(backend->handle);
+#ifdef OPENSSL_EXTRA
if(Curl_tls_keylog_enabled()) {
/* If key logging is enabled, wait for the handshake to complete and then
* proceed with logging secrets (for TLS 1.2 or older).
*
- * During the handshake (ret==-1), WolfSSL_want returns SSL_READING when it
- * is waiting for the server response. When the handshake finishes (success
- * or failure), it returns SSL_NOTHING and we may read the master secret.
- * Note that OpenSSL SSL_want always returns SSL_READING at this point.
- * If this ever changes, the worst case is that no key is logged on error.
+ * During the handshake (ret==-1), wolfSSL_want_read() is true as it waits
+ * for the server response. At that point the master secret is not yet
+ * available, so we must not try to read it.
+ * To log the secret on completion with a handshake failure, detect
+ * completion via the observation that there is nothing to read or write.
+ * Note that OpenSSL SSL_want_read() is always true here. If wolfSSL ever
+ * changes, the worst case is that no key is logged on error.
*/
- if(ret == SSL_SUCCESS || SSL_want(backend->handle) == SSL_NOTHING) {
+ if(ret == SSL_SUCCESS ||
+ (!wolfSSL_want_read(backend->handle) &&
+ !wolfSSL_want_write(backend->handle))) {
wolfssl_log_tls12_secret(backend->handle);
/* Client Random and master secrets are no longer needed, erase these.
* Ignored while the handshake is still in progress. */
wolfSSL_FreeArrays(backend->handle);
}
}
+#endif /* OPENSSL_EXTRA */
if(ret != 1) {
char error_buffer[WOLFSSL_MAX_ERROR_SZ];
@@ -870,7 +888,9 @@ static size_t Curl_wolfssl_version(char *buffer, size_t size)
static int Curl_wolfssl_init(void)
{
+#ifdef OPENSSL_EXTRA
Curl_tls_keylog_open();
+#endif
return (wolfSSL_Init() == SSL_SUCCESS);
}
@@ -878,7 +898,9 @@ static int Curl_wolfssl_init(void)
static void Curl_wolfssl_cleanup(void)
{
wolfSSL_Cleanup();
+#ifdef OPENSSL_EXTRA
Curl_tls_keylog_close();
+#endif
} This was tested with a bunch of wolfSSL v4.4.0-stable-128-g5179503e8 configs:
Does this still look fine to you @bagder? |
|
Refactored SSLKEYLOGFILE support in OpenSSL and extended support to WolfSSL.
Tested with OpenSSL 0.9.8zh, 1.0.2u, and 1.1.1f and WolfSSL v4.4.0-stable-44-g3944c8eb7 and TLS 1.0/1.3, see individual commit messages for details.
DONE:
Update documentation(not needed)