When trying to make a drupal_http_request over HTTPS from a site running PHP 5.3 (openSSL v0.9.8b) and the remote server is running openSSL v1.0.0 or newer the certificate version negotiation fails.

The internal error is: OpenSSL Error messages: error:14077458:SSL routines:SSL23_GET_SERVER_HELLO:reason(1112)

The solution is to define the transport/ssl certificate version expected. The PHP documentation has this definition: ssl:// will attempt to negotiate an SSL V2, or SSL V3 connection depending on the capabilities and preferences of the remote host. sslv2:// and sslv3:// will select the SSL V2 or SSL V3 protocol explicitly.

CommentFileSizeAuthor
#29 interdiff_28-29.txt414 bytesheddn
#29 drupal-ssl-socket-transports-1879970-29.patch1.1 KBheddn
PASSED: [[SimpleTest]]: [MySQL] 41,229 pass(es). View
#28 drupal-ssl-socket-transports-1879970-28.patch1.21 KBheddn
PASSED: [[SimpleTest]]: [MySQL] 40,911 pass(es). View
#13 ssl-socket-transports-1879970-13.patch1.51 KBDavid_Rothstein
PASSED: [[SimpleTest]]: [MySQL] 40,865 pass(es). View
#2 http_request_openssl-1879970-2-d6.patch1.58 KBcableman0408
FAILED: [[SimpleTest]]: [MySQL] Unable to apply patch http_request_openssl-1879970-2-d6.patch. Unable to apply patch. See the log in the details link for more information. View
#1 http_request_openssl-1879970-1.patch1.16 KBcableman0408
PASSED: [[SimpleTest]]: [MySQL] 40,140 pass(es). View
Members fund testing for the Drupal project. Drupal Association Learn more

Comments

cableman0408’s picture

FileSize
1.16 KB
PASSED: [[SimpleTest]]: [MySQL] 40,140 pass(es). View

This patch adds the option to set the SSL certificate version in the drupal_http_request options array.

cableman0408’s picture

FileSize
1.58 KB
FAILED: [[SimpleTest]]: [MySQL] Unable to apply patch http_request_openssl-1879970-2-d6.patch. Unable to apply patch. See the log in the details link for more information. View

This is a Drupal 6 backport of the patch from #1

cableman0408’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, http_request_openssl-1879970-2-d6.patch, failed testing.

cableman0408’s picture

Status: Needs work » Needs review
alphawebgroup’s picture

Hello Community.
I've had the same problem with ssl requests.
I've applied this patch to my D7.x core and tested. Works good.
I beleave, we should have this patch #1 in core.

Regards, Vitaliy

alphawebgroup’s picture

alphawebgroup’s picture

Status: Needs review » Reviewed & tested by the community

Automated QA tests also have been completed without any errors.

jbrown’s picture

Version: 7.x-dev » 8.x-dev
Status: Reviewed & tested by the community » Needs work

This needs to be fixed on 8 first.

greggles’s picture

Version: 8.x-dev » 7.x-dev
Status: Needs work » Reviewed & tested by the community

Given #1862398: [meta] Replace drupal_http_request() with Guzzle I don't think it makes sense to fix this in 8.x. I believe Guzzle (via curl) supports this already.

The code from #1 still applies and still works great.

das-peter’s picture

I've just tested the approach with a D7 installation to fix this related issue: #1959840: Unable to connect to the OHT service (OpenSSL 0.9.8 vs. OpenSSL 1.0.0)
Works like a charm! Thanks.

greggles’s picture

Title: drupal_http_request fails when remote server is using openssl v1.0.0 » drupal_http_request fails when remote server is using openssl v1.0.0 (allow calling code to specify sslv2 sslv3 tls)

Updated title to make it easier to find.

David_Rothstein’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
1.51 KB
PASSED: [[SimpleTest]]: [MySQL] 40,865 pass(es). View

Yeah, it looks like Drupal 8 can probably do this already (not sure if anyone has verified though).

The basic approach seems OK, but it's a very non-standard way to add an option to this function - the documentation in this patch implies that you can never use 'ssl' (which is in fact the default and the most common case), and similarly the $options array is going to be lying most of the time about which one is being used? I also don't understand why the variable is called 'ssl_version' (and is documented as storing an SSL version) when in fact that's not what it stores at all.

The attached Drupal 7 patch tries to clean this up a bit.

mikeytown2’s picture

I'm not 100% sure curl (what guzzle uses) will support that if php is less than 5.5
https://bugs.php.net/bug.php?id=62318
https://github.com/php/php-src/blob/74394733ed36f642fa52e10b73284e3ad2d3...

The commit that added this in on 2011-12-01
https://github.com/php/php-src/commit/e69f987948982d4259a574ca824398c261...

EDIT:
looks like this might do it
curl_setopt($curl, CURLOPT_SSLVERSION, 3);
http://guzzlephp.org/tour/http.html#low-level-curl-access

cableman0408’s picture

Status: Needs review » Reviewed & tested by the community

I have tested the solution from #13 in production environments and it works as expected.

mikeytown2’s picture

Damien Tournoud’s picture

Status: Reviewed & tested by the community » Needs review

I looked into this issue.

The problem seems to be that newer versions of OpenSSL send a warning-level alert in some cases (most notably: SNI name unrecognized), which older versions of OpenSSL trips on. There is a now a suggested patch.

The PHP documentation (and the naming of the transports) is misleading. Here is the exact definition of the transports:

  • ssl: negotiates TLSv1, SSLv3 or SSLv2 in this order (uses SSLv23_method internally);
  • tls: negotiates only TLSv1 (uses TLSv1_method internally)
  • sslv2: negotiates only SSLv2 (uses SSLv2_method internally)
  • sslv3: negotiates only SSLv3 (uses SSLv3_method internally)

Specifying an explicit SSLv2 or SSLv3 version solves the problem in this case, but just because it avoids TLS (and as a consequence SNI) altogether.

The problem probably comes from the fact that PHP sends a SNI host name unconditionally (the "SNI_enabled" context option actually defaults to TRUE), so an arguably better fix would be to disable SNI completely. We don't verify the peer certificate anyway.

greggles’s picture

re #17 - it looks like SNI_enabled is only available in PHP 5.3.2+ so it's not an option for Drupal 7 (right?).

Damien Tournoud’s picture

@greggles: I assume that older PHP versions don't support SNI and as a consequence are just not affected.

McGo’s picture

Same problem here for me after updating openssl from 0.9.8 to 1.0.1, fix in #13 works when using tls as transport.

ivanstegic’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community

I have applied the patch in #13 and tested it on PHP 5.3.10-1ubuntu3.9 in a pre-production environment. Changing the status to RTBC.

pcambra’s picture

Just confirming the RTBC for this.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 13: ssl-socket-transports-1879970-13.patch, failed testing.

mikeytown2’s picture

Status: Needs work » Needs review

13: ssl-socket-transports-1879970-13.patch queued for re-testing.

Test went fatal on this
The test did not complete due to a fatal error. Completion check form.test 261 FormsTestCase->testSelect()

mikeytown2’s picture

Status: Needs review » Reviewed & tested by the community

Setting back to RTBC

David_Rothstein’s picture

Status: Reviewed & tested by the community » Needs review

It doesn't look like Damien Tournoud's comments above have been fully addressed? So I think this should be "needs review". (It certainly would be nice to be able to fix the problem directly rather than adding yet another confusing option to this function. Although verifying the peer certificate is something that might eventually be added at #1081192: Verify peer on HTTPS if cURL available (but be careful of built-in cert bundles in the codebase).)

heddn’s picture

I did some research and from what I can tell, PHP 5.2 could have SNI support available if it is compiled. There just isn't any way to turn it off until 5.3.2+. This gets fixed by simply upgrading the OpenSSL stack. A fix is now in OpenSSL 0.9.8za per http://git.openssl.org/gitweb/?p=openssl.git;a=blob_plain;f=CHANGES;hb=r.... However, the stable release of 0.9.8 for some distros is older (see ubuntu), not 0.9.8za, so its going to be a while. Thankfully, using Ubuntu as an example, LTS support for 10.04 ends April 2015 so if we just sit on this the problem will go away on its own.

I'm still trying to confirm what versions of PHP and OpenSSL are installed on my prod LAMP stack, but this is hitting me with linkchecker module. It keeps throwing

Error opening socket ssl://example.com:443

. We can't use the cURL options from D8, since D7 still uses dhr(). If we go with #13, this doesn't help thinks like linkchecker, because we don't really know if the end-point is running SSL2 or SSL3. Long story short, upgrading OpenSSL is probably the best solution, not trying to solve this in Drupal.

heddn’s picture

FileSize
1.21 KB
PASSED: [[SimpleTest]]: [MySQL] 40,911 pass(es). View

While upgrading the OpenSSL version is ideal, the client isn't able to for various reason. They are however running PHP 5.3.x. So, let's see if this fixes the problem for them. This follows the approach proposed in #17. It doesn't solve the problem for earlier versions of PHP, but then I'm not sure there actually are any solutions unless OpenSSL is upgraded or PHP is upgraded.

heddn’s picture

FileSize
1.1 KB
PASSED: [[SimpleTest]]: [MySQL] 41,229 pass(es). View
414 bytes

After second though, I'm removing the else logic. If someone is providing a context, they know what they are doing and might not want SNI disabled.

Status: Needs review » Needs work

The last submitted patch, 29: drupal-ssl-socket-transports-1879970-29.patch, failed testing.

heddn’s picture

Status: Needs work » Needs review

Re-titling.

heddn’s picture

Title: drupal_http_request fails when remote server is using openssl v1.0.0 (allow calling code to specify sslv2 sslv3 tls) » drupal_http_request fails when remote server is using openssl v1.0.0
heddn’s picture

A similar approach was committed to httprl, could we do similar to core?

#2337071: Disable Server Name Indication (SNI) by default

heddn’s picture

This doesn't need to go to D8 first, since we don't want to disable SNI over there. Just in D7.

mgifford’s picture

Issue tags: +security, +ssl

Just tagging.. This seems fairly straight forward though...

hass’s picture

If we disable SNI we will run with fastly into issues. Update.d.o need to move to a native ip without SNI.

basic’s picture

Fastly does not use SNI, but a SAN cert on a dedicated IP.

heddn’s picture

I wonder if this is a won't fix now that PHP 5.3 is sunset?