Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 82 additions & 35 deletions ext/soap/soap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -1341,42 +1342,44 @@ 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);

zend_is_auto_global(server);
if ((server_vars = zend_hash_find(&EG(symbol_table), server)) != NULL &&
Z_TYPE_P(server_vars) == IS_ARRAY &&
(encoding = zend_hash_str_find(Z_ARRVAL_P(server_vars), "HTTP_CONTENT_ENCODING", sizeof("HTTP_CONTENT_ENCODING")-1)) != NULL &&
Z_TYPE_P(encoding) == IS_STRING) {

if (zend_string_equals_literal(Z_STR_P(encoding), "gzip")
|| zend_string_equals_literal(Z_STR_P(encoding), "x-gzip")
|| zend_string_equals_literal(Z_STR_P(encoding), "deflate")
) {
zval filter_params;

array_init_size(&filter_params, 1);
add_assoc_long_ex(&filter_params, "window", sizeof("window")-1, 0x2f); /* ANY WBITS */

zf = php_stream_filter_create("zlib.inflate", &filter_params, 0);
zend_array_destroy(Z_ARR(filter_params));

if (zf) {
php_stream_filter_append(&SG(request_info).request_body->readfilters, zf);
if ((server_vars = zend_hash_find(&EG(symbol_table), server)) != NULL && Z_TYPE_P(server_vars) == IS_ARRAY) {
if ((encoding = zend_hash_str_find(Z_ARRVAL_P(server_vars), "HTTP_CONTENT_ENCODING", sizeof("HTTP_CONTENT_ENCODING")-1)) != NULL && Z_TYPE_P(encoding) == IS_STRING) {
if (zend_string_equals_literal(Z_STR_P(encoding), "gzip")
|| zend_string_equals_literal(Z_STR_P(encoding), "x-gzip")
|| zend_string_equals_literal(Z_STR_P(encoding), "deflate")
) {
zval filter_params;

array_init_size(&filter_params, 1);
add_assoc_long_ex(&filter_params, "window", sizeof("window")-1, 0x2f); /* ANY WBITS */

zf = php_stream_filter_create("zlib.inflate", &filter_params, 0);
zend_array_destroy(Z_ARR(filter_params));

if (zf) {
php_stream_filter_append(&SG(request_info).request_body->readfilters, zf);
} else {
php_error_docref(NULL, E_WARNING,"Can't uncompress compressed request");
SOAP_SERVER_END_CODE();
return;
}
} else {
php_error_docref(NULL, E_WARNING,"Can't uncompress compressed request");
php_error_docref(NULL, E_WARNING,"Request is compressed with unknown compression '%s'",Z_STRVAL_P(encoding));
SOAP_SERVER_END_CODE();
return;
}
} else {
php_error_docref(NULL, E_WARNING,"Request is compressed with unknown compression '%s'",Z_STRVAL_P(encoding));
SOAP_SERVER_END_CODE();
return;
}
}

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) {
Expand Down Expand Up @@ -1420,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);
Expand Down Expand Up @@ -3060,6 +3063,8 @@ static void deserialize_parameters(xmlNodePtr params, sdlFunctionPtr function, u
}
/* }}} */

/* This function attempts to find the right function name based on the first node's name in the soap body.
* If that doesn't work it falls back to get_doc_function(). */
static sdlFunctionPtr find_function(sdlPtr sdl, xmlNodePtr func, zval* function_name) /* {{{ */
{
sdlFunctionPtr function;
Expand Down Expand Up @@ -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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As stated by the TODO, this may require more work but depends on another fix.

* 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) {
Expand All @@ -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();

Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -4187,6 +4230,10 @@ static sdlFunctionPtr get_function(sdlPtr sdl, const char *function_name, size_t
}
/* }}} */

/* This function tries to find the function that matches the given parameters.
* If params is NULL, it will return the first function that has no parameters.
* If params is not NULL, it will return the first function that has the same
* parameters as the given XML node. */
static sdlFunctionPtr get_doc_function(sdlPtr sdl, xmlNodePtr params) /* {{{ */
{
if (sdl) {
Expand Down
44 changes: 44 additions & 0 deletions ext/soap/tests/bugs/bug49169.phpt
Original file line number Diff line number Diff line change
@@ -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>
49 changes: 49 additions & 0 deletions ext/soap/tests/bugs/bug49169.wsdl
Original file line number Diff line number Diff line change
@@ -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>
Loading