ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
SOAPClient.php
Go to the documentation of this file.
1<?php
2
3namespace SAML2;
4
9
17{
18 const START_SOAP_ENVELOPE = '<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"><soap-env:Header/><soap-env:Body>';
19 const END_SOAP_ENVELOPE = '</soap-env:Body></soap-env:Envelope>';
20
30 public function send(Message $msg, SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata = null)
31 {
32 $issuer = $msg->getIssuer();
33
34 $ctxOpts = array(
35 'ssl' => array(
36 'capture_peer_cert' => true,
37 'allow_self_signed' => true
38 ),
39 );
40
41 // Determine if we are going to do a MutualSSL connection between the IdP and SP - Shoaib
42 if ($srcMetadata->hasValue('saml.SOAPClient.certificate')) {
43 $cert = $srcMetadata->getValue('saml.SOAPClient.certificate');
44 if ($cert !== false) {
45 $ctxOpts['ssl']['local_cert'] = SimpleSAML_Utilities::resolveCert(
46 $srcMetadata->getString('saml.SOAPClient.certificate')
47 );
48 if ($srcMetadata->hasValue('saml.SOAPClient.privatekey_pass')) {
49 $ctxOpts['ssl']['passphrase'] = $srcMetadata->getString('saml.SOAPClient.privatekey_pass');
50 }
51 }
52 } else {
53 /* Use the SP certificate and privatekey if it is configured. */
54 $privateKey = SimpleSAML_Utilities::loadPrivateKey($srcMetadata);
55 $publicKey = SimpleSAML_Utilities::loadPublicKey($srcMetadata);
56 if ($privateKey !== null && $publicKey !== null && isset($publicKey['PEM'])) {
57 $keyCertData = $privateKey['PEM'] . $publicKey['PEM'];
58 $file = SimpleSAML_Utilities::getTempDir() . '/' . sha1($keyCertData) . '.pem';
59 if (!file_exists($file)) {
60 SimpleSAML_Utilities::writeFile($file, $keyCertData);
61 }
62 $ctxOpts['ssl']['local_cert'] = $file;
63 if (isset($privateKey['password'])) {
64 $ctxOpts['ssl']['passphrase'] = $privateKey['password'];
65 }
66 }
67 }
68
69 // do peer certificate verification
70 if ($dstMetadata !== null) {
71 $peerPublicKeys = $dstMetadata->getPublicKeys('signing', true);
72 $certData = '';
73 foreach ($peerPublicKeys as $key) {
74 if ($key['type'] !== 'X509Certificate') {
75 continue;
76 }
77 $certData .= "-----BEGIN CERTIFICATE-----\n" .
78 chunk_split($key['X509Certificate'], 64) .
79 "-----END CERTIFICATE-----\n";
80 }
81 $peerCertFile = SimpleSAML_Utilities::getTempDir() . '/' . sha1($certData) . '.pem';
82 if (!file_exists($peerCertFile)) {
83 SimpleSAML_Utilities::writeFile($peerCertFile, $certData);
84 }
85 // create ssl context
86 $ctxOpts['ssl']['verify_peer'] = true;
87 $ctxOpts['ssl']['verify_depth'] = 1;
88 $ctxOpts['ssl']['cafile'] = $peerCertFile;
89 }
90
91 if ($srcMetadata->hasValue('saml.SOAPClient.stream_context.ssl.peer_name')) {
92 $ctxOpts['ssl']['peer_name'] = $srcMetadata->getString('saml.SOAPClient.stream_context.ssl.peer_name');
93 }
94
95 $context = stream_context_create($ctxOpts);
96 if ($context === null) {
97 throw new \Exception('Unable to create SSL stream context');
98 }
99
100 $options = array(
101 'uri' => $issuer,
102 'location' => $msg->getDestination(),
103 'stream_context' => $context,
104 );
105
106 if ($srcMetadata->hasValue('saml.SOAPClient.proxyhost')) {
107 $options['proxy_host'] = $srcMetadata->getValue('saml.SOAPClient.proxyhost');
108 }
109
110 if ($srcMetadata->hasValue('saml.SOAPClient.proxyport')) {
111 $options['proxy_port'] = $srcMetadata->getValue('saml.SOAPClient.proxyport');
112 }
113
114 $x = new \SoapClient(null, $options);
115
116 // Add soap-envelopes
117 $request = $msg->toSignedXML();
118 $request = self::START_SOAP_ENVELOPE . $request->ownerDocument->saveXML($request) . self::END_SOAP_ENVELOPE;
119
120 Utils::getContainer()->debugMessage($request, 'out');
121
122 $action = 'http://www.oasis-open.org/committees/security';
123 $version = '1.1';
125
126 /* Perform SOAP Request over HTTP */
127 $soapresponsexml = $x->__doRequest($request, $destination, $action, $version);
128 if ($soapresponsexml === null || $soapresponsexml === "") {
129 throw new \Exception('Empty SOAP response, check peer certificate.');
130 }
131
132 Utils::getContainer()->debugMessage($soapresponsexml, 'in');
133
134 // Convert to SAML2\Message (\DOMElement)
135 try {
136 $dom = DOMDocumentFactory::fromString($soapresponsexml);
137 } catch (RuntimeException $e) {
138 throw new \Exception('Not a SOAP response.', 0, $e);
139 }
140
141 $soapfault = $this->getSOAPFault($dom);
142 if (isset($soapfault)) {
143 throw new \Exception($soapfault);
144 }
145 //Extract the message from the response
146 $samlresponse = Utils::xpQuery($dom->firstChild, '/soap-env:Envelope/soap-env:Body/*[1]');
147 $samlresponse = Message::fromXML($samlresponse[0]);
148
149 /* Add validator to message which uses the SSL context. */
150 self::addSSLValidator($samlresponse, $context);
151
152 Utils::getContainer()->getLogger()->debug("Valid ArtifactResponse received from IdP");
153
154 return $samlresponse;
155 }
156
163 private static function addSSLValidator(Message $msg, $context)
164 {
165 $options = stream_context_get_options($context);
166 if (!isset($options['ssl']['peer_certificate'])) {
167 return;
168 }
169
170 //$out = '';
171 //openssl_x509_export($options['ssl']['peer_certificate'], $out);
172
173 $key = openssl_pkey_get_public($options['ssl']['peer_certificate']);
174 if ($key === false) {
175 Utils::getContainer()->getLogger()->warning('Unable to get public key from peer certificate.');
176
177 return;
178 }
179
180 $keyInfo = openssl_pkey_get_details($key);
181 if ($keyInfo === false) {
182 Utils::getContainer()->getLogger()->warning('Unable to get key details from public key.');
183
184 return;
185 }
186
187 if (!isset($keyInfo['key'])) {
188 Utils::getContainer()->getLogger()->warning('Missing key in public key details.');
189
190 return;
191 }
192
193 $msg->addValidator(array('\SAML2\SOAPClient', 'validateSSL'), $keyInfo['key']);
194 }
195
203 public static function validateSSL($data, XMLSecurityKey $key)
204 {
205 assert(is_string($data));
206
207 $keyInfo = openssl_pkey_get_details($key->key);
208 if ($keyInfo === false) {
209 throw new \Exception('Unable to get key details from XMLSecurityKey.');
210 }
211
212 if (!isset($keyInfo['key'])) {
213 throw new \Exception('Missing key in public key details.');
214 }
215
216 if ($keyInfo['key'] !== $data) {
217 Utils::getContainer()->getLogger()->debug('Key on SSL connection did not match key we validated against.');
218
219 return;
220 }
221
222 Utils::getContainer()->getLogger()->debug('Message validated based on SSL certificate.');
223 }
224
225 /*
226 * Extracts the SOAP Fault from SOAP message
227 * @param $soapmessage Soap response needs to be type DOMDocument
228 * @return $soapfaultstring string|null
229 */
230 private function getSOAPFault($soapMessage)
231 {
232 $soapFault = Utils::xpQuery($soapMessage->firstChild, '/soap-env:Envelope/soap-env:Body/soap-env:Fault');
233
234 if (empty($soapFault)) {
235 /* No fault. */
236
237 return null;
238 }
239 $soapFaultElement = $soapFault[0];
240 // There is a fault element but we haven't found out what the fault string is
241 $soapFaultString = "Unknown fault string found";
242 // find out the fault string
243 $faultStringElement = Utils::xpQuery($soapFaultElement, './soap-env:faultstring') ;
244 if (!empty($faultStringElement)) {
245 return $faultStringElement[0]->textContent;
246 }
247
248 return $soapFaultString;
249 }
250}
catch(Exception $e) if(!($request instanceof \SAML2\ArtifactResolve)) $issuer
foreach($paths as $path) $request
Definition: asyncclient.php:32
$version
Definition: build.php:27
An exception for terminatinating execution or to throw for unit testing.
Base class for all SAML 2 messages.
Definition: Message.php:19
toSignedXML()
Convert this message to a signed XML document.
Definition: Message.php:481
getDestination()
Retrieve the destination of this message.
Definition: Message.php:323
addValidator($function, $data)
Add a method for validating this message.
Definition: Message.php:225
getIssuer()
Retrieve the issuer if this message.
Definition: Message.php:375
static validateSSL($data, XMLSecurityKey $key)
Validate a SOAP message against the certificate on the SSL connection.
Definition: SOAPClient.php:203
send(Message $msg, SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata=null)
This function sends the SOAP message to the service location and returns SOAP response.
Definition: SOAPClient.php:30
getSOAPFault($soapMessage)
Definition: SOAPClient.php:230
static addSSLValidator(Message $msg, $context)
Add a signature validator based on a SSL context.
Definition: SOAPClient.php:163
getString($name, $default=self::REQUIRED_OPTION)
This function retrieves a string configuration option.
getValue($name, $default=null)
Retrieve a configuration option set in config.php.
hasValue($name)
Check whether a key in the configuration exists or not.
static loadPrivateKey(SimpleSAML_Configuration $metadata, $required=false, $prefix='')
Definition: Utilities.php:475
static loadPublicKey(SimpleSAML_Configuration $metadata, $required=false, $prefix='')
Definition: Utilities.php:466
static resolveCert($path)
Definition: Utilities.php:457
static writeFile($filename, $data, $mode=0600)
Definition: Utilities.php:602
$x
Definition: complexTest.php:9
$action
$key
Definition: croninfo.php:18
$destination
$data
Definition: bench.php:6
$context
Definition: webdav.php:25