diff --git a/modules/saml/www/sp/saml2-acs.php b/modules/saml/www/sp/saml2-acs.php index ff495904a27a559d971de82096c3200097939d48..624f7cca73a18cde7b736c8aa1b10dfed24c93a7 100644 --- a/modules/saml/www/sp/saml2-acs.php +++ b/modules/saml/www/sp/saml2-acs.php @@ -25,170 +25,177 @@ try { } if ($b instanceof SAML2_HTTPArtifact) { - $b->setSPMetadata($spMetadata); + $b->setSPMetadata($spMetadata); } $response = $b->receive(); if (!($response instanceof SAML2_Response)) { - throw new SimpleSAML_Error_BadRequest('Invalid message received to AssertionConsumerService endpoint.'); + throw new SimpleSAML_Error_BadRequest('Invalid message received to AssertionConsumerService endpoint.'); } $idp = $response->getIssuer(); -if ($idp === NULL) { - /* No Issuer in the response. Look for an unencrypted assertion with an issuer. */ - foreach ($response->getAssertions() as $a) { - if ($a instanceof SAML2_Assertion) { - /* We found an unencrypted assertion - there should be an issuer here. */ - $idp = $a->getIssuer(); - break; - } - } - if ($idp === NULL) { - /* No issuer found in the assertions. */ - throw new Exception('Missing <saml:Issuer> in message delivered to AssertionConsumerService.'); - } +if ($idp === null) { + // no Issuer in the response. Look for an unencrypted assertion with an issuer + foreach ($response->getAssertions() as $a) { + if ($a instanceof SAML2_Assertion) { + // we found an unencrypted assertion, there should be an issuer here + $idp = $a->getIssuer(); + break; + } + } + if ($idp === null) { + // no issuer found in the assertions + throw new Exception('Missing <saml:Issuer> in message delivered to AssertionConsumerService.'); + } } $session = SimpleSAML_Session::getSessionFromRequest(); $prevAuth = $session->getAuthData($sourceId, 'saml:sp:prevAuth'); -if ($prevAuth !== NULL && $prevAuth['id'] === $response->getId() && $prevAuth['issuer'] === $idp) { - /* OK, it looks like this message has the same issuer - * and ID as the SP session we already have active. We - * therefore assume that the user has somehow triggered - * a resend of the message. - * In that case we may as well just redo the previous redirect - * instead of displaying a confusing error message. - */ - SimpleSAML_Logger::info('Duplicate SAML 2 response detected - ignoring the response and redirecting the user to the correct page.'); - if (isset($prevAuth['redirect'])) { - \SimpleSAML\Utils\HTTP::redirectTrustedURL($prevAuth['redirect']); - } - - SimpleSAML_Logger::info('No RelayState or ReturnURL available, cannot redirect.'); - throw new SimpleSAML_Error_Exception('Duplicate assertion received.'); +if ($prevAuth !== null && $prevAuth['id'] === $response->getId() && $prevAuth['issuer'] === $idp) { + /* OK, it looks like this message has the same issuer + * and ID as the SP session we already have active. We + * therefore assume that the user has somehow triggered + * a resend of the message. + * In that case we may as well just redo the previous redirect + * instead of displaying a confusing error message. + */ + SimpleSAML_Logger::info( + 'Duplicate SAML 2 response detected - ignoring the response and redirecting the user to the correct page.' + ); + if (isset($prevAuth['redirect'])) { + \SimpleSAML\Utils\HTTP::redirectTrustedURL($prevAuth['redirect']); + } + + SimpleSAML_Logger::info('No RelayState or ReturnURL available, cannot redirect.'); + throw new SimpleSAML_Error_Exception('Duplicate assertion received.'); } $idpMetadata = array(); $stateId = $response->getInResponseTo(); if (!empty($stateId)) { - /* This is a response to a request we sent earlier. */ - $state = SimpleSAML_Auth_State::loadState($stateId, 'saml:sp:sso'); - - /* Check that the authentication source is correct. */ - assert('array_key_exists("saml:sp:AuthId", $state)'); - if ($state['saml:sp:AuthId'] !== $sourceId) { - throw new SimpleSAML_Error_Exception('The authentication source id in the URL does not match the authentication source which sent the request.'); - } - - /* Check that the issuer is the one we are expecting. */ - assert('array_key_exists("ExpectedIssuer", $state)'); - if ($state['ExpectedIssuer'] !== $idp) { - $idpMetadata = $source->getIdPMetadata($idp); - $idplist = $idpMetadata->getArrayize('IDPList', array()); - if (!in_array($state['ExpectedIssuer'], $idplist)) { - throw new SimpleSAML_Error_Exception('The issuer of the response does not match to the identity provider we sent the request to.'); - } - } + // this is a response to a request we sent earlier + $state = SimpleSAML_Auth_State::loadState($stateId, 'saml:sp:sso'); + + // check that the authentication source is correct + assert('array_key_exists("saml:sp:AuthId", $state)'); + if ($state['saml:sp:AuthId'] !== $sourceId) { + throw new SimpleSAML_Error_Exception( + 'The authentication source id in the URL does not match the authentication source which sent the request.' + ); + } + + // check that the issuer is the one we are expecting + assert('array_key_exists("ExpectedIssuer", $state)'); + if ($state['ExpectedIssuer'] !== $idp) { + $idpMetadata = $source->getIdPMetadata($idp); + $idplist = $idpMetadata->getArrayize('IDPList', array()); + if (!in_array($state['ExpectedIssuer'], $idplist)) { + throw new SimpleSAML_Error_Exception( + 'The issuer of the response does not match to the identity provider we sent the request to.' + ); + } + } } else { - /* This is an unsolicited response. */ - $state = array( - 'saml:sp:isUnsolicited' => TRUE, - 'saml:sp:AuthId' => $sourceId, - 'saml:sp:RelayState' => \SimpleSAML\Utils\HTTP::checkURLAllowed($spMetadata->getString( - 'RelayState', - $response->getRelayState() - ) - ), - ); + // this is an unsolicited response + $state = array( + 'saml:sp:isUnsolicited' => true, + 'saml:sp:AuthId' => $sourceId, + 'saml:sp:RelayState' => \SimpleSAML\Utils\HTTP::checkURLAllowed( + $spMetadata->getString( + 'RelayState', + $response->getRelayState() + ) + ), + ); } -SimpleSAML_Logger::debug('Received SAML2 Response from ' . var_export($idp, TRUE) . '.'); +SimpleSAML_Logger::debug('Received SAML2 Response from '.var_export($idp, true).'.'); if (empty($idpMetadata)) { - $idpMetadata = $source->getIdPmetadata($idp); + $idpMetadata = $source->getIdPmetadata($idp); } try { - $assertions = sspmod_saml_Message::processResponse($spMetadata, $idpMetadata, $response); + $assertions = sspmod_saml_Message::processResponse($spMetadata, $idpMetadata, $response); } catch (sspmod_saml_Error $e) { - /* The status of the response wasn't "success". */ - $e = $e->toException(); - SimpleSAML_Auth_State::throwException($state, $e); + // the status of the response wasn't "success" + $e = $e->toException(); + SimpleSAML_Auth_State::throwException($state, $e); } -$authenticatingAuthority = NULL; -$nameId = NULL; -$sessionIndex = NULL; -$expire = NULL; +$authenticatingAuthority = null; +$nameId = null; +$sessionIndex = null; +$expire = null; $attributes = array(); -$foundAuthnStatement = FALSE; +$foundAuthnStatement = false; foreach ($assertions as $assertion) { - /* Check for duplicate assertion (replay attack). */ - $store = SimpleSAML_Store::getInstance(); - if ($store !== FALSE) { - $aID = $assertion->getId(); - if ($store->get('saml.AssertionReceived', $aID) !== NULL) { - $e = new SimpleSAML_Error_Exception('Received duplicate assertion.'); - SimpleSAML_Auth_State::throwException($state, $e); - } - - $notOnOrAfter = $assertion->getNotOnOrAfter(); - if ($notOnOrAfter === NULL) { - $notOnOrAfter = time() + 24*60*60; - } else { - $notOnOrAfter += 60; /* We allow 60 seconds clock skew, so add it here also. */ - } - - $store->set('saml.AssertionReceived', $aID, TRUE, $notOnOrAfter); - } - - - if ($authenticatingAuthority === NULL) { - $authenticatingAuthority = $assertion->getAuthenticatingAuthority(); - } - if ($nameId === NULL) { - $nameId = $assertion->getNameId(); - } - if ($sessionIndex === NULL) { - $sessionIndex = $assertion->getSessionIndex(); - } - if ($expire === NULL) { - $expire = $assertion->getSessionNotOnOrAfter(); - } - - $attributes = array_merge($attributes, $assertion->getAttributes()); - - if ($assertion->getAuthnInstant() !== NULL) { - /* Assertion contains AuthnStatement, since AuthnInstant is a required attribute. */ - $foundAuthnStatement = TRUE; - } + // check for duplicate assertion (replay attack) + $store = SimpleSAML_Store::getInstance(); + if ($store !== false) { + $aID = $assertion->getId(); + if ($store->get('saml.AssertionReceived', $aID) !== null) { + $e = new SimpleSAML_Error_Exception('Received duplicate assertion.'); + SimpleSAML_Auth_State::throwException($state, $e); + } + + $notOnOrAfter = $assertion->getNotOnOrAfter(); + if ($notOnOrAfter === null) { + $notOnOrAfter = time() + 24 * 60 * 60; + } else { + $notOnOrAfter += 60; // we allow 60 seconds clock skew, so add it here also + } + + $store->set('saml.AssertionReceived', $aID, true, $notOnOrAfter); + } + + + if ($authenticatingAuthority === null) { + $authenticatingAuthority = $assertion->getAuthenticatingAuthority(); + } + if ($nameId === null) { + $nameId = $assertion->getNameId(); + } + if ($sessionIndex === null) { + $sessionIndex = $assertion->getSessionIndex(); + } + if ($expire === null) { + $expire = $assertion->getSessionNotOnOrAfter(); + } + + $attributes = array_merge($attributes, $assertion->getAttributes()); + + if ($assertion->getAuthnInstant() !== null) { + // assertion contains AuthnStatement, since AuthnInstant is a required attribute + $foundAuthnStatement = true; + } } if (!$foundAuthnStatement) { - $e = new SimpleSAML_Error_Exception('No AuthnStatement found in assertion(s).'); - SimpleSAML_Auth_State::throwException($state, $e); + $e = new SimpleSAML_Error_Exception('No AuthnStatement found in assertion(s).'); + SimpleSAML_Auth_State::throwException($state, $e); } -if ($expire !== NULL) { - $logoutExpire = $expire; +if ($expire !== null) { + $logoutExpire = $expire; } else { - /* Just expire the logout associtaion 24 hours into the future. */ - $logoutExpire = time() + 24*60*60; + // just expire the logout association 24 hours into the future + $logoutExpire = time() + 24 * 60 * 60; } -/* Register this session in the logout store. */ +// register this session in the logout store sspmod_saml_SP_LogoutStore::addSession($sourceId, $nameId, $sessionIndex, $logoutExpire); -/* We need to save the NameID and SessionIndex for logout. */ +// we need to save the NameID and SessionIndex for logout $logoutState = array( - 'saml:logout:Type' => 'saml2', - 'saml:logout:IdP' => $idp, - 'saml:logout:NameID' => $nameId, - 'saml:logout:SessionIndex' => $sessionIndex, - ); + 'saml:logout:Type' => 'saml2', + 'saml:logout:IdP' => $idp, + 'saml:logout:NameID' => $nameId, + 'saml:logout:SessionIndex' => $sessionIndex, +); $state['LogoutState'] = $logoutState; $state['saml:AuthenticatingAuthority'] = $authenticatingAuthority; $state['saml:AuthenticatingAuthority'][] = $idp; @@ -201,19 +208,19 @@ $state['PersistentAuthData'][] = 'saml:sp:SessionIndex'; $state['saml:sp:AuthnContext'] = $assertion->getAuthnContext(); $state['PersistentAuthData'][] = 'saml:sp:AuthnContext'; -if ($expire !== NULL) { - $state['Expire'] = $expire; +if ($expire !== null) { + $state['Expire'] = $expire; } // note some information about the authentication, in case we receive the same response again $state['saml:sp:prevAuth'] = array( - 'id' => $response->getId(), - 'issuer' => $idp, + 'id' => $response->getId(), + 'issuer' => $idp, ); if (isset($state['SimpleSAML_Auth_Default.ReturnURL'])) { - $state['saml:sp:prevAuth']['redirect'] = $state['SimpleSAML_Auth_Default.ReturnURL']; + $state['saml:sp:prevAuth']['redirect'] = $state['SimpleSAML_Auth_Default.ReturnURL']; } elseif (isset($state['saml:sp:RelayState'])) { - $state['saml:sp:prevAuth']['redirect'] = $state['saml:sp:RelayState']; + $state['saml:sp:prevAuth']['redirect'] = $state['saml:sp:RelayState']; } $state['PersistentAuthData'][] = 'saml:sp:prevAuth';