ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
SOAPClient.php
Go to the documentation of this file.
1 <?php
2 
3 namespace 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';
124  $destination = $msg->getDestination();
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 }
static resolveCert($path)
Definition: Utilities.php:457
$context
Definition: webdav.php:25
$action
foreach($paths as $path) $request
Definition: asyncclient.php:32
hasValue($name)
Check whether a key in the configuration exists or not.
$destination
getIssuer()
Retrieve the issuer if this message.
Definition: Message.php:375
toSignedXML()
Convert this message to a signed XML document.
Definition: Message.php:481
getValue($name, $default=null)
Retrieve a configuration option set in config.php.
static writeFile($filename, $data, $mode=0600)
Definition: Utilities.php:602
static loadPublicKey(SimpleSAML_Configuration $metadata, $required=false, $prefix='')
Definition: Utilities.php:466
Base class for all SAML 2 messages.
Definition: Message.php:18
$version
Definition: build.php:27
static addSSLValidator(Message $msg, $context)
Add a signature validator based on a SSL context.
Definition: SOAPClient.php:163
catch(Exception $e) if(!($request instanceof \SAML2\ArtifactResolve)) $issuer
getString($name, $default=self::REQUIRED_OPTION)
This function retrieves a string configuration option.
static validateSSL($data, XMLSecurityKey $key)
Validate a SOAP message against the certificate on the SSL connection.
Definition: SOAPClient.php:203
getDestination()
Retrieve the destination of this message.
Definition: Message.php:323
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
static loadPrivateKey(SimpleSAML_Configuration $metadata, $required=false, $prefix='')
Definition: Utilities.php:475
$key
Definition: croninfo.php:18
$x
Definition: complexTest.php:9
getSOAPFault($soapMessage)
Definition: SOAPClient.php:230
addValidator($function, $data)
Add a method for validating this message.
Definition: Message.php:225
$data
Definition: bench.php:6