1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix #49169: SoapServer calls wrong function, although "SOAP action" header is correct

Although the original reproducer no longer exists, I was able to cook up
something similar.
The problem is that there are two ways ext-soap currently looks up
functions:
1) By matching the exact function name; but this doesn't work if the
   function name is not in the body.
2) By matching the parameter names.

Neither of these work when we don't have the function name in the body,
and when the parameter names are not unique. That's where we can use the
"SOAPAction" header to distinguish between different actions. This header
should be checked first and be matched against the "soapAction"
attribute in the WSDL. We keep the existing fallbacks such that the
chance of a BC break is minimized.
Note that since #49169 a potential target namespace is ignored right
now.

Closes GH-15970.
This commit is contained in:
Niels Dossche
2024-09-21 00:23:24 +02:00
parent 6cf467cc9f
commit 63e0b9ccbf
4 changed files with 151 additions and 11 deletions

4
NEWS
View File

@@ -15,4 +15,8 @@ PHP NEWS
- Random:
. Moves from /dev/urandom usage to arc4random_buf on Haiku. (David Carlier)
- SOAP:
. Fixed bug #49169 (SoapServer calls wrong function, although "SOAP action"
header is correct). (nielsdos)
<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>

View File

@@ -58,7 +58,7 @@ static sdlParamPtr get_param(sdlFunctionPtr function, const char *param_name, ze
static sdlFunctionPtr get_function(sdlPtr sdl, const char *function_name, size_t function_name_length);
static sdlFunctionPtr get_doc_function(sdlPtr sdl, xmlNodePtr params);
static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, const char* actor, zval *function_name, uint32_t *num_params, zval **parameters, int *version, soapHeader **headers);
static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, const char* actor, const char *soap_action, zval *function_name, uint32_t *num_params, zval **parameters, int *version, soapHeader **headers);
static xmlDocPtr serialize_response_call(sdlFunctionPtr function, const char *function_name, const char *uri,zval *ret, soapHeader *headers, int version);
static xmlDocPtr serialize_function_call(zval *this_ptr, sdlFunctionPtr function, const char *function_name, const char *uri, zval *arguments, uint32_t arg_count, int version, HashTable *soap_headers);
static xmlNodePtr serialize_parameter(sdlParamPtr param,zval *param_val, uint32_t index,const char *name, int style, xmlNodePtr parent);
@@ -1273,6 +1273,7 @@ PHP_METHOD(SoapServer, handle)
HashTable *old_class_map, *old_typemap;
int old_features;
zval tmp_soap;
const char *soap_action = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &arg, &arg_len) == FAILURE) {
RETURN_THROWS();
@@ -1341,7 +1342,7 @@ PHP_METHOD(SoapServer, handle)
if (!arg) {
if (SG(request_info).request_body && 0 == php_stream_rewind(SG(request_info).request_body)) {
zval *server_vars, *encoding;
zval *server_vars, *encoding, *soap_action_z;
php_stream_filter *zf = NULL;
zend_string *server = ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_SERVER);
@@ -1375,6 +1376,10 @@ PHP_METHOD(SoapServer, handle)
}
}
if ((soap_action_z = zend_hash_str_find(Z_ARRVAL_P(server_vars), ZEND_STRL("HTTP_SOAPACTION"))) != NULL && Z_TYPE_P(soap_action_z) == IS_STRING) {
soap_action = Z_STRVAL_P(soap_action_z);
}
doc_request = soap_xmlParseFile("php://input");
if (zf) {
@@ -1418,7 +1423,7 @@ PHP_METHOD(SoapServer, handle)
old_soap_version = SOAP_GLOBAL(soap_version);
zend_try {
function = deserialize_function_call(service->sdl, doc_request, service->actor, &function_name, &num_params, &params, &soap_version, &soap_headers);
function = deserialize_function_call(service->sdl, doc_request, service->actor, soap_action, &function_name, &num_params, &params, &soap_version, &soap_headers);
} zend_catch {
/* Avoid leaking persistent memory */
xmlFreeDoc(doc_request);
@@ -3090,6 +3095,37 @@ static sdlFunctionPtr find_function(sdlPtr sdl, xmlNodePtr func, zval* function_
}
/* }}} */
static sdlFunctionPtr find_function_using_soap_action(const sdl *sdl, const char *soap_action, zval* function_name)
{
if (!sdl) {
return NULL;
}
/* The soap action may be a http-quoted string, in which case we're removing the quotes here. */
size_t soap_action_length = strlen(soap_action);
if (soap_action[0] == '"') {
if (soap_action_length < 2 || soap_action[soap_action_length - 1] != '"') {
return NULL;
}
soap_action++;
soap_action_length -= 2;
}
/* TODO: This may depend on a particular target namespace, in which case this won't find a match when multiple different
* target namespaces are used until #45282 is resolved. */
sdlFunctionPtr function;
ZEND_HASH_FOREACH_PTR(&sdl->functions, function) {
if (function->binding && function->binding->bindingType == BINDING_SOAP) {
sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
if (fnb && fnb->soapAction && strncmp(fnb->soapAction, soap_action, soap_action_length) == 0 && fnb->soapAction[soap_action_length] == '\0') {
ZVAL_STRING(function_name, function->functionName);
return function;
}
}
} ZEND_HASH_FOREACH_END();
return NULL;
}
static xmlNodePtr get_envelope(xmlNodePtr trav, int *version, char **envelope_ns) {
while (trav != NULL) {
if (trav->type == XML_ELEMENT_NODE) {
@@ -3115,12 +3151,12 @@ static xmlNodePtr get_envelope(xmlNodePtr trav, int *version, char **envelope_ns
return NULL;
}
static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, const char* actor, zval *function_name, uint32_t *num_params, zval **parameters, int *version, soapHeader **headers) /* {{{ */
static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, const char* actor, const char *soap_action, zval *function_name, uint32_t *num_params, zval **parameters, int *version, soapHeader **headers) /* {{{ */
{
char* envelope_ns = NULL;
xmlNodePtr trav,env,head,body,func;
xmlAttrPtr attr;
sdlFunctionPtr function;
sdlFunctionPtr function = NULL;
encode_reset_ns();
@@ -3204,12 +3240,17 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c
}
trav = trav->next;
}
if (soap_action) {
function = find_function_using_soap_action(sdl, soap_action, function_name);
}
if (func == NULL) {
function = get_doc_function(sdl, NULL);
if (function != NULL) {
ZVAL_STRING(function_name, (char *)function->functionName);
} else {
soap_server_fault("Client", "looks like we got \"Body\" without function call", NULL, NULL, NULL);
if (!function) {
function = get_doc_function(sdl, NULL);
if (function) {
ZVAL_STRING(function_name, (char *)function->functionName);
} else {
soap_server_fault("Client", "looks like we got \"Body\" without function call", NULL, NULL, NULL);
}
}
} else {
if (*version == SOAP_1_1) {
@@ -3223,7 +3264,9 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c
soap_server_fault("DataEncodingUnknown","Unknown Data Encoding Style", NULL, NULL, NULL);
}
}
function = find_function(sdl, func, function_name);
if (!function) {
function = find_function(sdl, func, function_name);
}
if (sdl != NULL && function == NULL) {
if (*version == SOAP_1_2) {
soap_server_fault("rpc:ProcedureNotPresent","Procedure not present", NULL, NULL, NULL);

View File

@@ -0,0 +1,44 @@
--TEST--
Bug #49169 (SoapServer calls wrong function, although "SOAP action" header is correct)
--EXTENSIONS--
soap
--INI--
soap.wsdl_cache_enabled=0
--SKIPIF--
<?php
if (php_sapi_name()=='cli') echo 'skip';
?>
--POST--
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
>
<SOAP-ENV:Body>
<testParam xsi:type="xsd:string">hello</testParam>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
--FILE--
<?php
function test($input) {
return strrev($input);
}
function test2($input) {
return strlen($input);
}
$server = new soapserver(__DIR__.'/bug49169.wsdl', []);
$server->addfunction("test");
$server->addfunction("test2");
$_SERVER["HTTP_SOAPACTION"] = "#test";
$server->handle();
$_SERVER["HTTP_SOAPACTION"] = "#test2";
$server->handle();
?>
--EXPECT--
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><testParam xsi:type="xsd:string">olleh</testParam></SOAP-ENV:Body></SOAP-ENV:Envelope>
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><testParam xsi:type="xsd:string">5</testParam></SOAP-ENV:Body></SOAP-ENV:Envelope>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<definitions name="InteropTest"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="http://test-uri/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="http://test-uri/">
<message name="testMessage">
<part name="testParam" type="xsd:string"/>
</message>
<portType name="testPortType">
<operation name="test">
<input message="testMessage"/>
<output message="testMessage"/>
</operation>
<operation name="test2">
<input message="testMessage"/>
<output message="testMessage"/>
</operation>
</portType>
<binding name="testBinding" type="testPortType">
<soap:binding style="rcp" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="test">
<soap:operation soapAction="#test" style="rcp"/>
<input>
<soap:body use="encoded" namespace="http://test-uri/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output>
<soap:body use="encoded" namespace="http://test-uri/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
<operation name="test2">
<soap:operation soapAction="#test2" style="rcp"/>
<input>
<soap:body use="encoded" namespace="http://test-uri/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output>
<soap:body use="encoded" namespace="http://test-uri/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
</binding>
<service name="testService">
<port name="testPort" binding="tns:testBinding">
<soap:address location="http://localhost:8080/server.php" />
</port>
</service>
</definitions>