Hi guys,
yesterday, I faced a very dire issue with the way we handle SSL in MINA 2.
Basically, SSL is managed through the SslFilter, which is added in the
chain. The problem is that the filter can only be added on an existing
session. What are the consequences of such a choice ?
Basically, the first time you will send some data, the handshake will
occur. That mans some data will be exchanged from the server to the
client (and vice-versa), each of those data being processed by the
SslFilter in the messageReceived() method.
Just like the data you will send.
Now, what happens when you try to use an asynchronous mode ? Let's look
at some sample code :
...
// Build the connection address
SocketAddress address = new InetSocketAddress(
config.getLdapHost(), config.getLdapPort() );
// And create the connection future
ConnectFuture connectionFuture = connector.connect( address );
// Wait until it's established
connectionFuture.awaitUninterruptibly();
boolean isConnected = connectionFuture.isConnected();
if ( !isConnected )
{
...
Here, if we suppose that we haven't injected a TrustManager in the SSL
context, we will get an exception during the Handshake :
javax.net.ssl.SSLHandshakeException: SSL handshake failed.
at
org.apache.mina.filter.ssl.SslFilter.messageReceived(SslFilter.java:495)
at
org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:434)
at
org.apache.mina.core.filterchain.DefaultIoFilterChain.access$5(DefaultIoFilterChain.java:429)
at
org.apache.mina.core.filterchain.DefaultIoFilterChain$EntryImpl$1.messageReceived(DefaultIoFilterChain.java:796)
at
org.apache.mina.core.filterchain.IoFilterAdapter.messageReceived(IoFilterAdapter.java:119)
at
org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:434)
at
org.apache.mina.core.filterchain.DefaultIoFilterChain.fireMessageReceived(DefaultIoFilterChain.java:426)
at
org.apache.mina.core.polling.AbstractPollingIoProcessor.read(AbstractPollingIoProcessor.java:715)
at
org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:668)
at
org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:657)
at
org.apache.mina.core.polling.AbstractPollingIoProcessor.access$9(AbstractPollingIoProcessor.java:654)
at
org.apache.mina.core.polling.AbstractPollingIoProcessor$Processor.run(AbstractPollingIoProcessor.java:1141)
at
org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:64)
at
java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:680)
Caused by: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
at
com.sun.net.ssl.internal.ssl.Handshaker.checkThrown(Handshaker.java:1015)
at
com.sun.net.ssl.internal.ssl.SSLEngineImpl.checkTaskThrown(SSLEngineImpl.java:485)
at
com.sun.net.ssl.internal.ssl.SSLEngineImpl.writeAppRecord(SSLEngineImpl.java:1128)
at
com.sun.net.ssl.internal.ssl.SSLEngineImpl.wrap(SSLEngineImpl.java:1100)
at javax.net.ssl.SSLEngine.wrap(SSLEngine.java:452)
at org.apache.mina.filter.ssl.SslHandler.handshake(SslHandler.java:575)
at
org.apache.mina.filter.ssl.SslHandler.messageReceived(SslHandler.java:349)
at
org.apache.mina.filter.ssl.SslFilter.messageReceived(SslFilter.java:476)
... 15 more
Caused by: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at
com.sun.net.ssl.internal.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1528)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:243)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:235)
at
com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1206)
at
com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:136)
at
com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:593)
at com.sun.net.ssl.internal.ssl.Handshaker$1.run(Handshaker.java:533)
at java.security.AccessController.doPrivileged(Native Method)
at
com.sun.net.ssl.internal.ssl.Handshaker$DelegatedTask.run(Handshaker.java:952)
at org.apache.mina.filter.ssl.SslHandler.doTasks(SslHandler.java:767)
at org.apache.mina.filter.ssl.SslHandler.handshake(SslHandler.java:541)
... 17 more
Caused by: sun.security.validator.ValidatorException: No trusted
certificate found
at
sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:330)
at
sun.security.validator.SimpleValidator.engineValidate(SimpleValidator.java:110)
at sun.security.validator.Validator.validate(Validator.java:218)
at
com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)
at
com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)
at
com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:249)
at
com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1185)
... 24 more
Fine... But the key here is that the *isConnected* boolean will be
*true* ! And that means we can send data to the other end, without
getting an error. What happens in our case is that when we try to bind,
the writeBindRequest() method blocks forever (well, until we reach the
timeout) :
private void writeBindRequest( BindRequest bindRequest ) throws
LdapException
{
// Send the request to the server
WriteFuture writeFuture = ldapSession.write( bindRequest );
// Wait for the message to be sent to the server
if ( !writeFuture.awaitUninterruptibly( timeout ) )
{
// We didn't received anything : this is an error
LOG.error( "Bind failed : timeout occured" );
throw new LdapException( TIME_OUT_ERROR );
}
}
Nothing will unlock the writeFuture, because we won't get any message
indicating that the write has been successful - or not, as the session
is not valid when the handshake fails.
There is no way with MINA to workaround this (I tried may possible
solution), because the MINA session is always connected as soon as it's
created. Even if the handshake is failing, the session can be
invalidated, but we may already be waiting on it in the writeBindRequet
method.
The best solution would be to modify MINA so that when the session is
invalidated, the awaitUniterruptiby() is interrupted. But the way MINA
is coded is so bloody horrible that I don't want to spend 2 weeks doing
that in MINA 2.
I applied a workaround, and ugly one : I check every 100 ms to see if
the connection is still valid. If the SSL handshake fails, I invalidate
the session, so we are fine.
I must say MINA 2 is depressing ...
--
Regards,
Cordialement,
Emmanuel Lécharny
www.iktek.com