Bug 65848

Summary: 3a4c7bf2513a6f3e52d9608f3855d5f8148fef48 introduces regression with cert-based authentication
Product: Tomcat 8 Reporter: Michael Osipov <michaelo>
Component: UtilAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED FIXED    
Severity: regression    
Priority: P2    
Version: 8.5.75   
Target Milestone: ----   
Hardware: All   
OS: All   

Description Michael Osipov 2022-01-26 13:54:29 UTC
Pure Tomcat upgrade from 8.5.73 to .75 rejects all client certificates with the following configuration:
>          <Connector port="18444" connectionTimeout="20000" keepAliveTimeout="7200000"
>              maxHttpHeaderSize="24576" maxThreads="250"
>              SSLEnabled="true" scheme="https" secure="true"
>              defaultSSLHostConfigName="deblndw028v.ad001.siemens.net">
>              <SSLHostConfig hostName="deblndw028v.ad001.siemens.net" protocols="TLSv1.2+TLSv1.3"
>                  honorCipherOrder="true" disableSessionTickets="true"
>                  certificateVerification="optional" certificateVerificationDepth="5"
>                  ciphers="HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK:!DSS:!SHA1:!SHA256:!SHA384">
>                  <Certificate certificateFile="/opt/openssl/deblndw028v.ad001.siemens.net/cert.crt"
>                      certificateKeyFile="/opt/openssl/deblndw028v.ad001.siemens.net/key.crt"
>                      certificateKeyPassword="..." type="RSA" />
>                  <OpenSSLConf>
>                      <OpenSSLConfCmd name="VerifyCAPath" value="/opt/openssl/certs" />
>                      <OpenSSLConfCmd name="RequestCAFile" value="/opt/openssl/siemens-medium+strong-clientcert-cacerts.crt" />
>                      <OpenSSLConfCmd name="NO_OCSP_CHECK" value="true" />
>                  </OpenSSLConf>
>              </SSLHostConfig>
>          </Connector>

I am using the conf command for consistency reasons because our OpenSSL wrapper does not offer SSLCADNRequestFile/SSLCADNRequestPath and did not want to mix config styles.

Now 3a4c7bf2513a6f3e52d9608f3855d5f8148fef48 does:
> +                if (sslHostConfig.getCaCertificateFile() == null && sslHostConfig.getCaCertificatePath(
> ) == null) {
> +                    // No CA certificates configured. Reject all client certificates.
> +                    SSLContext.setCertVerifyCallback(ctx, new CertificateVerifier() {
> +                        @Override
> +                        public boolean verify(long ssl, byte[][] chain, String auth) {
> +                            return false;
> +                        }
> +                    });

This is too shortsighted since it does not take the conf command into account.

Another issue is that this is inconsistent. I have set up the same case with HTTPd and mod_ssl and both Schannel and NSS give me the follwing:
* Schannel: curl: (60) schannel: SEC_E_UNTRUSTED_ROOT (0x80090325) - Die Zertifikatkette wurde von einer nicht vertrauenswürdigen Zertifizierungsstelle ausgestellt.
* NSS: SSL_ERROR_UNKNOWN_CA_ALERT

I haven't yet checked mod_ssl code for this.
Error log:
> 66233 [Wed Jan 26 14:28:43.071310 2022] [ssl:error] [pid 61976] [client 139.21.146.172:33249] AH02039: Certificate Verification: Error (20): unable to get local issuer certificate
> 66234 [Wed Jan 26 14:28:43.071442 2022] [ssl:warn] [pid 61976] [client 139.21.146.172:33249] AH02227: Failed to set r->user to 'SSL_CLIENT_SAN_OTHER_msUPN_0'
> 66235 [Wed Jan 26 14:28:43.071487 2022] [core:error] [pid 61976] [client 139.21.146.172:33249] AH00027: No authentication done but request not allowed without authentication for /~osipovmi/tls-auth/index.php. Au
> 66240 [Wed Jan 26 14:30:45.391914 2022] [ssl:error] [pid 62030] [client 139.21.146.172:42111] AH02039: Certificate Verification: Error (20): unable to get local issuer certificate
> 66241 [Wed Jan 26 14:30:45.392025 2022] [ssl:error] [pid 62030] [client 139.21.146.172:42111] AH02261: Re-negotiation handshake failed

with new broken setup I get:
* schannel: next InitializeSecurityContext failed: SEC_E_ILLEGAL_MESSAGE (0x80090326) - This error usually occurs when a fatal SSL/TLS alert is received (e.g. handshake failed). More detail may be available in the Windows System event log.
* NSS: SSL_ERROR_UNKNOWN_CA_ALERT

I need to check Wireshark packets and compare.
Comment 1 Remy Maucherat 2022-01-26 16:19:43 UTC
When the possibility exists, the regular configuration should be used otherwise there will always be problems.
The doc there https://httpd.apache.org/docs/trunk/mod/mod_ssl.html#sslopensslconfcmd also says it is going to be a mess since there is config duplication.

Looking at https://www.openssl.org/docs/man1.1.1/man3/SSL_CONF_cmd.html, it is possible to identify some commands that would accurately indicate that CA is being configured, including ChainCAFile, ChainCAPath, VerifyCAFile, VerifyCAPath, RequestCAFile. In that case the reject callback would not be set. But having to do and maintain that special handling is annoying, and calls for more special cases.
Comment 2 Michael Osipov 2022-01-26 19:23:08 UTC
Yet another problem is that the changelog entry does not really represent the change in behavior.

I guess we need to reproduce the same "Compatibility and Stability warning" block as mod_ssl.
Comment 3 Mark Thomas 2022-02-02 08:59:23 UTC
We can certainly add a similar warning to the docs for OpenSSLConf.

I'm going to look at this again to see if I can get to the bottom of why I see different behaviour on MacOS and if that enables me to identify an alternative solution that avoids the complications with OpenSSLConf.
Comment 4 Michael Osipov 2022-02-02 10:48:18 UTC
(In reply to Mark Thomas from comment #3)
> We can certainly add a similar warning to the docs for OpenSSLConf.

OpenSSLConf is not documented at all, we need to document that and simply copy the banner from mod_ssl. That should be enough to warn users.

> I'm going to look at this again to see if I can get to the bottom of why I
> see different behaviour on MacOS and if that enables me to identify an
> alternative solution that avoids the complications with OpenSSLConf.

Not only that, I would expect that if not configured it would return the same TLS message as mod_ssl for consistency reasons. The description in changelog doesn't really help to identify the actual change.
Comment 5 Mark Thomas 2022-02-02 14:03:10 UTC
The root cause was the way homebrew configures OpenSSL. It imports the CAs trrusted by the system when you install OpenSSL and uses those as the defaults of you don't explicitly define a CA. The Tomcat Test CA was in that list due to some previous testing I had been doing and that was the cause of the difference in behaviour.

I have reverted the original patch as it is unnecessary.

Separately, I'll add some docs for OpenSSLConf.