Bug 61394 - NIO/NIO2 + OpenSSL renegotiation doesn't send list of CAs to user agent
Summary: NIO/NIO2 + OpenSSL renegotiation doesn't send list of CAs to user agent
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 9
Classification: Unclassified
Component: Connectors (show other bugs)
Version: unspecified
Hardware: PC Linux
: P2 minor (vote)
Target Milestone: -----
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-08-08 20:16 UTC by Mark Thomas
Modified: 2017-09-08 09:07 UTC (History)
0 users



Attachments
TC trunk support adding client CA list from trust managers (2.51 KB, patch)
2017-08-10 03:42 UTC, Rainer Jung
Details | Diff
screenshot of a chrome ssl protocl error (161.48 KB, image/png)
2017-08-14 19:21 UTC, matej.spiller
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Mark Thomas 2017-08-08 20:16:01 UTC
There is a small glitch with NIO and NIO2 with OpenSSL. Because the code doesn't specify a set of trusted certs, OpenSSL doesn't send any CA names to the user agent which in turn (typically) opts to display all of the available client certs. If the user has a lot of certs it can take time to find the right one.
Comment 1 Rainer Jung 2017-08-08 20:46:03 UTC
The OpenSSL call for this should be SSL_CTX_set_client_CA_list() (at least mod_ssl in Apache httpd uses it).

We already wired that functionality in tcnative, file native/src/sslcontext.c, function setCACertificate. It gets alrady called in org.apache.tomcat.jni.SSLContext.setCACertificate(long ctx, String file, String path) which in trun is (conditionally) called from org.apache.tomcat.util.net.openssl.OpenSSLContext.init(...) and org.apache.tomcat.util.net.AprEndpoint.createSSLContext(...). Maybe something with the setup is wrong?

Regards,

Rainer
Comment 2 Mark Thomas 2017-08-08 21:08:23 UTC
I don't think we are calling that method when we are using JSSE config with the OpenSSL engine. I think we need the equivalent of the call to setCertificateRaw for the trusted certs.
Comment 3 Rainer Jung 2017-08-09 10:07:53 UTC
OK, so the problem is only occuring if JSSE style config is used?

And the attempt would be to read CA certs from the configured truststore, pass them as raw data to a new method setCACertificateRaw(), whose native impl converts them to OpenSSL X509 analogous to setCertificateRaw() and passes the result directly to OpenSSL via SSL_CTX_set_client_CA_list().

Is that what you expect?

I might give it an attempt this evening.

Note that our docs say:

###################
trustManagerClassName	

JSSE only.

The name of a custom trust manager class to use to validate client certificates. The class must have a zero argument constructor and must also implement javax.net.ssl.X509TrustManager. If this attribute is set, the trust store attributes may be ignored.
###################

So retrieving CA certs from a configured trust store might give wrong results, if e.g. a custom trust manager gets used and a trust store is configured, that the trust manager would not use but we would still use it to feed OpenSSL. One could argue that would be a configuration issue, but at least the docs ("may be ignored") would open to interpretation then.

Regards,

Rainer
Comment 4 Mark Thomas 2017-08-09 15:18:39 UTC
Yes, this is JSSE style config only.

Yes, I was thinking along the lines you describe.

Regarding the custom trust manager, what I think Tomcat needs to do is replicate what JSSE does which is:
- iterate through the provided TrustManager instances array and select the first instance of X509TrustManager
- call getAcceptedIssuers() on that instance to get the list of acceptable CAs

Then Tomcat can pass that array of X509Certificates to OpenSSL.

That should then give us equivalent behaviour for the same configuration with either implementation.
Comment 5 George Stanchev 2017-08-09 15:27:09 UTC
I don't mean to derail the discussion but we implement a trust-all manager thats gets injected from the connector attrobutes:

public class AnyCertX509TrustManager implements X509TrustManager {
    public void checkClientTrusted(X509Certificate[] certs, String auth) {}
    public void checkServerTrusted(X509Certificate[] certs, String auth) {}
    public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}

In case of OpenSSL setup, would the proposed approach to send the accepted issuers as [0] sized array of certs mean "trust-all" to OpenSSL or "trust-none"
Comment 6 Mark Thomas 2017-08-09 15:36:09 UTC
Currently no trusted certs means trust all. I don't see a reason to change that.
Comment 7 Rainer Jung 2017-08-10 03:42:32 UTC
Created attachment 35210 [details]
TC trunk support adding client CA list from trust managers
Comment 8 Rainer Jung 2017-08-10 03:44:31 UTC
The patch is based on the native extension committed in r1804622.

Please also note a related fix I noticed when looking at that part of the code (r1804595).

I started some testing for the attached patch and will add some new test cases. Simple preliminary tests look OK.

Note that the patch also moved the code for the "use files instead of trust managers" case.
Comment 9 Rainer Jung 2017-08-13 14:41:24 UTC
I applied the patch, wrapping the new native call in a check for a UnsatisfiedLinkError to not break compatibility with older tcnative.

I also added one new test and checks to existing tests to detcet, whether a CA for client cert is contained in the handshake and whether it is the right one.

We need to decide, whether and how to backport, since the fix relies on the not yet released tcnative 1.2.13. Eve once it is released it might not be widely available.
Comment 10 matej.spiller 2017-08-14 19:20:44 UTC
I am unable to debug the exact SSL error but I too have a problem with OpenSSL & NIO (or NIO2) when I have truststoreFile setup.

As soon as I enable certificateVerification I start getting SSL errors in chrome. If I switch to JSSE it works as advertised. I can set the verification to optional and not select a certificate and still get the same error.

    <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true">
		<SSLHostConfig truststoreFile="conf/truststore.jks" truststorePassword="test" certificateVerification="optional">
            <Certificate certificateKeystoreFile="conf/keystore.jks"
                         certificateKeystorePassword="test"
                         type="RSA" />
        </SSLHostConfig>
    </Connector>

If I open the page in IE it lists all the certificates.
Is there any chance to get a 1.2.13 snapshot build to test it on windows x64?
Comment 11 matej.spiller 2017-08-14 19:21:36 UTC
Created attachment 35228 [details]
screenshot of a chrome ssl protocl error
Comment 12 Mark Thomas 2017-09-01 16:21:25 UTC
comment #10 looks like the renegotiation issues that were fixed in 1.2.13. I can't reproduce the issue.

I've tested this with 1.2.14 and various combinations and all now behave as expected.

Once 1.2.14 is available and Tomcat has been updated to require it, this can be closed.
Comment 13 Mark Thomas 2017-09-08 09:07:56 UTC
9.0.x and 8.5.x have been updated to require 1.2.14