Duplicate Received trusted client CA messages

Hi, While configuring stunnel to connect as a client to a TLS server that requires client certificate auth (known as mutual TLS auth), I noticed that each new connection generates lots (up to hundreds) of duplicate logs like: 2025.06.18 10:23:58 LOG6[0]: Received trusted client CA: CN=Example CA 2025.06.18 10:23:58 LOG6[0]: Received trusted client CA: CN=Example CA I expect one log per trusted client CA provided by the server. Openssl s_client -msg shows that the server only sends a small CertificateRequest. Stunnel logs show that only one connection is made. This only happens if the server offers exactly one trusted client CA. If multiple client CA options are offered by the server then a single log entry is recorded for each. What appears to be happening is that OpenSSL’s state machine generates lots of info callbacks (the number depends on latency). This appears to be harmless except for the excessive logging. I’m running stunnel 5.75 against OpenSSL 3.5.0 (compiled with enable-weak-ssl-ciphers, enable-ssl3 and enable-ssl3-method): $ stunnel [ ] Initializing inetd mode configuration [ ] Clients allowed=125 [.] stunnel 5.75 on sparc-sun-solaris2.10 platform [.] Compiled/running with OpenSSL 3.5.0 8 Apr 2025 [.] Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,PSK,SNI [ ] errno: (*(___errno())) … $ openssl version OpenSSL 3.5.0 8 Apr 2025 (Library: OpenSSL 3.5.0 8 Apr 2025) This is probably NOT caused by an OpenSSL 3.5.0 behaviour change, I also see duplicate messages from the public stunnel build running on Windows: [ ] Initializing inetd mode configuration [ ] Running on Windows 6.2 [ ] No limit detected for the number of clients [.] stunnel 5.74 on x64-pc-mingw32-gnu platform [.] Compiled/running with OpenSSL 3.3.2 3 Sep 2024 [.] Threading:WIN32 Sockets:SELECT,IPv6 TLS:ENGINE,FIPS,OCSP,PSK,SNI [ ] errno: (*_errno()) To diagnose this further, I modified stunnel src/ctx.c info_callback() to log the TLS state in callbacks: //#if 0 // s_log(LOG_DEBUG, "state = %x", state); state_string=SSL_state_string_long(ssl); s_log(LOG_DEBUG, "TLS state (where %08X state %08X): %s", where, state, state_string); //#endif This shows that there are many callbacks from where SSL_CB_ACCEPT_EXIT and one final callback from SSL_CB_ACCEPT_LOOP: 2025.06.18 10:23:58 LOG6[0]: Received trusted client CA: CN=Example CA 2025.06.18 10:23:58 LOG7[0]: TLS state (where 00001002 state 00000008): SSLv3/TLS read server certificate request 2025.06.18 10:23:58 LOG6[0]: Received trusted client CA: CN=Example CA 2025.06.18 10:23:58 LOG7[0]: TLS state (where 00001002 state 00000008): SSLv3/TLS read server certificate request 2025.06.18 10:23:58 LOG6[0]: Received trusted client CA: CN=Example CA 2025.06.18 10:23:58 LOG7[0]: TLS state (where 00001002 state 00000008): SSLv3/TLS read server certificate request 2025.06.18 10:23:58 LOG6[0]: Received trusted client CA: CN=Example CA 2025.06.18 10:23:58 LOG7[0]: TLS state (where 00001001 state 00000008): SSLv3/TLS read server certificate request 2025.06.18 10:23:58 LOG6[0]: Received trusted client CA: CN=Example CA 2025.06.18 10:23:58 LOG7[0]: TLS state (connect): SSLv3/TLS read server certificate request Modifying info_callback to look for SSL_CB_LOOP in where suppresses the excess messages: /* log the client certificate request (if received) */ #ifndef SSL3_ST_CR_CERT_REQ_A if(state==TLS_ST_CR_CERT_REQ && *where & SSL_CB_LOOP*) #else if(state==SSL3_ST_CR_CERT_REQ_A && *where & SSL_CB_LOOP*) #endif print_CA_list("Received trusted client CA", SSL_get_client_CA_list(ssl)); Here’s my config: debug=info [outbound-http-to-https-mtls] accept = 127.0.0.1:8447 connect = 127.0.0.1:8445 client = yes ; verify 2 - verify peer certificate chain verify = 2 sni = www.example.com checkHost = www.example.com verifyChain = yes CAfile = /export/home/user/stunnel/trusted-ca-certs.pem ; Authenticate ourselves to peer using a client certificate. cert = /export/home/user/stunnel/user.ecdsa-p256.pem key = /export/home/user/stunnel/user.ecdsa-p256.key -- Thanks, Sean Burford
participants (1)
-
Sean Burford