php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #69137 Peer verification fails when using a proxy with SoapClient
Submitted: 2015-02-27 11:12 UTC Modified: 2017-06-23 09:08 UTC
Votes:5
Avg. Score:4.6 ± 0.8
Reproduced:5 of 5 (100.0%)
Same Version:0 (0.0%)
Same OS:4 (80.0%)
From: nj506 at zepler dot net Assigned: nikic (profile)
Status: Closed Package: SOAP related
PHP Version: 5.6.6 OS: All
Private report: No CVE-ID: None
 [2015-02-27 11:12 UTC] nj506 at zepler dot net
Description:
------------
This is the same issue as presented in #67609 - but manifested in the SOAP extension. The SOAP extension uses it's own HTTP handling code (i.e. not http_fopen_wrapper.c where the issue was patched for #67609).

(i) The crypto method defaults to SSL v2/3 - https://github.com/php/php-src/blob/942809909e1bc125db038796c0a1a0b53eeaca7d/ext/soap/php_http.c#L273 - this causes problems when the SOAP endpoint only accepts TLS.

This can be worked around by setting 'ssl_method' to SOAP_SSL_METHOD_TLS in the options supplied to \SoapClient::__construct().

(ii) The name in the peer certificate, by default, is compared to the "url_name" of the SSL socket - https://github.com/php/php-src/blob/c17e007a293356a5b1e511626addb9f13d4eaaee/ext/openssl/xp_ssl.c#L489 - when a proxy is in use, this is the proxy host, not SOAP endpoint host.

This can be worked around by setting "verify_peer_name" to FALSE, or specifying the correct "peer_name" value in the SSL portion of stream context that can be supplied within in the "stream_context" option for \SoapClient::__construct().

By the way, the error raised by SOAP where peer verification fails is very generic, to the point that it is basically impossible to work out what exactly the problem is. I couldn't see anything in the Exception context that indicated the exact problem.

Test script:
---------------
$options = [
  'proxy_host' => '..',
  'proxy_port' => ..
];

$client = new \SoapClient($wsdl, $options);
$client->__soapCall(..);

Expected result:
----------------
Call succeeds

Actual result:
--------------
\SoapFault: Could not connect to host

Patches

php-soap-patch-2152.patch (last revision 2018-11-14 14:53 UTC by tomp at tomp dot uk)

Add a Patch

Pull Requests

Pull requests:

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-06-10 12:49 UTC] nj506 at zepler dot net
I just wanted to add the following, as it may help people track down this issue:

An E_WARNING is triggered in this case, though the error_reporting value is set to exclude E_WARNING level errors (amongst others) here - https://github.com/php/php-src/blob/942809909e1bc125db038796c0a1a0b53eeaca7d/ext/soap/php_http.c#L189

It is possible to observe the error with a customer error handler registered, however -  this resolves to something like:

"SoapClient::__doRequest(): Peer certificate CN=`example.com' did not match expected CN=`192.168.1.155'"

If you see this, and have a proxy set, then you probably are being affected by this bug.
 [2016-09-21 08:36 UTC] tom at netz98 dot de
I can confirm this problem. You need a SSL connection via a proxy to reproduce it. The exact error can be made visible by using your own error-handler before sending the SOAP request, e.g.

    set_error_handler(
        function ($errno , $errstr, $errfile = null, $errline = null) {
            printf(
                "ERROR #%d: %s in %s line %s\n", $errno, $errstr, 
                $errfile, $errline
            );
        }
    );

This produces an error like:


> ERROR #2: SoapClient::__doRequest(): Peer certificate CN=`correct-domain.exmaple.com' did not match expected CN=`proxy.example.net'

The SOAP Fault itself is little saying as reported:

> Fatal error: Uncaught SoapFault exception: [HTTP] Could not connect to host
 [2016-10-03 18:21 UTC] ksmiley at salesforce dot com
Patch with test case for the peer-name verification problem (against the 7.0 branch): https://github.com/php/php-src/pull/2152

As for the first issue about SSL version, STREAM_CRYPTO_METHOD_SSLv23_CLIENT actually means highest available SSL/TLS version, up to TLSv1.2. It's a quirk of OpenSSL; see the description of SSLv23_method: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_new.html. STREAM_CRYPTO_METHOD_TLS_CLIENT will actually restrict the connection to TLSv1.0 only.
 [2016-12-11 21:19 UTC] p dot schulz at ibrams dot com
Could you please backport the patch https://github.com/php/php-src/pull/2152 to 5.6?
 [2017-06-23 09:08 UTC] nikic@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: nikic
 [2017-06-23 09:08 UTC] nikic@php.net
I believe this has been resolved by the aforementioned PR.

This will not be backported to 5.6, as it's no longer actively supported.
 [2018-11-14 14:52 UTC] tomp at tomp dot uk
Our company is running PHP 5.6 as we have PECL modules that don't support PHP 7.

We experienced this issue and the PHP 7 patch wouldn't apply cleanly to the PHP 5.6 tree, so I have backported it to PHP 5.6. This has been tested to work and is in production.

diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c
index 3246091..fcf593e 100644
--- a/ext/soap/php_http.c
+++ b/ext/soap/php_http.c
@@ -163,7 +163,7 @@ void http_context_headers(php_stream_context* context,
 static php_stream* http_connect(zval* this_ptr, php_url *phpurl, int use_ssl, php_stream_context *context, int *use_proxy TSRMLS_DC)
 {
 	php_stream *stream;
-	zval **proxy_host, **proxy_port, **tmp;
+	zval **proxy_host, **proxy_port, **tmp, ssl_proxy_peer_name;
 	char *host;
 	char *name;
 	char *protocol;
@@ -245,6 +245,13 @@ static php_stream* http_connect(zval* this_ptr, php_url *phpurl, int use_ssl, ph
 		char *http_headers;
 		int http_header_size;
 
+		/* Set peer_name or name verification will try to use the proxy server name */
+		if (!stream->context || php_stream_context_get_option(stream->context, "ssl", "peer_name", &tmp) != SUCCESS) {
+			ZVAL_STRING(&ssl_proxy_peer_name, phpurl->host, 1);
+			php_stream_context_set_option(stream->context, "ssl", "peer_name", &ssl_proxy_peer_name);
+			zval_dtor(&ssl_proxy_peer_name);
+		}
+
 		smart_str_append_const(&soap_headers, "CONNECT ");
 		smart_str_appends(&soap_headers, phpurl->host);
 		smart_str_appendc(&soap_headers, ':');
diff --git a/ext/soap/soap.c b/ext/soap/soap.c
index 569701a..571db1f 100644
--- a/ext/soap/soap.c
+++ b/ext/soap/soap.c
@@ -2398,7 +2398,9 @@ PHP_METHOD(SoapClient, SoapClient)
 				Z_TYPE_PP(tmp) == IS_RESOURCE) {
 			context = php_stream_context_from_zval(*tmp, 1);
 			zend_list_addref(context->rsrc_id);
-		}
+		} else {
+			context = php_stream_context_alloc();
+		}
 
 		if (zend_hash_find(ht, "location", sizeof("location"), (void**)&tmp) == SUCCESS &&
 		    Z_TYPE_PP(tmp) == IS_STRING) {
 
PHP Copyright © 2001-2018 The PHP Group
All rights reserved.
Last updated: Mon Dec 17 08:01:26 2018 UTC