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
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: nj506 at zepler dot net
New email:
PHP Version: OS:

 

 [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)

Pull Requests

Pull requests:

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-2025 The PHP Group
All rights reserved.
Last updated: Tue Jan 28 02:01:30 2025 UTC