diff --git a/docs/simplesamlphp-reference-idp-hosted.txt b/docs/simplesamlphp-reference-idp-hosted.txt index b2d14bab0a3f78b8fe2ce6740e386503e6ed8139..6ee0640596c64ae6b1c66029857769962ef5001b 100644 --- a/docs/simplesamlphp-reference-idp-hosted.txt +++ b/docs/simplesamlphp-reference-idp-hosted.txt @@ -181,26 +181,6 @@ The following SAML 2.0 options are available: : Note that this option can be set for each SP in the [SP-remote metadata](./simplesamlphp-reference-sp-remote). -`SingleSignOnService` -: Override the default URL for the SingleSignOnService for this - IdP. This is an absolute URL. The default value is - `<simpleSAMLphp-root>/saml2/idp/SSOService.php` - -: Note that this only changes the values in the generated - metadata and in the messages sent to others. You must also - configure your webserver to deliver this URL to the correct PHP - page. - -`SingleLogoutService` -: Override the default URL for the SingleLogoutService for this - IdP. This is an absolute URL. The default value is - `<simpleSAMLphp-root>/saml2/idp/SingleLogoutService.php` - -: Note that this only changes the values in the generated - metadata and in the messages sent to others. You must also - configure your webserver to deliver this URL to the correct PHP - page. - `saml20.sendartifact` : Set to `TRUE` to enable the IdP to send responses with the HTTP-Artifact binding. Defaults to `FALSE`. @@ -234,6 +214,48 @@ The following SAML 2.0 options are available: any value in the SP-remote metadata overrides the one configured in the IdP metadata. +`SingleSignOnService` +: Override the default URL for the SingleSignOnService for this + IdP. This is an absolute URL. The default value is + `<simpleSAMLphp-root>/saml2/idp/SSOService.php` + +: Note that this only changes the values in the generated + metadata and in the messages sent to others. You must also + configure your webserver to deliver this URL to the correct PHP + page. + +`SingleSignOnServiceBinding` +: List of SingleSignOnService bindings that the IdP will claim support for. +: Possible values: + + * `urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect` + * `urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST` + +: Defaults to HTTP-Redirect binding. Please note that the order + specified will be kept in the metadata, making the first binding + the default one. + +`SingleLogoutService` +: Override the default URL for the SingleLogoutService for this + IdP. This is an absolute URL. The default value is + `<simpleSAMLphp-root>/saml2/idp/SingleLogoutService.php` + +: Note that this only changes the values in the generated + metadata and in the messages sent to others. You must also + configure your webserver to deliver this URL to the correct PHP + page. + +`SingleLogoutServiceBinding` +: List of SingleLogoutService bindings the IdP will claim support for. +: Possible values: + + * `urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect` + * `urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST` + +: Defaults to HTTP-Redirect binding. Please note that the order + specified will be kept in the metadata, making the first binding + the default one. + `validate.authnrequest` : Whether we require signatures on authentication requests sent to this IdP. diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php index d91b913baaf8be49c137048ebfe0ae25b88049fe..4d07d3c88a0c0e33202da6ad2958e81e7ca87fe1 100644 --- a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php +++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php @@ -107,8 +107,14 @@ class SimpleSAML_Metadata_MetaDataStorageHandler { case 'SingleSignOnService' : return $baseurl . 'saml2/idp/SSOService.php'; + case 'SingleSignOnServiceBinding' : + return SAML2_Const::BINDING_HTTP_REDIRECT; + case 'SingleLogoutService' : return $baseurl . 'saml2/idp/SingleLogoutService.php'; + + case 'SingleLogoutServiceBinding' : + return SAML2_Const::BINDING_HTTP_REDIRECT; } } elseif($set == 'shib13-sp-hosted') { switch ($property) { @@ -341,4 +347,3 @@ class SimpleSAML_Metadata_MetaDataStorageHandler { } -?> \ No newline at end of file diff --git a/modules/saml/docs/sp.txt b/modules/saml/docs/sp.txt index 84f8ca43fc673b67332f58489a56f4d853b07777..e77b32d4c4c702635de9401a62f2fab5879f1fce 100644 --- a/modules/saml/docs/sp.txt +++ b/modules/saml/docs/sp.txt @@ -388,6 +388,14 @@ Options : *Note*: SAML 2 specific. +`SingleLogoutServiceBinding` +: List of SingleLogoutService bindings the IdP will claim support for. +: Possible values: + + * `urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect` + * `urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST` + * `urn:oasis:names:tc:SAML:2.0:bindings:SOAP` + `redirect.sign` : Whether authentication requests, logout requests and logout responses sent from this SP should be signed. The default is `FALSE`. diff --git a/modules/saml/lib/Auth/Source/SP.php b/modules/saml/lib/Auth/Source/SP.php index 32d4bcc5efe8b91b6609b4bb8a2058dde8f22280..1154bdc8e88cc77893778e969eafa0ad2a736265 100644 --- a/modules/saml/lib/Auth/Source/SP.php +++ b/modules/saml/lib/Auth/Source/SP.php @@ -259,7 +259,22 @@ class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source { $ar->setId($id); SimpleSAML_Logger::debug('Sending SAML 2 AuthnRequest to ' . var_export($idpMetadata->getString('entityid'), TRUE)); - $b = new SAML2_HTTPRedirect(); + + /* Select appropriate SSO endpoint */ + if ($ar->getProtocolBinding() === SAML2_Const::BINDING_HOK_SSO) { + $dst = $idpMetadata->getDefaultEndpoint('SingleSignOnService', array( + SAML2_Const::BINDING_HOK_SSO) + ); + } else { + $dst = $idpMetadata->getDefaultEndpoint('SingleSignOnService', array( + SAML2_Const::BINDING_HTTP_REDIRECT, + SAML2_Const::BINDING_HTTP_POST) + ); + } + $ar->setDestination($dst['Location']); + + $b = SAML2_Binding::getBinding($dst['Binding']); + $this->sendSAML2AuthnRequest($state, $b, $ar); assert('FALSE'); @@ -392,7 +407,9 @@ class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source { $idpMetadata = $this->getIdPMetadata($idp); - $endpoint = $idpMetadata->getDefaultEndpoint('SingleLogoutService', array(SAML2_Const::BINDING_HTTP_REDIRECT), FALSE); + $endpoint = $idpMetadata->getDefaultEndpoint('SingleLogoutService', array( + SAML2_Const::BINDING_HTTP_REDIRECT, + SAML2_Const::BINDING_HTTP_POST), FALSE); if ($endpoint === FALSE) { SimpleSAML_Logger::info('No logout endpoint for IdP ' . var_export($idp, TRUE) . '.'); return; @@ -402,6 +419,7 @@ class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source { $lr->setNameId($nameId); $lr->setSessionIndex($sessionIndex); $lr->setRelayState($id); + $lr->setDestination($endpoint['Location']); $encryptNameId = $idpMetadata->getBoolean('nameid.encryption', NULL); if ($encryptNameId === NULL) { @@ -411,7 +429,7 @@ class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source { $lr->encryptNameId(sspmod_saml_Message::getEncryptionKey($idpMetadata)); } - $b = new SAML2_HTTPRedirect(); + $b = SAML2_Binding::getBinding($endpoint['Binding']); $b->send($lr); assert('FALSE'); diff --git a/modules/saml/lib/IdP/SAML2.php b/modules/saml/lib/IdP/SAML2.php index 94613b5f9be1f2e7d689775e2d840331f36f19dd..8b8897eb428eb66c3ec8ef0d66e8811aa05af2e8 100644 --- a/modules/saml/lib/IdP/SAML2.php +++ b/modules/saml/lib/IdP/SAML2.php @@ -419,8 +419,18 @@ class sspmod_saml_IdP_SAML2 { 'idpEntityID' => $idpMetadata->getString('entityid'), 'partial' => $partial )); + $dst = $spMetadata->getDefaultEndpoint('SingleLogoutService', array( + SAML2_Const::BINDING_HTTP_REDIRECT, + SAML2_Const::BINDING_HTTP_POST) + ); + $binding = SAML2_Binding::getBinding($dst['Binding']); + if (isset($dst['ResponseLocation'])) { + $dst = $dst['ResponseLocation']; + } else { + $dst = $dst['Location']; + } + $lr->setDestination($dst); - $binding = new SAML2_HTTPRedirect(); $binding->send($lr); } diff --git a/modules/saml/lib/Message.php b/modules/saml/lib/Message.php index 5b6c91a3c95c077dd91ba6af20abb1cc6d47bc09..68c2edefc597f11c405dec80a6734ab8318d5e40 100644 --- a/modules/saml/lib/Message.php +++ b/modules/saml/lib/Message.php @@ -413,16 +413,7 @@ class sspmod_saml_Message { /* Shoaib - setting the appropriate binding based on parameter in sp-metadata defaults to HTTP_POST */ $ar->setProtocolBinding($protbind); - /* Select appropriate SSO endpoint */ - if ($protbind === SAML2_Const::BINDING_HOK_SSO) { - $dst = $idpMetadata->getDefaultEndpoint('SingleSignOnService', array(SAML2_Const::BINDING_HOK_SSO)); - } else { - $dst = $idpMetadata->getDefaultEndpoint('SingleSignOnService', array(SAML2_Const::BINDING_HTTP_REDIRECT)); - } - $dst = $dst['Location']; - $ar->setIssuer($spMetadata->getString('entityid')); - $ar->setDestination($dst); if ($spMetadata->hasValue('AuthnContextClassRef')) { $accr = $spMetadata->getArrayizeString('AuthnContextClassRef'); @@ -443,13 +434,8 @@ class sspmod_saml_Message { */ public static function buildLogoutRequest(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata) { - $dst = $dstMetadata->getDefaultEndpoint('SingleLogoutService', array(SAML2_Const::BINDING_HTTP_REDIRECT)); - $dst = $dst['Location']; - $lr = new SAML2_LogoutRequest(); - $lr->setIssuer($srcMetadata->getString('entityid')); - $lr->setDestination($dst); self::addRedirectSign($srcMetadata, $dstMetadata, $lr); @@ -465,17 +451,8 @@ class sspmod_saml_Message { */ public static function buildLogoutResponse(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata) { - $dst = $dstMetadata->getDefaultEndpoint('SingleLogoutService', array(SAML2_Const::BINDING_HTTP_REDIRECT)); - if (isset($dst['ResponseLocation'])) { - $dst = $dst['ResponseLocation']; - } else { - $dst = $dst['Location']; - } - $lr = new SAML2_LogoutResponse(); - $lr->setIssuer($srcMetadata->getString('entityid')); - $lr->setDestination($dst); self::addRedirectSign($srcMetadata, $dstMetadata, $lr); diff --git a/modules/saml/www/sp/metadata.php b/modules/saml/www/sp/metadata.php index 6e28942715abff08a5b76760189ebd512e4da875..6a9a153dc80b985a6dd9da3a0d14c2452b22a638 100644 --- a/modules/saml/www/sp/metadata.php +++ b/modules/saml/www/sp/metadata.php @@ -17,23 +17,26 @@ if (!($source instanceof sspmod_saml_Auth_Source_SP)) { $entityId = $source->getEntityId(); $spconfig = $source->getMetadata(); +$store = SimpleSAML_Store::getInstance(); + +$metaArray20 = array(); -$metaArray20 = array( - 'SingleLogoutService' => SimpleSAML_Module::getModuleURL('saml/sp/saml2-logout.php/' . $sourceId), +$slosvcdefault = array( + SAML2_Const::BINDING_HTTP_REDIRECT, + SAML2_Const::BINDING_SOAP, ); -$store = SimpleSAML_Store::getInstance(); -if ($store instanceof SimpleSAML_Store_SQL) { - /* We can properly support SOAP logout. */ - $metaArray20['SingleLogoutService'] = array( - array( - 'Binding' => SAML2_Const::BINDING_HTTP_REDIRECT, - 'Location' => SimpleSAML_Module::getModuleURL('saml/sp/saml2-logout.php/' . $sourceId), - ), - array( - 'Binding' => SAML2_Const::BINDING_SOAP, - 'Location' => SimpleSAML_Module::getModuleURL('saml/sp/saml2-logout.php/' . $sourceId), - ), +$slob = $spconfig->getArray('SingleLogoutServiceBinding', $slosvcdefault); +$slol = SimpleSAML_Module::getModuleURL('saml/sp/saml2-logout.php/' . $sourceId); + +foreach ($slob as $binding) { + if ($binding == SAML2_Const::BINDING_SOAP && !($store instanceof SimpleSAML_Store_SQL)) { + /* We cannot properly support SOAP logout. */ + continue; + } + $metaArray20['SingleLogoutService'][] = array( + 'Binding' => $binding, + 'Location' => $slol, ); } diff --git a/modules/saml/www/sp/saml2-logout.php b/modules/saml/www/sp/saml2-logout.php index caf09b046fa5fabeab4badbf143841b714dd698e..ca168fdc049219a31bd9e562e8b44349555e88b5 100644 --- a/modules/saml/www/sp/saml2-logout.php +++ b/modules/saml/www/sp/saml2-logout.php @@ -108,6 +108,21 @@ if ($message instanceof SAML2_LogoutResponse) { SimpleSAML_Logger::warning('Logged out of ' . $numLoggedOut . ' of ' . count($sessionIndexes) . ' sessions.'); } + $dst = $idpMetadata->getDefaultEndpoint('SingleLogoutService', array( + SAML2_Const::BINDING_HTTP_REDIRECT, + SAML2_Const::BINDING_HTTP_POST) + ); + + if (!$binding instanceof SAML2_SOAP) { + $binding = SAML2_Binding::getBinding($dst['Binding']); + if (isset($dst['ResponseLocation'])) { + $dst = $dst['ResponseLocation']; + } else { + $dst = $dst['Location']; + } + $binding->setDestination($dst); + } + $binding->send($lr); } else { throw new SimpleSAML_Error_BadRequest('Unknown message received on logout endpoint: ' . get_class($message)); diff --git a/www/saml2/idp/metadata.php b/www/saml2/idp/metadata.php index e0b8e0db55a0743f08e2ab7f547ae6ddcabb954f..a7d278553041161f1005823ee5251a6ce7b9038f 100644 --- a/www/saml2/idp/metadata.php +++ b/www/saml2/idp/metadata.php @@ -60,12 +60,41 @@ try { $metaArray = array( 'metadata-set' => 'saml20-idp-remote', 'entityid' => $idpentityid, - 'SingleSignOnService' => array(0 => array( - 'Binding' => SAML2_Const::BINDING_HTTP_REDIRECT, - 'Location' => $metadata->getGenerated('SingleSignOnService', 'saml20-idp-hosted'))), - 'SingleLogoutService' => $metadata->getGenerated('SingleLogoutService', 'saml20-idp-hosted'), ); + $ssob = $metadata->getGenerated('SingleSignOnServiceBinding', 'saml20-idp-hosted'); + $slob = $metadata->getGenerated('SingleLogoutServiceBinding', 'saml20-idp-hosted'); + $ssol = $metadata->getGenerated('SingleSignOnService', 'saml20-idp-hosted'); + $slol = $metadata->getGenerated('SingleLogoutService', 'saml20-idp-hosted'); + + if (is_array($ssob)) { + foreach ($ssob as $binding) { + $metaArray['SingleSignOnService'][] = array( + 'Binding' => $binding, + 'Location' => $ssol, + ); + } + } else { + $metaArray['SingleSignOnService'][] = array( + 'Binding' => $ssob, + 'Location' => $ssol, + ); + } + + if (is_array($slob)) { + foreach ($slob as $binding) { + $metaArray['SingleLogoutService'][] = array( + 'Binding' => $binding, + 'Location' => $slol, + ); + } + } else { + $metaArray['SingleLogoutService'][] = array( + 'Binding' => $slob, + 'Location' => $slol, + ); + } + if (count($keys) === 1) { $metaArray['certData'] = $keys[0]['X509Certificate']; } else { @@ -164,4 +193,3 @@ try { } -?> \ No newline at end of file diff --git a/www/saml2/sp/SingleLogoutService.php b/www/saml2/sp/SingleLogoutService.php index 559a088c05bd5e9d8d9e3367048f07746c809def..bf1a8dbbfe3538a8febb04239a5663b84394905d 100644 --- a/www/saml2/sp/SingleLogoutService.php +++ b/www/saml2/sp/SingleLogoutService.php @@ -52,8 +52,22 @@ if ($message instanceof SAML2_LogoutRequest) { SimpleSAML_Logger::info('SAML2.0 - SP.SingleLogoutService: SP me (' . $spEntityId . ') is sending logout response to IdP (' . $idpEntityId . ')'); + $dst = $idpMetadata->getDefaultEndpoint('SingleLogoutService', array( + SAML2_Const::BINDING_HTTP_REDIRECT, + SAML2_Const::BINDING_HTTP_POST) + ); + + if (!$binding instanceof SAML2_SOAP) { + $binding = SAML2_Binding::getBinding($dst['Binding']); + if (isset($dst['ResponseLocation'])) { + $dst = $dst['ResponseLocation']; + } else { + $dst = $dst['Location']; + } + $binding->setDestination($dst); + } + /* Send response. */ - $binding = new SAML2_HTTPRedirect(); $binding->send($lr); } catch (Exception $exception) { throw new SimpleSAML_Error_Error('LOGOUTREQUEST', $exception); @@ -80,6 +94,3 @@ if ($message instanceof SAML2_LogoutRequest) { throw new SimpleSAML_Error_Error('SLOSERVICEPARAMS'); } - - -?> \ No newline at end of file diff --git a/www/saml2/sp/initSLO.php b/www/saml2/sp/initSLO.php index 7d4bb8f86fd9c29ef5f4c6c2927c3544233c6f78..84855d00e3c8baf1c1814b80feff2b7358a379bf 100644 --- a/www/saml2/sp/initSLO.php +++ b/www/saml2/sp/initSLO.php @@ -28,12 +28,13 @@ try { SimpleSAML_Utilities::redirect($returnTo); } $idpMetadata = $metadata->getMetaDataConfig($idpEntityId, 'saml20-idp-remote'); - $SLOendpoint = $idpMetadata->getDefaultEndpoint('SingleLogoutService', array(SAML2_Const::BINDING_HTTP_REDIRECT), NULL); + $SLOendpoint = $idpMetadata->getDefaultEndpoint('SingleLogoutService', array(SAML2_Const::BINDING_HTTP_REDIRECT, SAML2_Const::BINDING_HTTP_POST), NULL); if ($SLOendpoint === NULL) { $session->doLogout('saml2'); SimpleSAML_Logger::info('SAML2.0 - SP.initSLO: No supported SingleLogoutService endpoint in IdP.'); SimpleSAML_Utilities::redirect($returnTo); } + $lr->setDestination($SLOendpoint['Location']); $spEntityId = isset($_GET['spentityid']) ? $_GET['spentityid'] : $metadata->getMetaDataCurrentEntityID(); $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-hosted'); @@ -51,7 +52,7 @@ try { SimpleSAML_Logger::info('SAML2.0 - SP.initSLO: SP (' . $spEntityId . ') is sending logout request to IdP (' . $idpEntityId . ')'); - $b = new SAML2_HTTPRedirect(); + $b = SAML2_Binding::getBinding($SLOendpoint['Binding']); $b->send($lr); @@ -59,5 +60,3 @@ try { throw new SimpleSAML_Error_Error('CREATEREQUEST', $exception); } - -?> \ No newline at end of file diff --git a/www/saml2/sp/initSSO.php b/www/saml2/sp/initSSO.php index b2abf60b82972749171c43db5eb29bc743656d16..491a18d17d1c251f59e9fd06d4324cb79f98c98a 100644 --- a/www/saml2/sp/initSSO.php +++ b/www/saml2/sp/initSSO.php @@ -164,11 +164,18 @@ try { } $session->setData('SAML2:SP:SSO:Info', $ar->getId(), $info); - $b = new SAML2_HTTPRedirect(); + /* Select appropriate SSO endpoint */ + if ($ar->getProtocolBinding() === SAML2_Const::BINDING_HOK_SSO) { + $dst = $idpMetadata->getDefaultEndpoint('SingleSignOnService', array(SAML2_Const::BINDING_HOK_SSO)); + } else { + $dst = $idpMetadata->getDefaultEndpoint('SingleSignOnService', array(SAML2_Const::BINDING_HTTP_REDIRECT, SAML2_Const::BINDING_HTTP_POST)); + } + $ar->setDestination($dst['Location']); + + $b = SAML2_Binding::getBinding($dst['Binding']); $b->send($ar); } catch(Exception $exception) { throw new SimpleSAML_Error_Error('CREATEREQUEST', $exception); } -?> \ No newline at end of file diff --git a/www/saml2/sp/metadata.php b/www/saml2/sp/metadata.php index 03331212ccda6e9e35d3f03aa60a1a6587bd0b9a..9471946851b914ed36cdddc208caf2580c45114e 100644 --- a/www/saml2/sp/metadata.php +++ b/www/saml2/sp/metadata.php @@ -25,9 +25,25 @@ try { 'metadata-set' => 'saml20-sp-remote', 'entityid' => $spentityid, 'AssertionConsumerService' => $metadata->getGenerated('AssertionConsumerService', 'saml20-sp-hosted'), - 'SingleLogoutService' => $metadata->getGenerated('SingleLogoutService', 'saml20-sp-hosted'), ); + $slob = $metadata->getGenerated('SingleLogoutServiceBinding', 'saml20-sp-hosted'); + $slol = $metadata->getGenerated('SingleLogoutService', 'saml20-sp-hosted'); + + if (is_array($slob)) { + foreach ($slob as $binding) { + $metaArray['SingleLogoutService'][] = array( + 'Binding' => $binding, + 'Location' => $slol, + ); + } + } else { + $metaArray['SingleLogoutService'][] = array( + 'Binding' => $slob, + 'Location' => $slol, + ); + } + $metaArray['NameIDFormat'] = $spmeta->getString('NameIDFormat', 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'); if ($spmeta->hasValue('OrganizationName')) { @@ -97,4 +113,3 @@ try { } -?> \ No newline at end of file