diff --git a/lib/SAML2/ArtifactResolve.php b/lib/SAML2/ArtifactResolve.php
index c7de093ba6e77e4c28001e1bdb653ac4733d01cb..b0e100649cc6836733e8cb4160a4cd8f0f7e4a02 100644
--- a/lib/SAML2/ArtifactResolve.php
+++ b/lib/SAML2/ArtifactResolve.php
@@ -42,7 +42,7 @@ class SAML2_ArtifactResolve extends SAML2_Request {
* @param String The $artifact.
*/
public function setArtifact($artifact) {
-
+ assert('is_string($artifact)');
$this->artifact = $artifact;
}
@@ -53,7 +53,10 @@ class SAML2_ArtifactResolve extends SAML2_Request {
*/
public function toUnsignedXML() {
- throw new Exception('Not SUPPORTED');
+ $root = parent::toUnsignedXML();
+ $artifactelement = $this->document->createElementNS(SAML2_Const::NS_SAMLP, 'Artifact', $this->artifact);
+ $root->appendChild($artifactelement);
+ return $root;
}
diff --git a/lib/SAML2/HTTPArtifact.php b/lib/SAML2/HTTPArtifact.php
index 0251a69ea4f17115406a03fca69b947a34f0e1de..962a1633bef2df3b8a9769e2fd8bff26f62c3ece 100644
--- a/lib/SAML2/HTTPArtifact.php
+++ b/lib/SAML2/HTTPArtifact.php
@@ -10,6 +10,8 @@
*/
class SAML2_HTTPArtifact extends SAML2_Binding {
+ private $spMetadata;
+
/**
* Create the redirect URL for a message.
*
@@ -50,7 +52,7 @@ class SAML2_HTTPArtifact extends SAML2_Binding {
/**
- * Receive a SAMLart.
+ * Receive a SAML 2 message sent using the HTTP-Artifact binding.
*
* Throws an exception if it is unable receive the message.
*
@@ -58,7 +60,81 @@ class SAML2_HTTPArtifact extends SAML2_Binding {
*/
public function receive() {
- throw new Exception('Receiving SAML2 Artifact messages not supported.');
+ if (array_key_exists('SAMLart', $_REQUEST)) {
+ $artifact = base64_decode($_REQUEST['SAMLart']);
+ $endpointIndex = bin2hex(substr($artifact,2,2));
+ $sourceId = bin2hex(substr($artifact,4,20));
+
+ }else{
+ throw new Execption('Missing SAMLArt parameter.');
+ }
+
+ $metadataHandler = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+
+ $idpmetadata = $metadataHandler->getMetaDataConfigForSha1($sourceId, 'saml20-idp-remote');
+
+
+ $endpoint = NULL;
+ foreach ($idpmetadata->getEndpoints('ArtifactResolutionService') as $ep) {
+ if ($ep['index'] === hexdec($endpointIndex)) {
+ $endpoint = $ep;
+ break;
+ }
+ }
+
+ if ($endpoint === NULL) {
+ throw new Exception('No ArtifactResolutionService with the correct index.');
+ }
+
+ SimpleSAML_Logger::debug("ArtifactResolutionService endpoint being used is := " . $endpoint['Location']);
+
+ //Construct the ArtifactResolve Request
+ $ar = new SAML2_ArtifactResolve();
+
+ /* Set the request attributes */
+
+ $ar->setIssuer($this->spMetadata->getString('entityid'));
+ $ar->setArtifact($_REQUEST['SAMLart']);
+ $ar->setDestination($endpoint['Location']);
+
+ /* Sign the request */
+ sspmod_saml2_Message::addSign($this->spMetadata, $idpmetadata, $ar); // Shoaib - moved from the SOAPClient.
+
+ $soap = new SAML2_SOAPClient();
+
+ // Send message through SoapClient
+ $artifactResponse = $soap->send($ar, $this->spMetadata);
+
+ if (!$artifactResponse->isSuccess()) {
+ throw new Exception('Received error from ArtifactResolutionService.');
+ }
+
+ $xml = $artifactResponse->getAny();
+ $samlresponse = SAML2_Message::fromXML($xml);
+ $samlresponse->addValidator(array(get_class($this), 'validateSignature'), $artifactResponse);
+
+
+ if (isset($_REQUEST['RelayState'])) {
+ $samlresponse->setRelayState($_REQUEST['RelayState']);
+ }
+
+ return $samlresponse;
+ }
+
+
+ public function setSPMetadata($sp){
+ $this->spMetadata = $sp;
+ }
+
+
+ /**
+ * A validator which returns TRUE if the ArtifactResponse was signed with the given key
+ *
+ * @return TRUE
+ */
+ public static function validateSignature(SAML2_ArtifactResponse $message, XMLSecurityKey $key) {
+
+ return $message->validate($key);
}
}
diff --git a/lib/SAML2/SOAPClient.php b/lib/SAML2/SOAPClient.php
new file mode 100644
index 0000000000000000000000000000000000000000..2c272f176e09bd3b40fe0e8d490ef98a184088be
--- /dev/null
+++ b/lib/SAML2/SOAPClient.php
@@ -0,0 +1,113 @@
+<?php
+/**
+ * Implementation of the SAML 2.0 SOAP binding.
+ *
+ * @author Shoaib Ali
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class SAML2_SOAPClient {
+
+ const START_SOAP_ENVELOPE = '<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"><soap-env:Header/><soap-env:Body>';
+ const END_SOAP_ENVELOPE = '</soap-env:Body></soap-env:Envelope>';
+
+ /**
+ * This function sends the SOAP message to the service location and returns SOAP response
+ *
+ * @param $ar SAML2_ArtifactResolve object.
+ * @return $soapresponse string
+ */
+ public function send(SAML2_ArtifactResolve $ar, SimpleSAML_Configuration $spMetadata) {
+
+ $issuer = $ar->getIssuer();
+
+ $options = array(
+ 'uri' => $issuer,
+ 'location' => $ar->getDestination(),
+ );
+
+ // Determine if we are going to do a MutualSSL connection between the IdP and SP - Shoaib
+ if ($spMetadata->hasValue('saml.SOAPClient.certificate')) {
+ $options['local_cert'] = SimpleSAML_Utilities::resolveCert($spMetadata->getString('saml.SOAPClient.certificate'));
+ if ($spMetadata->hasValue('saml.SOAPClient.privatekey_pass')) {
+ $options['passphrase'] = $spMetadata->getString('saml.SOAPClient.privatekey_pass');
+ }
+ } else {
+ /* Use the SP certificate and privatekey if it is configured. */
+ $privateKey = SimpleSAML_Utilities::loadPrivateKey($spMetadata);
+ $publicKey = SimpleSAML_Utilities::loadPublicKey($spMetadata);
+ if ($privateKey !== NULL && $publicKey !== NULL && isset($publicKey['PEM'])) {
+ $keyCertData = $privateKey['PEM'] . $publicKey['PEM'];
+ $file = SimpleSAML_Utilities::getTempDir() . '/' . sha1($keyCertData) . '.pem';
+ if (!file_exists($file)) {
+ SimpleSAML_Utilities::writeFile($file, $keyCertData);
+ }
+ $options['local_cert'] = $file;
+ if (isset($privateKey['password'])) {
+ $options['passphrase'] = $privateKey['password'];
+ }
+ }
+ }
+
+ $x = new SoapClient(NULL, $options);
+
+ // Add soap-envelopes
+ $request = $ar->toSignedXML();
+ $request = self::START_SOAP_ENVELOPE . $request->ownerDocument->saveXML($request) . self::END_SOAP_ENVELOPE;
+
+ $action = 'http://www.oasis-open.org/committees/security';
+ $version = '1.1';
+ $destination = $ar->getDestination();
+
+
+ /* Perform SOAP Request over HTTP */
+ $soapresponsexml = $x->__doRequest($request, $destination, $action, $version);
+
+
+ // Convert to SAML2_Message (DOMElement)
+ $dom = new DOMDocument();
+ if (!$dom->loadXML($soapresponsexml)) {
+ throw new Exception('Not a SOAP response.');
+ }
+
+ $soapfault = $this->getSOAPFault($dom);
+ if (isset($soapfault)) {
+ throw new Exception($soapfault);
+ }
+ //Extract the message from the response
+ $xml = $dom->firstChild; /* Soap Envelope */
+ $samlresponse = SAML2_Utils::xpQuery($dom->firstChild, '/soap-env:Envelope/soap-env:Body/*[1]');
+ $samlresponse = SAML2_Message::fromXML($samlresponse[0]);
+
+
+ simpleSAML_Logger::debug("Valid ArtifactResponse received from IdP");
+
+ return $samlresponse;
+
+ }
+
+
+ /*
+ * Extracts the SOAP Fault from SOAP message
+ * @param $soapmessage Soap response needs to be type DOMDocument
+ * @return $soapfaultstring string|NULL
+ */
+ private function getSOAPFault($soapmessage) {
+
+ $soapfault = SAML2_Utils::xpQuery($soapmessage->firstChild, '/soap-env:Envelope/soap-env:Body/soap-env:Fault');
+
+ if (empty($soapfault)) {
+ /* No fault. */
+ return NULL;
+ }
+ $soapfaultelement = $soapfault[0];
+ $soapfaultstring = "Unknown fault string found"; // There is a fault element but we havn't found out what the fault string is
+ // find out the fault string
+ $faultstringelement = SAML2_Utils::xpQuery($soapfaultelement, './soap-env:faultstring') ;
+ if (!empty($faultstringelement)) {
+ return $faultstringelement[0]->textContent;
+ }
+ return $soapfaultstring;
+ }
+
+}
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
index 7494bf5a238cdbc058fc5d313bce18a1a84add80..91c999cd0fa08f2159466500db9b5565bc75774c 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
@@ -307,6 +307,34 @@ class SimpleSAML_Metadata_MetaDataStorageHandler {
return SimpleSAML_Configuration::loadFromArray($metadata, $set . '/' . var_export($entityId, TRUE));
}
+ public function getMetaDataConfigForSha1($sha1, $set) {
+ assert('is_string($sha1)');
+ assert('is_string($set)');
+
+
+ $result = array();
+
+ foreach($this->sources as $source) {
+ $srcList = $source->getMetadataSet($set);
+
+
+ /* $result is the last argument to array_merge because we want the content already
+ * in $result to have precedence.
+ */
+ $result = array_merge($srcList, $result);
+ }
+ foreach($result as $remote_provider ){
+
+ if(sha1($remote_provider['entityid'])==$sha1){
+ $remote_provider['metadata-set'] = $set;
+
+ return SimpleSAML_Configuration::loadFromArray($remote_provider, $set . '/' . var_export($remote_provider['entityid'], TRUE));
+ }
+ }
+
+ return null;
+ }
+
}
?>
\ No newline at end of file
diff --git a/modules/saml/lib/Auth/Source/SP.php b/modules/saml/lib/Auth/Source/SP.php
index 7224b6130de87c22e15e7bb238149f5c5b3f7067..f869414eac3fd3b62d5dde76fd5de85e51fff878 100644
--- a/modules/saml/lib/Auth/Source/SP.php
+++ b/modules/saml/lib/Auth/Source/SP.php
@@ -183,7 +183,6 @@ class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source {
$ar = sspmod_saml2_Message::buildAuthnRequest($this->metadata, $idpMetadata);
$ar->setAssertionConsumerServiceURL(SimpleSAML_Module::getModuleURL('saml/sp/saml2-acs.php/' . $this->authId));
- $ar->setProtocolBinding(SAML2_Const::BINDING_HTTP_POST);
if (isset($state['SimpleSAML_Auth_Default.ReturnURL'])) {
$ar->setRelayState($state['SimpleSAML_Auth_Default.ReturnURL']);
diff --git a/modules/saml/www/sp/saml2-acs.php b/modules/saml/www/sp/saml2-acs.php
index f42e83ba61eb41807fabe6f777655b749b912a74..4c7160169f95b5f4f3d6c03efecdb699aadbe5ef 100644
--- a/modules/saml/www/sp/saml2-acs.php
+++ b/modules/saml/www/sp/saml2-acs.php
@@ -6,8 +6,13 @@
$sourceId = substr($_SERVER['PATH_INFO'], 1);
$source = SimpleSAML_Auth_Source::getById($sourceId, 'sspmod_saml_Auth_Source_SP');
+$spMetadata = $source->getMetadata();
$b = SAML2_Binding::getCurrentBinding();
+if ($b instanceof SAML2_HTTPArtifact) {
+ $b->setSPMetadata($spMetadata);
+}
+
$response = $b->receive();
if (!($response instanceof SAML2_Response)) {
throw new SimpleSAML_Error_BadRequest('Invalid message received to AssertionConsumerService endpoint.');
@@ -40,7 +45,6 @@ if ($idp === NULL) {
SimpleSAML_Logger::debug('Received SAML2 Response from ' . var_export($idp, TRUE) . '.');
$idpMetadata = $source->getIdPmetadata($idp);
-$spMetadata = $source->getMetadata();
try {
$assertion = sspmod_saml2_Message::processResponse($spMetadata, $idpMetadata, $response);
diff --git a/modules/saml2/lib/Message.php b/modules/saml2/lib/Message.php
index 918cbe626ca769e0bd18317543055ac94599bb64..38de9951ff0df6511aa3bcc01f40060a8a2045e6 100644
--- a/modules/saml2/lib/Message.php
+++ b/modules/saml2/lib/Message.php
@@ -385,6 +385,15 @@ class sspmod_saml2_Message {
$ar->setForceAuthn($spMetadata->getBoolean('ForceAuthn', FALSE));
$ar->setIsPassive($spMetadata->getBoolean('IsPassive', FALSE));
+ $protbind = $spMetadata->getValueValidate('ProtocolBinding', array(
+ SAML2_Const::BINDING_HTTP_POST,
+ SAML2_Const::BINDING_HTTP_ARTIFACT,
+ SAML2_Const::BINDING_HTTP_REDIRECT,
+ ), SAML2_Const::BINDING_HTTP_POST);
+
+ /* Shoaib - setting the appropriate binding based on parameter in sp-metadata defaults to HTTP_POST */
+ $ar->setProtocolBinding($protbind);
+
if ($spMetadata->hasValue('AuthnContextClassRef')) {
$accr = $spMetadata->getArrayizeString('AuthnContextClassRef');
$ar->setRequestedAuthnContext(array('AuthnContextClassRef' => $accr));
diff --git a/www/saml2/sp/AssertionConsumerService.php b/www/saml2/sp/AssertionConsumerService.php
index 2fa55b819c8e3776e7539d0a40dc77cec9f2f0e4..bfe0c7766719c60642816901403d26e54d14e680 100644
--- a/www/saml2/sp/AssertionConsumerService.php
+++ b/www/saml2/sp/AssertionConsumerService.php
@@ -63,13 +63,16 @@ if (array_key_exists(SimpleSAML_Auth_ProcessingChain::AUTHPARAM, $_REQUEST)) {
}
-if (empty($_REQUEST['SAMLResponse']))
- SimpleSAML_Utilities::fatalError($session->getTrackID(), 'ACSPARAMS', $exception);
-
-
try {
+ $metadataHandler = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+ $sp = $metadataHandler->getMetaDataCurrentEntityID();
+ $spMetadata = $metadataHandler->getMetaDataConfig($sp, 'saml20-sp-hosted');
$b = SAML2_Binding::getCurrentBinding();
+ if ($b instanceof SAML2_HTTPArtifact) {
+ $b->setSPMetadata($spMetadata);
+ }
+
$response = $b->receive();
if (!($response instanceof SAML2_Response)) {
throw new SimpleSAML_Error_BadRequest('Invalid message received to AssertionConsumerService endpoint.');
@@ -80,11 +83,8 @@ try {
throw new Exception('Missing <saml:Issuer> in message delivered to AssertionConsumerService.');
}
- $metadataHandler = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
- $sp = $metadataHandler->getMetaDataCurrentEntityID();
$idpMetadata = $metadataHandler->getMetaDataConfig($idp, 'saml20-idp-remote');
- $spMetadata = $metadataHandler->getMetaDataConfig($sp, 'saml20-sp-hosted');
/* Fetch the request information if it exists, fall back to RelayState if not. */
$requestId = $response->getInResponseTo();
diff --git a/www/saml2/sp/initSSO.php b/www/saml2/sp/initSSO.php
index 02058b20c784facc673bd1d99d4e31c69a24039f..4146d103f68c210fd4a3aade32ac5c54b0b57ded 100644
--- a/www/saml2/sp/initSSO.php
+++ b/www/saml2/sp/initSSO.php
@@ -136,7 +136,6 @@ try {
$assertionConsumerServiceURL = $metadata->getGenerated('AssertionConsumerService', 'saml20-sp-hosted');
$ar->setAssertionConsumerServiceURL($assertionConsumerServiceURL);
- $ar->setProtocolBinding(SAML2_Const::BINDING_HTTP_POST);
$ar->setRelayState($_REQUEST['RelayState']);
if ($isPassive) {