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

Reply via email to