Skip to content

Commit a196632

Browse files
committed
1. commit, signed SOAP calls / WSS support
Modified files BAD_CAST & bailout Random id & tests +1 test +2 tests Newline +1 test Spaces -> tabs CRLF -> LF Added CHECK_LIB Missing declarations & sprintf -> slprintf (just in case) Fix test
1 parent 1977ef9 commit a196632

20 files changed

+1254
-6
lines changed

ext/libxml/php_libxml2.def

+2
Original file line numberDiff line numberDiff line change
@@ -1547,3 +1547,5 @@ xmlXPtrWrapLocationSet
15471547
xmlBufContent
15481548
xmlBufUse
15491549
xmlBufferDetach
1550+
xmlOutputBufferGetContent
1551+
xmlOutputBufferGetSize

ext/soap/config.m4

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ if test "$PHP_SOAP" != "no"; then
1111

1212
PHP_SETUP_LIBXML(SOAP_SHARED_LIBADD, [
1313
AC_DEFINE(HAVE_SOAP,1,[ ])
14-
PHP_NEW_EXTENSION(soap, soap.c php_encoding.c php_http.c php_packet_soap.c php_schema.c php_sdl.c php_xml.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
14+
PHP_NEW_EXTENSION(soap, soap.c php_encoding.c php_http.c php_packet_soap.c php_schema.c php_sdl.c php_wss.c php_xml.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
1515
PHP_SUBST(SOAP_SHARED_LIBADD)
1616
])
1717
fi

ext/soap/config.w32

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ if (PHP_SOAP != "no") {
88
CHECK_HEADER_ADD_INCLUDE("libxml/parser.h", "CFLAGS_SOAP", PHP_PHP_BUILD + "\\include\\libxml2") &&
99
CHECK_HEADER_ADD_INCLUDE("libxml/tree.h", "CFLAGS_SOAP", PHP_PHP_BUILD + "\\include\\libxml2")
1010
) {
11-
EXTENSION('soap', 'soap.c php_encoding.c php_http.c php_packet_soap.c php_schema.c php_sdl.c php_xml.c', null, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
11+
EXTENSION('soap', 'soap.c php_encoding.c php_http.c php_packet_soap.c php_schema.c php_sdl.c php_wss.c php_xml.c', null, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
1212
AC_DEFINE('HAVE_PHP_SOAP', 1, "SOAP support");
1313

1414
if (!PHP_SOAP_SHARED) {

ext/soap/php_soap.h

+9
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ typedef struct _soapService soapService, *soapServicePtr;
6767
#include "php_schema.h"
6868
#include "php_http.h"
6969
#include "php_packet_soap.h"
70+
#include "php_wss.h"
7071

7172
struct _soapMapping {
7273
zval to_xml;
@@ -252,5 +253,13 @@ static zend_always_inline zval *php_soap_deref(zval *zv) {
252253
#define Z_CLIENT_LAST_RESPONSE_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 33))
253254
#define Z_CLIENT_LAST_REQUEST_HEADERS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 34))
254255
#define Z_CLIENT_LAST_RESPONSE_HEADERS_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 35))
256+
#define Z_CLIENT_WSS_SET_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 36))
257+
#define Z_CLIENT_WSS_SIGNFUNC_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 37))
258+
#define Z_CLIENT_WSS_X509_BINSECTOKEN_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 38))
259+
#define Z_CLIENT_WSS_ADD_TIMESTAMP_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 39))
260+
#define Z_CLIENT_WSS_TIMESTAMP_EXPIRES_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 40))
261+
#define Z_CLIENT_WSS_DIGEST_METHOD_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 41))
262+
#define Z_CLIENT_WSS_VERSION_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 42))
263+
#define Z_CLIENT_WSS_RANDOM_ID_P(zv) php_soap_deref(OBJ_PROP_NUM(Z_OBJ_P(zv), 43))
255264

256265
#endif

ext/soap/php_wss.c

+279
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
#include "php_soap.h"
2+
#include "ext/date/php_date.h"
3+
#include "ext/hash/php_hash.h"
4+
#include "ext/standard/base64.h"
5+
#include "ext/standard/php_mt_rand.h"
6+
#include <libxml/c14n.h>
7+
#include <libxml/xpathInternals.h>
8+
9+
static zend_string *get_exc_c14n_from_node_ptr(xmlNodePtr node) /* {{{ */
10+
{
11+
zend_string *result = NULL;
12+
13+
xmlOutputBufferPtr buffer = xmlAllocOutputBuffer(NULL);
14+
if (buffer != NULL) {
15+
xmlXPathContextPtr xpath_ctx = xmlXPathNewContext(node->doc);
16+
if(xpath_ctx != NULL) {
17+
xpath_ctx->node = node;
18+
xmlXPathObjectPtr xpath_obj = xmlXPathEvalExpression(BAD_CAST("(.//. | .//@* | .//namespace::*)"), xpath_ctx);
19+
xpath_ctx->node = NULL;
20+
21+
if (xpath_obj != NULL && xpath_obj->type == XPATH_NODESET) {
22+
int c14n_ret = xmlC14NDocSaveTo(node->doc, xpath_obj->nodesetval, XML_C14N_EXCLUSIVE_1_0, NULL, 0, buffer);
23+
if (c14n_ret >= 0) {
24+
#ifdef LIBXML2_NEW_BUFFER
25+
int buffer_size = xmlOutputBufferGetSize(buffer);
26+
#else
27+
int buffer_size = buffer->buffer->use;
28+
#endif
29+
if (buffer_size > 0) {
30+
#ifdef LIBXML2_NEW_BUFFER
31+
char *buffer_content = (char *)xmlOutputBufferGetContent(buffer);
32+
#else
33+
char *buffer_content = (char *)buffer->buffer->content;
34+
#endif
35+
result = zend_string_init(buffer_content, strlen(buffer_content), 0);
36+
}
37+
}
38+
}
39+
40+
xmlXPathFreeObject(xpath_obj);
41+
xmlXPathFreeContext(xpath_ctx);
42+
}
43+
xmlOutputBufferClose(buffer);
44+
}
45+
46+
return result;
47+
}
48+
/* }}} */
49+
50+
static int add_reference_to_signed_info(zval *this_ptr, xmlNodePtr signed_info, xmlNodePtr source, zend_string *source_uri, xmlNsPtr ns_ds) /* {{{ */
51+
{
52+
xmlNodePtr reference = xmlNewChild(signed_info, ns_ds, BAD_CAST("Reference"), NULL);
53+
xmlSetProp(reference, BAD_CAST("URI"), BAD_CAST(ZSTR_VAL(source_uri)));
54+
xmlNodePtr reference_trsfrms = xmlNewChild(reference, ns_ds, BAD_CAST("Transforms"), NULL);
55+
xmlNodePtr reference_trsfrm = xmlNewChild(reference_trsfrms, ns_ds, BAD_CAST("Transform"), NULL);
56+
xmlSetProp(reference_trsfrm, BAD_CAST("Algorithm"), BAD_CAST(SOAP_WSS_EXC_C14N));
57+
xmlNodePtr reference_dm = xmlNewChild(reference, ns_ds, BAD_CAST("DigestMethod"), NULL);
58+
59+
const php_hash_ops *ops = NULL;
60+
zend_string *algo = NULL;
61+
zend_long digest_method = Z_LVAL_P(Z_CLIENT_WSS_DIGEST_METHOD_P(this_ptr));
62+
switch (digest_method) {
63+
case SOAP_WSS_DIGEST_METHOD_SHA1:
64+
algo = zend_string_init("sha1", strlen("sha1"), 0);
65+
ops = php_hash_fetch_ops(algo);
66+
xmlSetProp(reference_dm, BAD_CAST("Algorithm"), BAD_CAST(SOAP_WSS_SHA1));
67+
break;
68+
case SOAP_WSS_DIGEST_METHOD_SHA256:
69+
algo = zend_string_init("sha256", strlen("sha256"), 0);
70+
ops = php_hash_fetch_ops(algo);
71+
xmlSetProp(reference_dm, BAD_CAST("Algorithm"), BAD_CAST(SOAP_WSS_SHA256));
72+
break;
73+
case SOAP_WSS_DIGEST_METHOD_SHA512:
74+
algo = zend_string_init("sha512", strlen("sha512"), 0);
75+
ops = php_hash_fetch_ops(algo);
76+
xmlSetProp(reference_dm, BAD_CAST("Algorithm"), BAD_CAST(SOAP_WSS_SHA512));
77+
break;
78+
}
79+
80+
if (ops == NULL) {
81+
if (algo != NULL) {
82+
php_error_docref(NULL, E_WARNING, "invalid hashing algorithm, %s", ZSTR_VAL(algo));
83+
zend_string_release_ex(algo, 0);
84+
} else {
85+
php_error_docref(NULL, E_WARNING, "unknown hashing algorithm");
86+
}
87+
return 0;
88+
}
89+
90+
zend_string_release_ex(algo, 0);
91+
92+
zend_string *exc_c14n = get_exc_c14n_from_node_ptr(source);
93+
if (exc_c14n != NULL) {
94+
void *context = php_hash_alloc_context(ops);
95+
ops->hash_init(context, NULL);
96+
ops->hash_update(context, (unsigned char *)ZSTR_VAL(exc_c14n), ZSTR_LEN(exc_c14n));
97+
98+
zend_string *digest = zend_string_alloc(ops->digest_size, 0);
99+
ops->hash_final((unsigned char *)ZSTR_VAL(digest), context);
100+
efree(context);
101+
102+
ZSTR_VAL(digest)[ops->digest_size] = 0;
103+
104+
zend_string *digest_base64 = php_base64_encode((unsigned char *)ZSTR_VAL(digest), ZSTR_LEN(digest));
105+
xmlNewChild(reference, ns_ds, BAD_CAST("DigestValue"), BAD_CAST(ZSTR_VAL(digest_base64)));
106+
107+
zend_string_release(digest_base64);
108+
zend_string_release(digest);
109+
zend_string_release_ex(exc_c14n, 0);
110+
111+
return 1;
112+
} else {
113+
php_error_docref(NULL, E_WARNING, "exclusive C14N failed, URI \"%s\"", ZSTR_VAL(source_uri));
114+
return 0;
115+
}
116+
}
117+
/* }}} */
118+
119+
void add_wss_to_function_call(zval *this_ptr, int soap_version, xmlNodePtr envelope, xmlNodePtr head, xmlNodePtr body) /* {{{ */
120+
{
121+
char random_s[10];
122+
if (Z_TYPE_P(Z_CLIENT_WSS_RANDOM_ID_P(this_ptr)) == IS_TRUE) {
123+
slprintf(random_s, sizeof(random_s), "%d", (int)php_mt_rand_common(100000000,999999999));
124+
} else {
125+
slprintf(random_s, sizeof(random_s), "%d", 1);
126+
}
127+
128+
zend_string *body_id = zend_string_concat2(
129+
"BodyID-", strlen("BodyID-"),
130+
random_s, strlen(random_s));
131+
zend_string *body_id_uri = zend_string_concat2(
132+
"#", strlen("#"),
133+
ZSTR_VAL(body_id), ZSTR_LEN(body_id));
134+
135+
xmlNsPtr ns_wsse = NULL;
136+
if (Z_LVAL_P(Z_CLIENT_WSS_VERSION_P(this_ptr)) == SOAP_WSS_VERSION_1_1) {
137+
ns_wsse = xmlNewNs(envelope, BAD_CAST(SOAP_WSS_SECEXT_1_1), BAD_CAST("wsse11"));
138+
} else {
139+
ns_wsse = xmlNewNs(envelope, BAD_CAST(SOAP_WSS_SECEXT_1_0), BAD_CAST("wsse"));
140+
}
141+
142+
xmlNsPtr ns_ds = xmlNewNs(envelope, BAD_CAST(SOAP_WSS_XMLDSIG), BAD_CAST("ds"));
143+
xmlNsPtr ns_wsu = xmlNewNs(envelope, BAD_CAST(SOAP_WSS_UTILITY), BAD_CAST("wsu"));
144+
xmlNodePtr security = xmlNewChild(head, ns_wsse, BAD_CAST("Security"), NULL);
145+
xmlSetNsProp(body, ns_wsu, BAD_CAST("Id"), BAD_CAST(ZSTR_VAL(body_id)));
146+
147+
if (soap_version == SOAP_1_1) {
148+
xmlSetProp(security, BAD_CAST(SOAP_1_1_ENV_NS_PREFIX":mustUnderstand"), BAD_CAST("1"));
149+
} else {
150+
xmlSetProp(security, BAD_CAST(SOAP_1_2_ENV_NS_PREFIX":mustUnderstand"), BAD_CAST("true"));
151+
}
152+
153+
xmlNodePtr timestamp = NULL;
154+
zend_string *timestamp_id = NULL, *timestamp_id_uri = NULL;
155+
if (Z_TYPE_P(Z_CLIENT_WSS_ADD_TIMESTAMP_P(this_ptr)) == IS_TRUE) {
156+
char *time_format = "Y-m-d\\TH:i:s\\Z";
157+
zend_long now = (zend_long)php_time();
158+
zend_long expires = Z_LVAL_P(Z_CLIENT_WSS_TIMESTAMP_EXPIRES_P(this_ptr));
159+
zend_string *timestamp_s = php_format_date(time_format, strlen(time_format), now, 0);
160+
zend_string *expires_s = php_format_date(time_format, strlen(time_format), (now + expires), 0);
161+
162+
timestamp_id = zend_string_concat2(
163+
"TimestampID-", strlen("TimestampID-"),
164+
random_s, strlen(random_s));
165+
timestamp_id_uri = zend_string_concat2(
166+
"#", strlen("#"),
167+
ZSTR_VAL(timestamp_id), ZSTR_LEN(timestamp_id));
168+
169+
timestamp = xmlNewChild(security, ns_wsu, BAD_CAST("Timestamp"), NULL);
170+
xmlSetNsProp(timestamp, ns_wsu, BAD_CAST("Id"), BAD_CAST(ZSTR_VAL(timestamp_id)));
171+
xmlNewChild(timestamp, ns_wsu, BAD_CAST("Created"), BAD_CAST(ZSTR_VAL(timestamp_s)));
172+
xmlNewChild(timestamp, ns_wsu, BAD_CAST("Expires"), BAD_CAST(ZSTR_VAL(expires_s)));
173+
174+
zend_string_release(expires_s);
175+
zend_string_release(timestamp_s);
176+
}
177+
178+
int references = 0;
179+
xmlNodePtr signed_info, signature_value = NULL;
180+
if (Z_TYPE_P(Z_CLIENT_WSS_SIGNFUNC_P(this_ptr)) != IS_NULL) {
181+
zend_string *token_id = NULL, *token_id_uri = NULL;
182+
if (Z_TYPE_P(Z_CLIENT_WSS_X509_BINSECTOKEN_P(this_ptr)) == IS_STRING) {
183+
token_id = zend_string_concat2(
184+
"TokenID-", strlen("TokenID-"),
185+
random_s, strlen(random_s));
186+
token_id_uri = zend_string_concat2(
187+
"#", strlen("#"),
188+
ZSTR_VAL(token_id), ZSTR_LEN(token_id));
189+
190+
zend_string *token_base64 = php_base64_encode((unsigned char *)Z_STRVAL_P(Z_CLIENT_WSS_X509_BINSECTOKEN_P(this_ptr)), Z_STRLEN_P(Z_CLIENT_WSS_X509_BINSECTOKEN_P(this_ptr)));
191+
192+
xmlNodePtr x509_binsectoken = xmlNewChild(security, ns_wsse, BAD_CAST("BinarySecurityToken"), BAD_CAST(ZSTR_VAL(token_base64)));
193+
xmlSetProp(x509_binsectoken, BAD_CAST("EncodingType"), BAD_CAST(SOAP_WSS_BASE64_BINARY));
194+
xmlSetProp(x509_binsectoken, BAD_CAST("ValueType"), BAD_CAST(SOAP_WSS_X509v3_TOKEN_PROFILE));
195+
xmlSetNsProp(x509_binsectoken, ns_wsu, BAD_CAST("Id"), BAD_CAST(ZSTR_VAL(token_id)));
196+
197+
zend_string_release(token_base64);
198+
}
199+
200+
xmlNodePtr signature = xmlNewChild(security, ns_ds, BAD_CAST("Signature"), NULL);
201+
signed_info = xmlNewChild(signature, ns_ds, BAD_CAST("SignedInfo"), NULL);
202+
xmlNodePtr canon_method = xmlNewChild(signed_info, ns_ds, BAD_CAST("CanonicalizationMethod"), NULL);
203+
xmlNodePtr sign_method = xmlNewChild(signed_info, ns_ds, BAD_CAST("SignatureMethod"), NULL);
204+
xmlSetProp(canon_method, BAD_CAST("Algorithm"), BAD_CAST(SOAP_WSS_EXC_C14N));
205+
xmlSetProp(sign_method, BAD_CAST("Algorithm"), BAD_CAST(SOAP_WSS_RSA_SHA1));
206+
207+
if (timestamp != NULL) {
208+
references += add_reference_to_signed_info(this_ptr, signed_info, timestamp, timestamp_id_uri, ns_ds);
209+
}
210+
references += add_reference_to_signed_info(this_ptr, signed_info, body, body_id_uri, ns_ds);
211+
212+
signature_value = xmlNewChild(signature, ns_ds, BAD_CAST("SignatureValue"), NULL);
213+
214+
if (token_id != NULL) {
215+
xmlNodePtr keyinfo = xmlNewChild(signature, ns_ds, BAD_CAST("KeyInfo"), NULL);
216+
xmlNodePtr keyinfo_sectoken_reference = xmlNewChild(keyinfo, ns_wsse, BAD_CAST("SecurityTokenReference"), NULL);
217+
xmlNodePtr keyinfo_sectoken_reference_ref = xmlNewChild(keyinfo_sectoken_reference, ns_wsse, BAD_CAST("Reference"), NULL);
218+
xmlSetProp(keyinfo_sectoken_reference_ref, BAD_CAST("URI"), BAD_CAST(ZSTR_VAL(token_id_uri)));
219+
xmlSetProp(keyinfo_sectoken_reference_ref, BAD_CAST("ValueType"), BAD_CAST(SOAP_WSS_X509v3_TOKEN_PROFILE));
220+
zend_string_release_ex(token_id_uri, 0);
221+
zend_string_release_ex(token_id, 0);
222+
}
223+
}
224+
225+
if (timestamp_id != NULL) {
226+
zend_string_release_ex(timestamp_id_uri, 0);
227+
zend_string_release_ex(timestamp_id, 0);
228+
}
229+
230+
bool bailout = 0;
231+
if (references != 0) {
232+
zend_string *signed_info_exc_c14n = get_exc_c14n_from_node_ptr(signed_info);
233+
if (signed_info_exc_c14n != NULL) {
234+
zend_fcall_info signature_fci;
235+
zend_fcall_info_cache signature_fcc;
236+
if (zend_fcall_info_init(Z_CLIENT_WSS_SIGNFUNC_P(this_ptr), 0, &signature_fci, &signature_fcc, NULL, NULL) == SUCCESS) {
237+
zval signature_params, signature_retval;
238+
239+
ZVAL_STR(&signature_params, signed_info_exc_c14n);
240+
241+
signature_fci.param_count = 1;
242+
signature_fci.params = &signature_params;
243+
signature_fci.retval = &signature_retval;
244+
245+
if (zend_call_function(&signature_fci, &signature_fcc) == SUCCESS) {
246+
if (Z_TYPE(signature_retval) == IS_STRING) {
247+
zend_string *signature_base64 = php_base64_encode((unsigned char *)Z_STRVAL(signature_retval), Z_STRLEN(signature_retval));
248+
xmlNodeSetContent(signature_value, BAD_CAST(ZSTR_VAL(signature_base64)));
249+
zend_string_release(signature_base64);
250+
} else {
251+
if (!EG(exception)) {
252+
php_error_docref(NULL, E_WARNING, "\"signfunc\" should return a string, %s given", zend_zval_type_name(&signature_retval));
253+
} else {
254+
bailout = 1;
255+
}
256+
}
257+
} else {
258+
php_error_docref(NULL, E_WARNING, "unable to run \"signfunc\"");
259+
}
260+
261+
zval_ptr_dtor_str(&signature_params);
262+
zval_ptr_dtor(&signature_retval);
263+
} else {
264+
php_error_docref(NULL, E_WARNING, "unable to initialize \"signfunc\"");
265+
zend_string_release_ex(signed_info_exc_c14n, 0);
266+
}
267+
} else {
268+
php_error_docref(NULL, E_WARNING, "exclusive C14N failed, element \"SignedInfo\"");
269+
}
270+
}
271+
272+
zend_string_release_ex(body_id_uri, 0);
273+
zend_string_release_ex(body_id, 0);
274+
275+
if (bailout) {
276+
zend_bailout();
277+
}
278+
}
279+
/* }}} */

ext/soap/php_wss.h

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#ifndef PHP_WSS_H
2+
#define PHP_WSS_H
3+
4+
#define SOAP_WSS_DIGEST_METHOD_SHA1 1
5+
#define SOAP_WSS_DIGEST_METHOD_SHA256 2
6+
#define SOAP_WSS_DIGEST_METHOD_SHA512 3
7+
8+
#define SOAP_WSS_VERSION_1_0 1
9+
#define SOAP_WSS_VERSION_1_1 2
10+
11+
#define SOAP_WSS_TIMESTAMP_EXPIRES 300
12+
13+
#define SOAP_WSS_BASE64_BINARY "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
14+
#define SOAP_WSS_EXC_C14N "http://www.w3.org/2001/10/xml-exc-c14n#"
15+
#define SOAP_WSS_RSA_SHA1 "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
16+
#define SOAP_WSS_SHA1 "http://www.w3.org/2000/09/xmldsig#sha1"
17+
#define SOAP_WSS_SHA256 "http://www.w3.org/2001/04/xmlenc#sha256"
18+
#define SOAP_WSS_SHA512 "http://www.w3.org/2001/04/xmlenc#sha512"
19+
#define SOAP_WSS_SECEXT_1_0 "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
20+
#define SOAP_WSS_SECEXT_1_1 "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"
21+
#define SOAP_WSS_UTILITY "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
22+
#define SOAP_WSS_X509v3_TOKEN_PROFILE "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
23+
#define SOAP_WSS_XMLDSIG "http://www.w3.org/2000/09/xmldsig#"
24+
25+
void add_wss_to_function_call(zval *this_ptr, int soap_version, xmlNodePtr envelope, xmlNodePtr head, xmlNodePtr body);
26+
27+
#endif

0 commit comments

Comments
 (0)