diff --git a/dictionaries/errors.php b/dictionaries/errors.php index 49a654a64bcee3f3e52a8ff09b4d981b2cfd2616..e94c482178a77c318903743d5575f3f8b109be4f 100644 --- a/dictionaries/errors.php +++ b/dictionaries/errors.php @@ -13,6 +13,10 @@ $lang = array( 'title_GENERATEAUTHNRESPONSE' => 'Could not create authentication response', 'descr_GENERATEAUTHNRESPONSE' => 'When this identity provider tried to create an authentication response, an error occured.', + + 'title_GENERATELOGOUTRESPONSE' => 'Could not create logout response', + 'descr_GENERATELOGOUTRESPONSE' => 'When this SAML entity tried to create an logout response, an error occured.', + 'title_LDAPERROR' => 'LDAP Error', 'descr_LDAPERROR' => 'LDAP is the user database, and when you try to login, we need to contact an LDAP database. When we tried it this time an error occured.', @@ -20,6 +24,9 @@ $lang = array( 'title_LOGOUTREQUEST' => 'Error processing Logout Request', 'descr_LOGOUTREQUEST' => 'An error occured when trying to process the Logout Request.', + 'title_GENERATELOGOUTREQUEST' => 'Could not create logout request', + 'descr_GENERATELOGOUTREQUEST' => 'When this SAML entity tried to create an logout request, an error occured.', + 'title_LOGOUTRESPONSE' => 'Error processing Logout Response', 'descr_LOGOUTRESPONSE' => 'An error occured when trying to process the Logout Response.', diff --git a/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php b/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php index a57bd2270b42e15a56dfc20eeab448bf72a79fad..c7d26d7861c928aaaffcd327255db4f167967bfe 100644 --- a/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php +++ b/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php @@ -67,7 +67,7 @@ class SimpleSAML_Bindings_SAML20_HTTPRedirect { if ($mode == 'IdP') { $metadataset = 'saml20-sp-remote'; } - + SimpleSAML_Logger::debug('Library - HTTPRedirect validateQuery(): Looking up metadata issuer:' . $issuer . ' in set '. $metadataset); $md = $this->metadata->getMetaData($issuer, $metadataset); // check wether to validate or not diff --git a/www/saml2/idp/SingleLogoutService.php b/www/saml2/idp/SingleLogoutService.php index cbe7f4644f816f58f6e7238634d39c4643c9ba10..ddf7a3d1b288a717618865d64ce3ea969f5d0e72 100644 --- a/www/saml2/idp/SingleLogoutService.php +++ b/www/saml2/idp/SingleLogoutService.php @@ -39,6 +39,7 @@ try { SimpleSAML_Utilities::fatalError($session->getTrackID(), 'METADATA', $exception); } +SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: Got IdP entity id: ' . $idpentityid); /** * If we get an incomming LogoutRequest then we initiate the logout process. @@ -48,6 +49,8 @@ try { */ if (isset($_GET['SAMLRequest'])) { + SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: Got SAML reuqest'); + $binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); try { @@ -58,16 +61,9 @@ if (isset($_GET['SAMLRequest'])) { } } catch(Exception $exception) { - - $et = new SimpleSAML_XHTML_Template($config, 'error.php'); - - $et->data['header'] = 'Error in received logout request'; - $et->data['message'] = 'An error occured when trying to read logout request.'; - $et->data['e'] = $exception; - - $et->show(); - exit(0); - + + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'LOGOUTREQUEST', $exception); + } // Extract some parameters from the logout request @@ -125,6 +121,8 @@ if (isset($_GET['SAMLRequest'])) { $requestcache['RelayState'] = $relaystate; $session->setLogoutRequest($requestcache); + SimpleSAML_Logger::debug('SAML2.0 - IDP.SingleLogoutService: Setting cached request with issuer ' . $logoutrequest->getIssuer()); + $session->set_sp_logout_completed($logoutrequest->getIssuer() ); @@ -133,26 +131,23 @@ if (isset($_GET['SAMLRequest'])) { */ } elseif (isset($_GET['SAMLResponse'])) { + SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: Got SAML response'); + $binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); try { $loginresponse = $binding->decodeLogoutResponse($_GET); + + SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: SAML response parsed. Issuer is: ' . $loginresponse->getIssuer()); - if ($binding->validateQuery($loginresponse->getIssuer(),'SP','SAMLResponse')) { + if ($binding->validateQuery($loginresponse->getIssuer(),'IdP','SAMLResponse')) { SimpleSAML_Logger::info('SAML2.0 - IDP.SingleLogoutService: Valid signature found'); } } catch(Exception $exception) { - $et = new SimpleSAML_XHTML_Template($config, 'error.php'); - - $et->data['header'] = 'Error in received logout response'; - $et->data['message'] = 'An error occured when trying to read logout response.'; - $et->data['e'] = $exception; - - $et->show(); - exit(0); + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'LOGOUTRESPONSE', $exception); } @@ -163,6 +158,7 @@ if (isset($_GET['SAMLRequest'])) { SimpleSAML_Logger::info('SAML2.0 - IDP.SingleLogoutService: got LogoutResponse from ' . $loginresponse->getIssuer()); } else { + SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: No request or response...'); /** * This error message was removed 2008-02-27, because it interrupts with bridged SLO. * @@ -170,15 +166,38 @@ if (isset($_GET['SAMLRequest'])) { */ } +$lookformore = true; +$spentityid = null; +do { + /* Dump the current sessions (for debugging). */ + $session->dump_sp_sessions(); -/* Dump the current sessions (for debugging). */ -$session->dump_sp_sessions(); + /* + * We proceed to send logout requests to all remaining SPs. + */ + $spentityid = $session->get_next_sp_logout(); + + + // If there are no more SPs left, then we will not look for more SPs. + if (empty($spentityid)) $lookformore = false; + + try { + $spmetadata = $metadata->getMetadata($spentityid, 'saml20-sp-remote'); + } catch (Exception $e) { + continue; + } + + // If the SP we found have an SingleLogout endpoint then we will use it, and + // hence we do not need to look for more yet. + if (array_key_exists('SingleLogoutService', $spmetadata) && + !empty($spmetadata['SingleLogoutService']) ) $lookformore = false; + + if ($lookformore) + SimpleSAML_Logger::info('SAML2.0 - IDP.SingleLogoutService: Will not logout from ' . $spentityid . ' looking for more SPs'); + +} while ($lookformore); -/* - * We proceed to send logout requests to all remaining SPs. - */ -$spentityid = $session->get_next_sp_logout(); if ($spentityid) { SimpleSAML_Logger::info('SAML2.0 - IDP.SingleLogoutService: Logout next SP ' . $spentityid); @@ -203,14 +222,8 @@ if ($spentityid) { } catch(Exception $exception) { - $et = new SimpleSAML_XHTML_Template($config, 'error.php'); - - $et->data['header'] = 'Error sending logout request to service'; - $et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.'; - $et->data['e'] = $exception; + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'GENERATELOGOUTREQUEST', $exception); - $et->show(); - exit(0); } @@ -252,6 +265,7 @@ try { throw new Exception('Could not get reference to the logout request.'); } + SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: Found request cache with these keys: ' . join(',', array_keys($requestcache))); /** * Clean up session object to save storage. @@ -265,32 +279,44 @@ try { SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Session Size after cleaning: ' . $session->getSize()); - /** - * Create a Logot Response. + /* + * Check if the Single Logout procedure is initated by an SP (alternatively IdP initiated SLO */ - $rg = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata); - - // generate($issuer, $receiver, $inresponseto, $mode ) - $logoutResponseXML = $rg->generate($idpentityid, $requestcache['Issuer'], $requestcache['RequestID'], 'IdP'); - - // Create a HTTP-REDIRECT Binding. - $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); - - // Find the relaystate if cached. - $relayState = isset($requestcache['RelayState']) ? $requestcache['RelayState'] : null; + if (array_key_exists('Issuer', $requestcache)) { + + /** + * Create a Logot Response. + */ + $rg = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata); + + // generate($issuer, $receiver, $inresponseto, $mode ) + $logoutResponseXML = $rg->generate($idpentityid, $requestcache['Issuer'], $requestcache['RequestID'], 'IdP'); + + // Create a HTTP-REDIRECT Binding. + $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + + // Find the relaystate if cached. + $relayState = isset($requestcache['RelayState']) ? $requestcache['RelayState'] : null; + + // Parameters: $request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP' + $httpredirect->sendMessage($logoutResponseXML, $idpentityid, $requestcache['Issuer'], $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP'); + exit; + + } elseif (array_key_exists('RelayState', $requestcache)) { - // Parameters: $request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP' - $httpredirect->sendMessage($logoutResponseXML, $idpentityid, $requestcache['Issuer'], $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP'); + SimpleSAML_Utilities::redirect($requestcache['RelayState']); + exit; + + } else { + + echo 'You are logged out'; exit; + + } } catch(Exception $exception) { - - $et = new SimpleSAML_XHTML_Template($config, 'error.php'); - - $et->data['header'] = 'Error sending response to service'; - $et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.'; - $et->data['e'] = $exception; - - $et->show(); + + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'GENERATELOGOUTRESPONSE', $exception); + } -?> \ No newline at end of file + diff --git a/www/saml2/idp/initSLO.php b/www/saml2/idp/initSLO.php new file mode 100644 index 0000000000000000000000000000000000000000..e3eec8d4b0aa75b228d2704e088eaf856cbef30c --- /dev/null +++ b/www/saml2/idp/initSLO.php @@ -0,0 +1,153 @@ +<?php + +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . '../../../www/_include.php'); + +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Logger.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/MetaDataStorageHandler.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutRequest.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutResponse.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Bindings/SAML20/HTTPRedirect.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php'); + + +$config = SimpleSAML_Configuration::getInstance(); +$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); +$session = SimpleSAML_Session::getInstance(); + + +SimpleSAML_Logger::info('SAML2.0 - IdP.initSLO: Accessing SAML 2.0 IdP endpoint init Single Logout'); + +if (!$config->getValue('enable.saml20-idp', false)) + SimpleSAML_Utilities::fatalError(isset($session) ? $session->getTrackID() : null, 'NOACCESS'); + +try { + $idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); +} catch (Exception $exception) { + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'METADATA', $exception); +} + + +/** + * If we get an incomming LogoutRequest then we initiate the logout process. + * in this case an SAML 2.0 SP is sending an request, which also is referred to as + * SP initiated Single Logout. + * + */ +if (isset($_GET['RelayState'])) { + + $relaystate = $_GET['RelayState']; + + /** + * No session exists. Just go to the RelayState. + */ + if($session === NULL) { + SimpleSAML_Logger::info('SAML2.0 - IdP.initSLO: Did not find a session here, so we redirect to the RelayState'); + SimpleSAML_Utilities::redirect($relaystate); + exit; + } + + // Set local IdP session to invalid. + $session->setAuthenticated(false, $session->getAuthority() ); + + + /* + * Create an assoc array of the request to store in the session cache. + */ + $requestcache = array( + 'RelayState' => $relaystate + ); + + $session->setLogoutRequest($requestcache); + SimpleSAML_Logger::debug('SAML2.0 - IDP.initSSO: Setting cached request with relay state ' . $relaystate); + + //$session->set_sp_logout_completed($logoutrequest->getIssuer() ); + + +} + +$lookformore = true; +$spentityid = null; +do { + /* Dump the current sessions (for debugging). */ + $session->dump_sp_sessions(); + + /* + * We proceed to send logout requests to all remaining SPs. + */ + $spentityid = $session->get_next_sp_logout(); + + + // If there are no more SPs left, then we will not look for more SPs. + if (empty($spentityid)) $lookformore = false; + + try { + $spmetadata = $metadata->getMetadata($spentityid, 'saml20-sp-remote'); + } catch (Exception $e) { + continue; + } + + // If the SP we found have an SingleLogout endpoint then we will use it, and + // hence we do not need to look for more yet. + if (array_key_exists('SingleLogoutService', $spmetadata) && + !empty($spmetadata['SingleLogoutService']) ) $lookformore = false; + + if ($lookformore) + SimpleSAML_Logger::info('SAML2.0 - IDP.SingleLogoutService: Will not logout from ' . $spentityid . ' looking for more SPs'); + +} while ($lookformore); + + + +/* + * We proceed to send logout requests to the first remaining SP. + */ +$spentityid = $session->get_next_sp_logout(); +if ($spentityid) { + + SimpleSAML_Logger::info('SAML2.0 - IDP.SingleLogoutService: Logout next SP ' . $spentityid); + + try { + $lr = new SimpleSAML_XML_SAML20_LogoutRequest($config, $metadata); + + // ($issuer, $receiver, $nameid, $nameidformat, $sessionindex, $mode) { + $req = $lr->generate($idpentityid, $spentityid, $session->getNameID(), $session->getSessionIndex(), 'IdP'); + + $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + + $relayState = SimpleSAML_Utilities::selfURL(); + if (isset($_GET['RelayState'])) { + $relayState = $_GET['RelayState']; + } + + //$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP' + $httpredirect->sendMessage($req, $idpentityid, $spentityid, $relayState, 'SingleLogoutService', 'SAMLRequest', 'IdP'); + + exit(); + + } catch(Exception $exception) { + + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'GENERATELOGOUTREQUEST', $exception); + + } + +} + +if ($config->getValue('debug', false)) + SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: LogoutService: All SPs done '); + + +if (isset($_GET['RelayState'])) { + + $relayState = $_GET['RelayState']; + SimpleSAML_Utilities::redirect($relayState); + +} else { + + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'NORELAYSTATE'); + +} + + +