How to make Indy OpenSSL compatible with most servers

Coder12345 picture Coder12345 · Feb 28, 2014 · Viewed 8k times · Source

I use the following code to setup SSLHandler for POP3/SMTP sending/receiving app:

IdSSLHandler->SSLOptions->Mode        = sslmClient;
IdSSLHandler->SSLOptions->Method      = slvSSLv23;
IdSSLHandler->SSLOptions->SSLVersions = TIdSSLVersions() << sslvSSLv3 << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2;

So, the above code is supposed to support SSL 3, TLS 1, TLS 1.1 and TLS 1.2 automatically. This does not work well and reports "wrong version" error. When the SSLVersions line is removed then it works but by defaults it includes sslvSSLv2 which I don't want to support. It is the same like:

IdSSLHandler->SSLOptions->Mode        = sslmClient;
IdSSLHandler->SSLOptions->Method      = slvSSLv23;
IdSSLHandler->SSLOptions->SSLVersions = TIdSSLVersions() << sslvSSLv2 << sslvSSLv3 << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2;

For some reason, this works and the above does not on same server. I know that slvSSLv23 is a kind of "use any available version" value. So why does it not work with above code where version 2 is not present?

Additionally, I can use TSL1 which seems to be widely deployed, but if the server supports 1.1 or 1.2 then my code won't be using more recent versions but will force 1.0 version unless something like above is used.

I would like to make an initialization with the following goals:

  • compatible with all servers, regardless if they use v3, tls1, tls1.1 or tls1.2
  • automatically use the most recent version and use lower version if more recent is not available on the server but not lower than version 3 - fail/exception if version is lower than 3

I thought the first version of the code would provide that but it reports version error. Are the above goals possible or a user-setting must be provided to select SSL version to use?

Answer

Remy Lebeau picture Remy Lebeau · Mar 1, 2014

Setting the SSLVersions to a single value automatically sets the Method to the corresponding single version. Setting the SSLVersions to multiple values automatically sets the Method to slvSSLv23.

Setting the Method to a single version automatically sets the SSLVersions to the corresponding single value. Setting the Method to slvSSLv23 automatically sets the SSLVersions to all supported values.

Indy activates/deactivates the appropriate SSL_OP_NO_SSL_v# and SSL_OP_NO_TLS_v# flags in OpenSSL depending on the values of Method and SSLVersions.

I would suggest the following approach, which is a slightly more future proof as new TLS versions are added:

IdSSLHandler->SSLOptions->Method      = sslvSSLv23;
IdSSLHandler->SSLOptions->SSLVersions = IdSSLHandler->SSLOptions->SSLVersions >> sslvSSLv2;

Either way, Indy would be using SSLv23 with only the SSL_OP_NO_SSL_v2 flag activated.

In that configuration, a "wrong version" error typically means the server is using a specific version that does not support version negotiation (which uses an SSLv2-compatible client hello). In other words, an SSLv23 client connecting to a TLSv1 server will fail. The server must use SSLv23 in order to support version negotiation. Using SSLv23 on the server allows it to accept any version client, as the client initiates the handshake so the server can see which version header is being used. But using SSLv23 on the client DOES NOT allow it to connect to any server OTHER THAN an SSLv23 server. An SSLv2 server can only accept SSLv2 clients, an SSLv3 server can only accept SSLv3 clients, a TLSv1 server can only accept TLSv1 clients, and so on.

To truly connect to "any" server, you would have to detect a "wrong version" error and retry with a different specific Method/SSLVersions configuration. Unfortunately, the "wrong version" reply does not include the server's actual version, so you have to use trial-and-error. If SSLv23 fails, try TLSv1_2. If that fails, try TLSv1_1. If that fails, try TLSv1. If that fails, try SSLv3.