Skip to content

Commit d9cda73

Browse files
committed
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.
1 parent 0db2f33 commit d9cda73

File tree

4 files changed

+153
-11
lines changed

4 files changed

+153
-11
lines changed

ext/soap/soap.c

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ static sdlParamPtr get_param(sdlFunctionPtr function, const char *param_name, ze
5858
static sdlFunctionPtr get_function(sdlPtr sdl, const char *function_name, size_t function_name_length);
5959
static sdlFunctionPtr get_doc_function(sdlPtr sdl, xmlNodePtr params);
6060

61-
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);
61+
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);
6262
static xmlDocPtr serialize_response_call(sdlFunctionPtr function, const char *function_name, const char *uri,zval *ret, soapHeader *headers, int version);
6363
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);
6464
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)
12731273
HashTable *old_class_map, *old_typemap;
12741274
int old_features;
12751275
zval tmp_soap;
1276+
const char *soap_action = NULL;
12761277

12771278
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &arg, &arg_len) == FAILURE) {
12781279
RETURN_THROWS();
@@ -1341,7 +1342,7 @@ PHP_METHOD(SoapServer, handle)
13411342

13421343
if (!arg) {
13431344
if (SG(request_info).request_body && 0 == php_stream_rewind(SG(request_info).request_body)) {
1344-
zval *server_vars, *encoding;
1345+
zval *server_vars, *encoding, *soap_action_z;
13451346
php_stream_filter *zf = NULL;
13461347
zend_string *server = ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_SERVER);
13471348

@@ -1375,6 +1376,10 @@ PHP_METHOD(SoapServer, handle)
13751376
}
13761377
}
13771378

1379+
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) {
1380+
soap_action = Z_STRVAL_P(soap_action_z);
1381+
}
1382+
13781383
doc_request = soap_xmlParseFile("php://input");
13791384

13801385
if (zf) {
@@ -1418,7 +1423,7 @@ PHP_METHOD(SoapServer, handle)
14181423
old_soap_version = SOAP_GLOBAL(soap_version);
14191424

14201425
zend_try {
1421-
function = deserialize_function_call(service->sdl, doc_request, service->actor, &function_name, &num_params, &params, &soap_version, &soap_headers);
1426+
function = deserialize_function_call(service->sdl, doc_request, service->actor, soap_action, &function_name, &num_params, &params, &soap_version, &soap_headers);
14221427
} zend_catch {
14231428
/* Avoid leaking persistent memory */
14241429
xmlFreeDoc(doc_request);
@@ -3090,6 +3095,32 @@ static sdlFunctionPtr find_function(sdlPtr sdl, xmlNodePtr func, zval* function_
30903095
}
30913096
/* }}} */
30923097

3098+
static sdlFunctionPtr find_function_using_soap_action(const sdl *sdl, const char *soap_action, zval* function_name)
3099+
{
3100+
/* The soap action may be a http-quoted string, in which case we're removing the quotes here. */
3101+
if (soap_action[0] == '"') {
3102+
soap_action++;
3103+
}
3104+
size_t soap_action_length = strlen(soap_action);
3105+
if (soap_action_length > 0 && soap_action[soap_action_length - 1] == '"') {
3106+
soap_action_length--;
3107+
}
3108+
3109+
/* TODO: This may depend on a particular target namespace, in which case this won't find a match when multiple different
3110+
* target namespaces are used until #45282 is resolved. */
3111+
sdlFunctionPtr function;
3112+
ZEND_HASH_FOREACH_PTR(&sdl->functions, function) {
3113+
if (function->binding && function->binding->bindingType == BINDING_SOAP) {
3114+
sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
3115+
if (fnb->soapAction && strncmp(fnb->soapAction, soap_action, soap_action_length) == 0 && fnb->soapAction[soap_action_length] == '\0') {
3116+
ZVAL_STRING(function_name, function->functionName);
3117+
return function;
3118+
}
3119+
}
3120+
} ZEND_HASH_FOREACH_END();
3121+
return NULL;
3122+
}
3123+
30933124
static xmlNodePtr get_envelope(xmlNodePtr trav, int *version, char **envelope_ns) {
30943125
while (trav != NULL) {
30953126
if (trav->type == XML_ELEMENT_NODE) {
@@ -3115,12 +3146,12 @@ static xmlNodePtr get_envelope(xmlNodePtr trav, int *version, char **envelope_ns
31153146
return NULL;
31163147
}
31173148

3118-
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) /* {{{ */
3149+
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) /* {{{ */
31193150
{
31203151
char* envelope_ns = NULL;
31213152
xmlNodePtr trav,env,head,body,func;
31223153
xmlAttrPtr attr;
3123-
sdlFunctionPtr function;
3154+
sdlFunctionPtr function = NULL;
31243155

31253156
encode_reset_ns();
31263157

@@ -3204,12 +3235,17 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c
32043235
}
32053236
trav = trav->next;
32063237
}
3238+
if (soap_action) {
3239+
function = find_function_using_soap_action(sdl, soap_action, function_name);
3240+
}
32073241
if (func == NULL) {
3208-
function = get_doc_function(sdl, NULL);
3209-
if (function != NULL) {
3210-
ZVAL_STRING(function_name, (char *)function->functionName);
3211-
} else {
3212-
soap_server_fault("Client", "looks like we got \"Body\" without function call", NULL, NULL, NULL);
3242+
if (!function) {
3243+
function = get_doc_function(sdl, NULL);
3244+
if (function) {
3245+
ZVAL_STRING(function_name, (char *)function->functionName);
3246+
} else {
3247+
soap_server_fault("Client", "looks like we got \"Body\" without function call", NULL, NULL, NULL);
3248+
}
32133249
}
32143250
} else {
32153251
if (*version == SOAP_1_1) {
@@ -3223,7 +3259,9 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c
32233259
soap_server_fault("DataEncodingUnknown","Unknown Data Encoding Style", NULL, NULL, NULL);
32243260
}
32253261
}
3226-
function = find_function(sdl, func, function_name);
3262+
if (!function) {
3263+
function = find_function(sdl, func, function_name);
3264+
}
32273265
if (sdl != NULL && function == NULL) {
32283266
if (*version == SOAP_1_2) {
32293267
soap_server_fault("rpc:ProcedureNotPresent","Procedure not present", NULL, NULL, NULL);

ext/soap/tests/bugs/bug49169.phpt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
Bug #49169 (SoapServer calls wrong function, although "SOAP action" header is correct)
3+
--EXTENSIONS--
4+
soap
5+
--INI--
6+
soap.wsdl_cache_enabled=0
7+
--SKIPIF--
8+
<?php
9+
if (php_sapi_name()=='cli') echo 'skip';
10+
?>
11+
--POST--
12+
<SOAP-ENV:Envelope
13+
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
14+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
15+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
16+
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
17+
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
18+
>
19+
<SOAP-ENV:Body>
20+
<testParam xsi:type="xsd:string">hello</testParam>
21+
</SOAP-ENV:Body>
22+
</SOAP-ENV:Envelope>
23+
--FILE--
24+
<?php
25+
function test($input) {
26+
return strrev($input);
27+
}
28+
function test2($input) {
29+
return strlen($input);
30+
}
31+
32+
$server = new soapserver(__DIR__.'/bug49169.wsdl', []);
33+
$server->addfunction("test");
34+
$server->addfunction("test2");
35+
$_SERVER["HTTP_SOAPACTION"] = "#test";
36+
$server->handle();
37+
$_SERVER["HTTP_SOAPACTION"] = "#test2";
38+
$server->handle();
39+
?>
40+
--EXPECT--
41+
<?xml version="1.0" encoding="UTF-8"?>
42+
<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>
43+
<?xml version="1.0" encoding="UTF-8"?>
44+
<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>

ext/soap/tests/bugs/bug49169.post

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<SOAP-ENV:Envelope
2+
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
3+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
6+
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
7+
>
8+
<SOAP-ENV:Body>
9+
<testParam xsi:type="xsd:string">hello</testParam>
10+
</SOAP-ENV:Body>
11+
</SOAP-ENV:Envelope>

ext/soap/tests/bugs/bug49169.wsdl

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<definitions name="InteropTest"
3+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
4+
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
5+
xmlns:tns="http://test-uri/"
6+
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
7+
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
8+
xmlns="http://schemas.xmlsoap.org/wsdl/"
9+
targetNamespace="http://test-uri/">
10+
<message name="testMessage">
11+
<part name="testParam" type="xsd:string"/>
12+
</message>
13+
<portType name="testPortType">
14+
<operation name="test">
15+
<input message="testMessage"/>
16+
<output message="testMessage"/>
17+
</operation>
18+
<operation name="test2">
19+
<input message="testMessage"/>
20+
<output message="testMessage"/>
21+
</operation>
22+
</portType>
23+
<binding name="testBinding" type="testPortType">
24+
<soap:binding style="rcp" transport="http://schemas.xmlsoap.org/soap/http"/>
25+
<operation name="test">
26+
<soap:operation soapAction="#test" style="rcp"/>
27+
<input>
28+
<soap:body use="encoded" namespace="http://test-uri/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
29+
</input>
30+
<output>
31+
<soap:body use="encoded" namespace="http://test-uri/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
32+
</output>
33+
</operation>
34+
<operation name="test2">
35+
<soap:operation soapAction="#test2" style="rcp"/>
36+
<input>
37+
<soap:body use="encoded" namespace="http://test-uri/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
38+
</input>
39+
<output>
40+
<soap:body use="encoded" namespace="http://test-uri/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
41+
</output>
42+
</operation>
43+
</binding>
44+
<service name="testService">
45+
<port name="testPort" binding="tns:testBinding">
46+
<soap:address location="http://localhost:8080/server.php" />
47+
</port>
48+
</service>
49+
</definitions>

0 commit comments

Comments
 (0)