Skip to content

Commit 63e0b9c

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. Closes GH-15970.
1 parent 6cf467c commit 63e0b9c

File tree

4 files changed

+151
-11
lines changed

4 files changed

+151
-11
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,8 @@ PHP NEWS
1515
- Random:
1616
. Moves from /dev/urandom usage to arc4random_buf on Haiku. (David Carlier)
1717

18+
- SOAP:
19+
. Fixed bug #49169 (SoapServer calls wrong function, although "SOAP action"
20+
header is correct). (nielsdos)
21+
1822
<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>

ext/soap/soap.c

Lines changed: 54 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,37 @@ 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+
if (!sdl) {
3101+
return NULL;
3102+
}
3103+
3104+
/* The soap action may be a http-quoted string, in which case we're removing the quotes here. */
3105+
size_t soap_action_length = strlen(soap_action);
3106+
if (soap_action[0] == '"') {
3107+
if (soap_action_length < 2 || soap_action[soap_action_length - 1] != '"') {
3108+
return NULL;
3109+
}
3110+
soap_action++;
3111+
soap_action_length -= 2;
3112+
}
3113+
3114+
/* TODO: This may depend on a particular target namespace, in which case this won't find a match when multiple different
3115+
* target namespaces are used until #45282 is resolved. */
3116+
sdlFunctionPtr function;
3117+
ZEND_HASH_FOREACH_PTR(&sdl->functions, function) {
3118+
if (function->binding && function->binding->bindingType == BINDING_SOAP) {
3119+
sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
3120+
if (fnb && fnb->soapAction && strncmp(fnb->soapAction, soap_action, soap_action_length) == 0 && fnb->soapAction[soap_action_length] == '\0') {
3121+
ZVAL_STRING(function_name, function->functionName);
3122+
return function;
3123+
}
3124+
}
3125+
} ZEND_HASH_FOREACH_END();
3126+
return NULL;
3127+
}
3128+
30933129
static xmlNodePtr get_envelope(xmlNodePtr trav, int *version, char **envelope_ns) {
30943130
while (trav != NULL) {
30953131
if (trav->type == XML_ELEMENT_NODE) {
@@ -3115,12 +3151,12 @@ static xmlNodePtr get_envelope(xmlNodePtr trav, int *version, char **envelope_ns
31153151
return NULL;
31163152
}
31173153

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) /* {{{ */
3154+
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) /* {{{ */
31193155
{
31203156
char* envelope_ns = NULL;
31213157
xmlNodePtr trav,env,head,body,func;
31223158
xmlAttrPtr attr;
3123-
sdlFunctionPtr function;
3159+
sdlFunctionPtr function = NULL;
31243160

31253161
encode_reset_ns();
31263162

@@ -3204,12 +3240,17 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c
32043240
}
32053241
trav = trav->next;
32063242
}
3243+
if (soap_action) {
3244+
function = find_function_using_soap_action(sdl, soap_action, function_name);
3245+
}
32073246
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);
3247+
if (!function) {
3248+
function = get_doc_function(sdl, NULL);
3249+
if (function) {
3250+
ZVAL_STRING(function_name, (char *)function->functionName);
3251+
} else {
3252+
soap_server_fault("Client", "looks like we got \"Body\" without function call", NULL, NULL, NULL);
3253+
}
32133254
}
32143255
} else {
32153256
if (*version == SOAP_1_1) {
@@ -3223,7 +3264,9 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c
32233264
soap_server_fault("DataEncodingUnknown","Unknown Data Encoding Style", NULL, NULL, NULL);
32243265
}
32253266
}
3226-
function = find_function(sdl, func, function_name);
3267+
if (!function) {
3268+
function = find_function(sdl, func, function_name);
3269+
}
32273270
if (sdl != NULL && function == NULL) {
32283271
if (*version == SOAP_1_2) {
32293272
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.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)