From de4e22c5f1b38a2336da3e27f545be24ba665fe4 Mon Sep 17 00:00:00 2001
From: Jaime Perez Crespo <jaime.perez@uninett.no>
Date: Sun, 30 Aug 2015 19:45:15 +0200
Subject: [PATCH] When building an assertion, the current time should be
 obtained once, used many, instead of being obtained every time we are using
 it (that could lead to clock discrepancies between several timestamps in the
 same assertion). Additionally, if authentication happened in the past (that
 is, we got a request that is not the one that triggered authentication, and
 this is pure SSO), we should calculate the value for SessionNotOnOrAfter
 relative to the start of the session, not the current time. This resolves
 #244.

---
 modules/saml/lib/IdP/SAML2.php | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/modules/saml/lib/IdP/SAML2.php b/modules/saml/lib/IdP/SAML2.php
index 40afece41..c57e5bc46 100644
--- a/modules/saml/lib/IdP/SAML2.php
+++ b/modules/saml/lib/IdP/SAML2.php
@@ -773,6 +773,8 @@ class sspmod_saml_IdP_SAML2 {
 		assert('isset($state["Attributes"])');
 		assert('isset($state["saml:ConsumerURL"])');
 
+		$now = time();
+
 		$signAssertion = $spMetadata->getBoolean('saml20.sign.assertion', NULL);
 		if ($signAssertion === NULL) {
 			$signAssertion = $idpMetadata->getBoolean('saml20.sign.assertion', TRUE);
@@ -788,13 +790,13 @@ class sspmod_saml_IdP_SAML2 {
 		$a->setIssuer($idpMetadata->getString('entityid'));
 		$a->setValidAudiences(array($spMetadata->getString('entityid')));
 
-		$a->setNotBefore(time() - 30);
+		$a->setNotBefore($now - 30);
 
 		$assertionLifetime = $spMetadata->getInteger('assertion.lifetime', NULL);
 		if ($assertionLifetime === NULL) {
 			$assertionLifetime = $idpMetadata->getInteger('assertion.lifetime', 300);
 		}
-		$a->setNotOnOrAfter(time() + $assertionLifetime);
+		$a->setNotOnOrAfter($now + $assertionLifetime);
 
 		if (isset($state['saml:AuthnContextClassRef'])) {
 			$a->setAuthnContext($state['saml:AuthnContextClassRef']);
@@ -802,18 +804,20 @@ class sspmod_saml_IdP_SAML2 {
 			$a->setAuthnContext(SAML2_Const::AC_PASSWORD);
 		}
 
+		$sessionStart = $now;
 		if (isset($state['AuthnInstant'])) {
 			$a->setAuthnInstant($state['AuthnInstant']);
+			$sessionStart = $state['AuthnInstant'];
 		}
 
 		$sessionLifetime = $config->getInteger('session.duration', 8*60*60);
-		$a->setSessionNotOnOrAfter(time() + $sessionLifetime);
+		$a->setSessionNotOnOrAfter($sessionStart + $sessionLifetime);
 
 		$a->setSessionIndex(SimpleSAML\Utils\Random::generateID());
 
 		$sc = new SAML2_XML_saml_SubjectConfirmation();
 		$sc->SubjectConfirmationData = new SAML2_XML_saml_SubjectConfirmationData();
-		$sc->SubjectConfirmationData->NotOnOrAfter = time() + $assertionLifetime;
+		$sc->SubjectConfirmationData->NotOnOrAfter = $now + $assertionLifetime;
 		$sc->SubjectConfirmationData->Recipient = $state['saml:ConsumerURL'];
 		$sc->SubjectConfirmationData->InResponseTo = $state['saml:RequestId'];
 
-- 
GitLab