From 6b91de0e6ba015ce0ac02709b3832f963469ba5e Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Mon, 3 Aug 2009 12:47:48 +0000
Subject: [PATCH] saml2: Use SAML library for handling logout messages from
 IdP.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@1609 44740490-163a-0410-bde0-09ae8108e29a
---
 modules/saml2/lib/Message.php   | 52 +++++++++++++++++++++++++++
 modules/saml2/www/sp/logout.php | 63 ++++++++++++++++-----------------
 2 files changed, 82 insertions(+), 33 deletions(-)

diff --git a/modules/saml2/lib/Message.php b/modules/saml2/lib/Message.php
index 1f8c9a39a..b80a55a97 100644
--- a/modules/saml2/lib/Message.php
+++ b/modules/saml2/lib/Message.php
@@ -181,6 +181,34 @@ class sspmod_saml2_Message {
 	}
 
 
+	/**
+	 * Check signature on a SAML2 message if enabled.
+	 *
+	 * @param SimpleSAML_Configuration $srcMetadata  The metadata of the sender.
+	 * @param SimpleSAML_Configuration $dstMetadata  The metadata of the recipient.
+	 * @param SAML2_Message $message  The message we should check the signature on.
+	 */
+	public static function validateMessage(
+		SimpleSAML_Configuration $srcMetadata,
+		SimpleSAML_Configuration $dstMetadata,
+		SAML2_Message $message
+		) {
+
+		$enabled = $srcMetadata->getBoolean('redirect.validate', NULL);
+		if ($enabled === NULL) {
+			$enabled = $dstMetadata->getBoolean('redirect.validate', FALSE);
+		}
+
+		if (!$enabled) {
+			return;
+		}
+
+		if (!self::checkSign($srcMetadata, $message)) {
+			throw new SimpleSAML_Error_Exception('Validation of received messages enabled, but no signature found on message.');
+		}
+	}
+
+
 	/**
 	 * Decrypt an assertion.
 	 *
@@ -293,6 +321,30 @@ class sspmod_saml2_Message {
 	}
 
 
+	/**
+	 * Build a logout response based on information in the metadata.
+	 *
+	 * @param SimpleSAML_Configuration $srcMetadata  The metadata of the sender.
+	 * @param SimpleSAML_Configuration $dstpMetadata  The metadata of the recipient.
+	 */
+	public static function buildLogoutResponse(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata) {
+
+		$lr = new SAML2_LogoutResponse();
+
+		$lr->setIssuer($srcMetadata->getString('entityid'));
+
+		$dst = $dstMetadata->getString('SingleLogoutServiceResponse', NULL);
+		if ($dst === NULL) {
+			$dst = $dstMetadata->getString('SingleLogoutService');
+		}
+		$lr->setDestination($dst);
+
+		self::addSign($srcMetadata, $dstMetadata, $lr);
+
+		return $lr;
+	}
+
+
 	/**
 	 * Process a response message.
 	 *
diff --git a/modules/saml2/www/sp/logout.php b/modules/saml2/www/sp/logout.php
index 75d0fa413..43448c362 100644
--- a/modules/saml2/www/sp/logout.php
+++ b/modules/saml2/www/sp/logout.php
@@ -17,57 +17,54 @@ if ($source === NULL) {
 	throw new Exception('Could not find authentication source with id ' . $sourceId);
 }
 
+$binding = SAML2_Binding::getCurrentBinding();
+$message = $binding->receive();
 
-$config = SimpleSAML_Configuration::getInstance();
-$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+$idpEntityId = $message->getIssuer();
+if ($idpEntityId === NULL) {
+	/* Without an issuer we have no way to respond to the message. */
+	throw new SimpleSAML_Error_BadRequest('Received message on logout endpoint without issuer.');
+}
 
-if (array_key_exists('SAMLResponse', $_GET)) {
+$spEntityId = $source->getEntityId();
 
-	$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
-	$response = $binding->decodeLogoutResponse($_GET);
+$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+$idpMetadata = $metadata->getMetaDataConfig($idpEntityId, 'saml20-idp-remote');
+$spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-hosted');
 
-	if ($binding->validateQuery($response->getIssuer(), 'SP', 'SAMLResponse')) {
-		SimpleSAML_Logger::debug('module/saml2/sp/logout: Valid signature found on response');
-	}
+sspmod_saml2_Message::validateMessage($idpMetadata, $spMetadata, $message);
 
-	if (!array_key_exists('RelayState', $_REQUEST)) {
+if ($message instanceof SAML2_LogoutResponse) {
+
+	$relayState = $message->getRelayState();
+	if ($relayState === NULL) {
+		/* Somehow, our RelayState has been lost. */
 		throw new SimpleSAML_Error_BadRequest('Missing RelayState in logout response.');
 	}
 
-	$stateId = $_REQUEST['RelayState'];
-	$state = SimpleSAML_Auth_State::loadState($_REQUEST['RelayState'], sspmod_saml2_Auth_Source_SP::STAGE_LOGOUTSENT);
-
-
-	SimpleSAML_Auth_Source::completeLogout($state);
-
-} elseif (array_key_exists('SAMLRequest', $_GET)) {
-
-	$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
-
-	$request = $binding->decodeLogoutRequest($_GET);
-
-	$requestId = $request->getRequestID();
-	$idpEntityId = $request->getIssuer();
-	$relayState = $request->getRelayState();
-
-	if ($binding->validateQuery($idpEntityId, 'SP')) {
-		SimpleSAML_Logger::debug('module/saml2/sp/logout: Valid signature found on request');
+	if (!$message->isSuccess()) {
+		SimpleSAML_Logger::warning('Unsuccessful logout. Status was: ' . sspmod_saml2_Message::getResponseError($message));
 	}
 
+	$state = SimpleSAML_Auth_State::loadState($relayState, sspmod_saml2_Auth_Source_SP::STAGE_LOGOUTSENT);
+	SimpleSAML_Auth_Source::completeLogout($state);
 
-	$spEntityId = $source->getEntityId();
+} elseif ($message instanceof SAML2_LogoutRequest) {
 
-	SimpleSAML_Logger::debug('module/saml2/sp/logout: Request from ' . $idpEntityId . ' with request id ' . $requestId);
+	SimpleSAML_Logger::debug('module/saml2/sp/logout: Request from ' . $idpEntityId);
 	SimpleSAML_Logger::stats('saml20-idp-SLO idpinit ' . $spEntityId . ' ' . $idpEntityId);
 
 	/* Notify source of logout, so that it may call logout callbacks. */
 	$source->onLogout($idpEntityId);
 
 	/* Create an send response. */
-	$lr = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata);
-	$responseXML = $lr->generate($spEntityId, $idpEntityId, $requestId, 'SP');
-	$binding->sendMessage($responseXML, $spEntityId, $idpEntityId, $relayState, 'SingleLogoutServiceResponse', 'SAMLResponse');
+	$lr = sspmod_saml2_Message::buildLogoutResponse($spMetadata, $idpMetadata);
+	$lr->setRelayState($message->getRelayState());
+	$lr->setInResponseTo($message->getId());
 
+	$binding = new SAML2_HTTPRedirect();
+	$binding->setDestination(sspmod_SAML2_Message::getDebugDestination());
+	$binding->send($lr);
 } else {
-	throw new SimpleSAML_Error_BadRequest('Missing request or response to logout endpoint');
+	throw new SimpleSAML_Error_BadRequest('Unknown message received on logout endpoint: ' . get_class($message));
 }
-- 
GitLab