Beispiel #1
0
 public function validate(SAML2_Response $response, SAML2_Response_Validation_Result $result)
 {
     $destination = $response->getDestination();
     if (!$this->expectedDestination->equals(new SAML2_Configuration_Destination($destination))) {
         $result->addError(sprintf('Destination in response "%s" does not match the expected destination "%s"', $destination, $this->expectedDestination));
     }
 }
 /**
  * @param SAML2_Response $response
  * @param SimpleSAML_Configuration $idpConfig
  */
 private function addSigns(SAML2_Response $response, SimpleSAML_Configuration $idpConfig)
 {
     $assertions = $response->getAssertions();
     $className = EngineBlock_ApplicationSingleton::getInstance()->getDiContainer()->getMessageUtilClassName();
     // Special case the 'normal' message verification class name so we have IDE support.
     if ($className === 'sspmod_saml_Message') {
         sspmod_saml_Message::addSign($idpConfig, SimpleSAML_Configuration::loadFromArray(array()), $assertions[0]);
         return;
     }
     $className::addSign($idpConfig, SimpleSAML_Configuration::loadFromArray(array()), $assertions[0]);
 }
 public function setUp()
 {
     $this->request = new EngineBlock_Saml2_AuthnRequestAnnotationDecorator(new SAML2_AuthnRequest());
     $assertion = new SAML2_Assertion();
     $assertion->setAttributes(array());
     $response = new SAML2_Response();
     $response->setAssertions(array($assertion));
     $response = new EngineBlock_Saml2_ResponseAnnotationDecorator($response);
     $response->setIntendedNameId('urn:collab:person:example.edu:mock1');
     $this->response = $response;
     $this->serviceProvider = new ServiceProvider('http://sp.example.edu');
     $this->collabPersonId = 'urn:collab:person:example.edu:mock1';
     $this->resolver = new EngineBlock_Test_Saml2_NameIdResolverMock();
 }
 /**
  * @test
  * @group signature
  */
 public function signed_message_with_valid_signature_is_validated_correctly()
 {
     $pattern = SAML2_Utilities_Certificate::CERTIFICATE_PATTERN;
     preg_match($pattern, SAML2_CertificatesMock::PUBLIC_KEY_PEM, $matches);
     $config = new SAML2_Configuration_IdentityProvider(array('certificateData' => $matches[1]));
     $validator = new SAML2_Signature_PublicKeyValidator(new SAML2_SimpleTestLogger(), new SAML2_Certificate_KeyLoader());
     $doc = SAML2_DOMDocumentFactory::fromFile(__DIR__ . '/response.xml');
     $response = new SAML2_Response($doc->firstChild);
     $response->setSignatureKey(SAML2_CertificatesMock::getPrivateKey());
     $response->setCertificates(array(SAML2_CertificatesMock::PUBLIC_KEY_PEM));
     // convert to signed response
     $response = new SAML2_Response($response->toSignedXML());
     $this->assertTrue($validator->canValidate($response, $config), 'Cannot validate the element');
     $this->assertTrue($validator->hasValidSignature($response, $config), 'The signature is not valid');
 }
 public function testMarshalling()
 {
     $response = new SAML2_Response();
     $response->setStatus(array('Code' => 'OurStatusCode', 'SubCode' => 'OurSubStatusCode', 'Message' => 'OurMessageText'));
     $responseElement = $response->toUnsignedXML();
     $statusElements = SAML2_Utils::xpQuery($responseElement, './saml_protocol:Status');
     $this->assertCount(1, $statusElements);
     $statusCodeElements = SAML2_Utils::xpQuery($statusElements[0], './saml_protocol:StatusCode');
     $this->assertCount(1, $statusCodeElements);
     $this->assertEquals('OurStatusCode', $statusCodeElements[0]->getAttribute("Value"));
     $nestedStatusCodeElements = SAML2_Utils::xpQuery($statusCodeElements[0], './saml_protocol:StatusCode');
     $this->assertCount(1, $nestedStatusCodeElements);
     $this->assertEquals('OurSubStatusCode', $nestedStatusCodeElements[0]->getAttribute("Value"));
     $statusMessageElements = SAML2_Utils::xpQuery($statusElements[0], './saml_protocol:StatusMessage');
     $this->assertCount(1, $statusMessageElements);
     $this->assertEquals('OurMessageText', $statusMessageElements[0]->textContent);
 }
 /**
  * @test
  * @group signature
  */
 public function signed_message_with_valid_signature_is_validated_correctly()
 {
     $pattern = SAML2_Utilities_Certificate::CERTIFICATE_PATTERN;
     preg_match($pattern, SAML2_CertificatesMock::PUBLIC_KEY_PEM, $matches);
     $certdata = SAML2_Certificate_X509::createFromCertificateData($matches[1]);
     $fingerprint = $certdata->getFingerprint();
     $fingerprint_retry = $certdata->getFingerprint();
     $this->assertTrue($fingerprint->equals($fingerprint_retry), 'Cached fingerprint does not match original');
     $config = new SAML2_Configuration_IdentityProvider(array('certificateFingerprints' => array($fingerprint->getRaw())));
     $validator = new SAML2_Signature_FingerprintValidator(new SAML2_SimpleTestLogger(), new SAML2_Certificate_FingerprintLoader());
     $doc = SAML2_DOMDocumentFactory::fromFile(__DIR__ . '/response.xml');
     $response = new SAML2_Response($doc->firstChild);
     $response->setSignatureKey(SAML2_CertificatesMock::getPrivateKey());
     $response->setCertificates(array(SAML2_CertificatesMock::PUBLIC_KEY_PEM));
     // convert to signed response
     $response = new SAML2_Response($response->toSignedXML());
     $this->assertTrue($validator->canValidate($response, $config), 'Cannot validate the element');
     $this->assertTrue($validator->hasValidSignature($response, $config), 'The signature is not valid');
 }
 /**
  * @param string $samlMessageXml
  * @param string $class
  * @return SAML_Message
  */
 public function deserialize($samlMessageXml, $class)
 {
     $elementName = $this->getElementForClass($class);
     $document = new DOMDocument();
     $document->loadXML($samlMessageXml);
     $messageDomElement = $document->getElementsByTagNameNs('urn:oasis:names:tc:SAML:2.0:protocol', $elementName)->item(0);
     if ($class === 'SAML2_AuthnRequest') {
         return SAML2_AuthnRequest::fromXML($messageDomElement);
     } else {
         if ($class === 'SAML2_Response') {
             return SAML2_Response::fromXML($messageDomElement);
         }
     }
     throw new EngineBlock_Exception('Unknown message type for deserialization?');
 }
 /**
  * @return EngineBlock_Corto_Module_Bindings
  */
 private function mockBindingsModule()
 {
     $spRequest = new SAML2_AuthnRequest();
     $spRequest->setId('SPREQUEST');
     $spRequest->setIssuer('testSp');
     $spRequest = new EngineBlock_Saml2_AuthnRequestAnnotationDecorator($spRequest);
     $ebRequest = new SAML2_AuthnRequest();
     $ebRequest->setId('EBREQUEST');
     $ebRequest = new EngineBlock_Saml2_AuthnRequestAnnotationDecorator($ebRequest);
     $dummyLog = new Psr\Log\NullLogger();
     $authnRequestRepository = new EngineBlock_Saml2_AuthnRequestSessionRepository($dummyLog);
     $authnRequestRepository->store($spRequest);
     $authnRequestRepository->store($ebRequest);
     $authnRequestRepository->link($ebRequest, $spRequest);
     $assertion = new SAML2_Assertion();
     $assertion->setAttributes(array('urn:org:openconext:corto:internal:sp-entity-id' => array('testSp'), 'urn:mace:dir:attribute-def:cn' => array(null)));
     $responseFixture = new SAML2_Response();
     $responseFixture->setInResponseTo('EBREQUEST');
     $responseFixture->setAssertions(array($assertion));
     $responseFixture = new EngineBlock_Saml2_ResponseAnnotationDecorator($responseFixture);
     $responseFixture->setOriginalIssuer('testIdP');
     // Mock bindings module
     /** @var EngineBlock_Corto_Module_Bindings $bindingsModuleMock */
     $bindingsModuleMock = Phake::mock('EngineBlock_Corto_Module_Bindings');
     Phake::when($bindingsModuleMock)->receiveResponse()->thenReturn($responseFixture);
     return $bindingsModuleMock;
 }
        }
        /* Filter which attribute values we should return. */
        $returnAttributes[$name] = array_intersect($values, $attributes[$name]);
    }
}
/* $returnAttributes contains the attributes we should return. Send them. */
$assertion = new SAML2_Assertion();
$assertion->setIssuer($idpEntityId);
$assertion->setNameId($query->getNameId());
$assertion->setNotBefore(time());
$assertion->setNotOnOrAfter(time() + 5 * 60);
$assertion->setValidAudiences(array($spEntityId));
$assertion->setAttributes($returnAttributes);
$assertion->setAttributeNameFormat($attributeNameFormat);
$sc = new SAML2_XML_saml_SubjectConfirmation();
$sc->Method = SAML2_Const::CM_BEARER;
$sc->SubjectConfirmationData = new SAML2_XML_saml_SubjectConfirmationData();
$sc->SubjectConfirmationData->NotOnOrAfter = time() + 5 * 60;
$sc->SubjectConfirmationData->Recipient = $endpoint;
$sc->SubjectConfirmationData->InResponseTo = $query->getId();
$assertion->setSubjectConfirmation(array($sc));
sspmod_saml_Message::addSign($idpMetadata, $spMetadata, $assertion);
$response = new SAML2_Response();
$response->setRelayState($query->getRelayState());
$response->setDestination($endpoint);
$response->setIssuer($idpEntityId);
$response->setInResponseTo($query->getId());
$response->setAssertions(array($assertion));
sspmod_saml_Message::addSign($idpMetadata, $spMetadata, $response);
$binding = new SAML2_HTTPPost();
$binding->send($response);
Beispiel #10
0
 /**
  * Process a response message.
  *
  * If the response is an error response, we will throw a sspmod_saml2_Error
  * exception with the error.
  *
  * @param SimpleSAML_Configuration $spMetadata  The metadata of the service provider.
  * @param SimpleSAML_Configuration $idpMetadata  The metadata of the identity provider.
  * @param SAML2_Response $response  The response.
  * @return SAML2_Assertion  The assertion in the response, if it is valid.
  */
 public static function processResponse(SimpleSAML_Configuration $spMetadata, SimpleSAML_Configuration $idpMetadata, SAML2_Response $response)
 {
     if (!$response->isSuccess()) {
         throw self::getResponseError($response);
     }
     /*
      * When we get this far, the response itself is valid.
      * We only need to check signatures and conditions of the response.
      */
     $assertion = $response->getAssertions();
     if (empty($assertion)) {
         throw new SimpleSAML_Error_Exception('No assertions found in response from IdP.');
     } elseif (count($assertion) > 1) {
         throw new SimpleSAML_Error_Exception('More than one assertion found in response from IdP.');
     }
     $assertion = $assertion[0];
     $assertion = self::decryptAssertion($idpMetadata, $spMetadata, $assertion);
     if (!self::checkSign($idpMetadata, $assertion)) {
         if (!self::checkSign($idpMetadata, $response)) {
             throw new SimpleSAML_Error_Exception('Neither the assertion nor the response was signed.');
         }
     }
     /* At least one valid signature found. */
     /* Make sure that some fields in the assertion matches the same fields in the message. */
     $asrtInResponseTo = $assertion->getInResponseTo();
     $msgInResponseTo = $response->getInResponseTo();
     if ($asrtInResponseTo !== NULL && $msgInResponseTo !== NULL) {
         if ($asrtInResponseTo !== $msgInResponseTo) {
             throw new SimpleSAML_Error_Exception('InResponseTo in assertion did not match InResponseTo in message.');
         }
     }
     $asrtDestination = $assertion->getDestination();
     $msgDestination = $response->getDestination();
     if ($asrtDestination !== NULL && $msgDestination !== NULL) {
         if ($asrtDestination !== $msgDestination) {
             throw new SimpleSAML_Error_Exception('Destination in assertion did not match Destination in message.');
         }
     }
     /* Check various properties of the assertion. */
     $notBefore = $assertion->getNotBefore();
     if ($notBefore > time() + 60) {
         throw new SimpleSAML_Error_Exception('Received an assertion that is valid in the future. Check clock synchronization on IdP and SP.');
     }
     $notOnOrAfter = $assertion->getNotOnOrAfter();
     if ($notOnOrAfter <= time() - 60) {
         throw new SimpleSAML_Error_Exception('Received an assertion that has expired. Check clock synchronization on IdP and SP.');
     }
     $sessionNotOnOrAfter = $assertion->getSessionNotOnOrAfter();
     if ($sessionNotOnOrAfter !== NULL && $sessionNotOnOrAfter <= time() - 60) {
         throw new SimpleSAML_Error_Exception('Received an assertion with a session that has expired. Check clock synchronization on IdP and SP.');
     }
     $destination = $assertion->getDestination();
     $currentURL = SimpleSAML_Utilities::selfURLNoQuery();
     if ($destination !== $currentURL) {
         throw new Exception('Recipient in assertion doesn\'t match the current URL. Recipient is "' . $destination . '", current URL is "' . $currentURL . '".');
     }
     $validAudiences = $assertion->getValidAudiences();
     if ($validAudiences !== NULL) {
         $spEntityId = $spMetadata->getString('entityid');
         if (!in_array($spEntityId, $validAudiences, TRUE)) {
             $candidates = '[' . implode('], [', $validAudiences) . ']';
             throw new SimpleSAML_Error_Exception('This SP [' . $spEntityId . ']  is not a valid audience for the assertion. Candidates were: ' . $candidates);
         }
     }
     /* As far as we can tell, the assertion is valid. */
     /* Maybe we need to base64 decode the attributes in the assertion? */
     if ($idpMetadata->getBoolean('base64attributes', FALSE)) {
         $attributes = $assertion->getAttributes();
         $newAttributes = array();
         foreach ($attributes as $name => $values) {
             $newAttributes[$name] = array();
             foreach ($values as $value) {
                 foreach (explode('_', $value) as $v) {
                     $newAttributes[$name][] = base64_decode($v);
                 }
             }
         }
         $assertion->setAttributes($newAttributes);
     }
     /* Decrypt the NameID element if it is encrypted. */
     if ($assertion->isNameIdEncrypted()) {
         try {
             $key = self::getDecryptionKey($idpMetadata, $spMetadata);
         } catch (Exception $e) {
             throw new SimpleSAML_Error_Exception('Error decrypting NameID: ' . $e->getMessage());
         }
         $assertion->decryptNameId($key);
     }
     return $assertion;
 }
 /**
  * Determines if a Response carries an encrypted assertion.
  *
  * @param SAML2_Response $sspResponse
  * @return bool
  */
 private function hasEncryptedAssertion(SAML2_Response $sspResponse)
 {
     $hasEncryptedAssertion = false;
     foreach ($sspResponse->getAssertions() as $assertion) {
         if ($assertion instanceof SAML2_EncryptedAssertion) {
             $hasEncryptedAssertion = true;
             break;
         }
     }
     return $hasEncryptedAssertion;
 }
Beispiel #12
0
 private function buildResponse($returnAttributes)
 {
     /* SubjectConfirmation */
     $sc = new SAML2_XML_saml_SubjectConfirmation();
     $sc->Method = SAML2_Const::CM_BEARER;
     $sc->SubjectConfirmationData = new SAML2_XML_saml_SubjectConfirmationData();
     $sc->SubjectConfirmationData->NotBefore = time();
     $sc->SubjectConfirmationData->NotOnOrAfter = time() + $this->config->getInteger('validFor');
     $sc->SubjectConfirmationData->InResponseTo = $this->query->getId();
     $assertion = new SAML2_Assertion();
     $assertion->setSubjectConfirmation(array($sc));
     $assertion->setIssuer($this->aaEntityId);
     $assertion->setNameId($this->query->getNameId());
     $assertion->setNotBefore(time());
     $assertion->setNotOnOrAfter(time() + $this->config->getInteger('validFor'));
     $assertion->setValidAudiences(array($this->spEntityId));
     $assertion->setAttributes($returnAttributes);
     $assertion->setAttributeNameFormat($this->attributeNameFormat);
     if ($this->signAssertion) {
         sspmod_saml_Message::addSign($this->aaMetadata, $this->spMetadata, $assertion);
     }
     /* The Response */
     $response = new SAML2_Response();
     $response->setRelayState($this->query->getRelayState());
     $response->setIssuer($this->aaEntityId);
     $response->setInResponseTo($this->query->getId());
     $response->setAssertions(array($assertion));
     if ($this->signResponse) {
         sspmod_saml_Message::addSign($this->aaMetadata, $this->spMetadata, $response);
     }
     return $response;
 }
 private static function combine_response(array $values, \SAML2_Response $response = NULL)
 {
     if (!isset($response)) {
         $response = new \SAML2_Response();
     }
     $presented_assertions = $response->getAssertions();
     $assertion = empty($presented_assertions) ? new \SAML2_Assertion() : $presented_assertions[0];
     if (self::original_spid_isset($values)) {
         $response->setInResponseTo($values['InResponseTo']);
     }
     if (array_key_exists('ResponseID', $values)) {
         $response->setId($values['ResponseID']);
     }
     if (array_key_exists('AssertionID', $values)) {
         $assertion->setId($values['AssertionID']);
     }
     if (array_key_exists('Issuer', $values)) {
         $response->setIssuer($values['Issuer']);
         $assertion->setIssuer($values['Issuer']);
     }
     if (array_key_exists('NameID', $values)) {
         $assertion->setNameId(self::build_name_id($values['NameID']));
     }
     $not_on_or_after_time = time();
     if (array_key_exists('AllowedTimeDelta', $values)) {
         $not_on_or_after_time += $values['AllowedTimeDelta'];
         $assertion->setNotBefore(time() - $values['AllowedTimeDelta']);
         $assertion->setNotOnOrAfter($not_on_or_after_time);
     } else {
         $not_on_or_after_time += DEFAULT_RESPONSE_TIME_DELTA;
     }
     if (array_key_exists('Audience', $values)) {
         $assertion->setValidAudiences(array($values['Audience']));
     }
     if (array_key_exists('Attributes', $values)) {
         $assertion->setAttributes($values['Attributes']);
     }
     $assertion->setAuthnInstant(time());
     if (array_key_exists('AuthnContextClassRef', $values)) {
         $assertion->setAuthnContextClassRef($values['AuthnContextClassRef']);
     }
     if (array_key_exists('SessionIndex', $values)) {
         $assertion->setSessionIndex($values['SessionIndex']);
     }
     if (self::original_spid_isset($values) || array_key_exists('Destination', $values)) {
         $original_confirmations = $assertion->getSubjectConfirmation();
         $confirmation = NULL;
         if (empty($original_confirmations)) {
             $confirmation = new \SAML2_XML_saml_SubjectConfirmation();
             $confirmation->Method = SAML_CONFIGURATION_METHOD;
         } else {
             $confirmation = $original_confirmations[0];
         }
         $original_data = $confirmation->SubjectConfirmationData;
         $data = NULL;
         if (empty($original_data)) {
             $data = new \SAML2_XML_saml_SubjectConfirmationData();
             $data->NotOnOrAfter = $not_on_or_after_time;
         } else {
             $data = $original_data;
             if (empty($data->NotOnOrAfter)) {
                 $data->NotOnOrAfter = $not_on_or_after_time;
             }
         }
         if (array_key_exists('Destination', $values)) {
             $data->Recipient = $values['Destination'];
         }
         if (self::original_spid_isset($values)) {
             $data->InResponseTo = $values['InResponseTo'];
         }
         $confirmation->SubjectConfirmationData = $data;
         $assertion->setSubjectConfirmation(array($confirmation));
     }
     if (array_key_exists('Destination', $values)) {
         $response->setDestination($values['Destination']);
     }
     if (self::need_sign($values)) {
         if (array_key_exists('SHA256KeyFile', $values)) {
             $ekey = new \XMLSecurityKey(\XMLSecurityKey::RSA_SHA256, array('type' => 'private'));
             $ekey->loadKey($values['SHA256KeyFile'], true);
             if (self::need_sign_attributes($values)) {
                 $assertion->setSignatureKey($ekey);
             }
             if (self::need_sign_message($values)) {
                 $response->setSignatureKey($ekey);
             }
         }
         if (array_key_exists('SHA256CertFile', $values)) {
             $certifictaes = array(file_get_contents($values['SHA256CertFile']));
             if (self::need_sign_attributes($values)) {
                 $assertion->setCertificates($certifictaes);
             }
             if (self::need_sign_message($values)) {
                 $response->setCertificates($certifictaes);
             }
         }
     }
     $response->setAssertions(array($assertion));
     return $response;
 }
 /**
  * @return SAML2_Response
  */
 private function getSignedResponseWithSignedAssertion()
 {
     $doc = new DOMDocument();
     $doc->load(__DIR__ . '/response.xml');
     $response = new SAML2_Response($doc->firstChild);
     $response->setSignatureKey(SAML2_CertificatesMock::getPrivateKey());
     $response->setCertificates(array(SAML2_CertificatesMock::PUBLIC_KEY_PEM));
     $assertions = $response->getAssertions();
     $assertion = $assertions[0];
     $assertion->setSignatureKey(SAML2_CertificatesMock::getPrivateKey());
     $assertion->setCertificates(array(SAML2_CertificatesMock::PUBLIC_KEY_PEM));
     return new SAML2_Response($response->toSignedXML());
 }
 /**
  * @return SAML2_Assertion[]|SAML2_EncryptedAssertion[]
  */
 public function getAssertions()
 {
     return $this->sspMessage->getAssertions();
 }
Beispiel #16
0
 /**
  * Process an assertion in a response.
  *
  * Will throw an exception if it is invalid.
  *
  * @param SimpleSAML_Configuration $spMetadata  The metadata of the service provider.
  * @param SimpleSAML_Configuration $idpMetadata  The metadata of the identity provider.
  * @param SAML2_Response $response  The response containing the assertion.
  * @param SAML2_Assertion|SAML2_EncryptedAssertion $assertion  The assertion.
  * @param bool $responseSigned  Whether the response is signed.
  * @return SAML2_Assertion  The assertion, if it is valid.
  */
 private static function processAssertion(SimpleSAML_Configuration $spMetadata, SimpleSAML_Configuration $idpMetadata, SAML2_Response $response, $assertion, $responseSigned)
 {
     assert('$assertion instanceof SAML2_Assertion || $assertion instanceof SAML2_EncryptedAssertion');
     assert('is_bool($responseSigned)');
     $assertion = self::decryptAssertion($idpMetadata, $spMetadata, $assertion);
     if (!self::checkSign($idpMetadata, $assertion)) {
         if (!$responseSigned) {
             throw new SimpleSAML_Error_Exception('Neither the assertion nor the response was signed.');
         }
     }
     /* At least one valid signature found. */
     $currentURL = \SimpleSAML\Utils\HTTP::getSelfURLNoQuery();
     /* Check various properties of the assertion. */
     $notBefore = $assertion->getNotBefore();
     if ($notBefore !== NULL && $notBefore > time() + 60) {
         throw new SimpleSAML_Error_Exception('Received an assertion that is valid in the future. Check clock synchronization on IdP and SP.');
     }
     $notOnOrAfter = $assertion->getNotOnOrAfter();
     if ($notOnOrAfter !== NULL && $notOnOrAfter <= time() - 60) {
         throw new SimpleSAML_Error_Exception('Received an assertion that has expired. Check clock synchronization on IdP and SP.');
     }
     $sessionNotOnOrAfter = $assertion->getSessionNotOnOrAfter();
     if ($sessionNotOnOrAfter !== NULL && $sessionNotOnOrAfter <= time() - 60) {
         throw new SimpleSAML_Error_Exception('Received an assertion with a session that has expired. Check clock synchronization on IdP and SP.');
     }
     $validAudiences = $assertion->getValidAudiences();
     if ($validAudiences !== NULL) {
         $spEntityId = $spMetadata->getString('entityid');
         if (!in_array($spEntityId, $validAudiences, TRUE)) {
             $candidates = '[' . implode('], [', $validAudiences) . ']';
             throw new SimpleSAML_Error_Exception('This SP [' . $spEntityId . ']  is not a valid audience for the assertion. Candidates were: ' . $candidates);
         }
     }
     $found = FALSE;
     $lastError = 'No SubjectConfirmation element in Subject.';
     $validSCMethods = array(SAML2_Const::CM_BEARER, SAML2_Const::CM_HOK, SAML2_Const::CM_VOUCHES);
     foreach ($assertion->getSubjectConfirmation() as $sc) {
         if (!in_array($sc->Method, $validSCMethods)) {
             $lastError = 'Invalid Method on SubjectConfirmation: ' . var_export($sc->Method, TRUE);
             continue;
         }
         /* Is SSO with HoK enabled? IdP remote metadata overwrites SP metadata configuration. */
         $hok = $idpMetadata->getBoolean('saml20.hok.assertion', NULL);
         if ($hok === NULL) {
             $hok = $spMetadata->getBoolean('saml20.hok.assertion', FALSE);
         }
         if ($sc->Method === SAML2_Const::CM_BEARER && $hok) {
             $lastError = 'Bearer SubjectConfirmation received, but Holder-of-Key SubjectConfirmation needed';
             continue;
         }
         if ($sc->Method === SAML2_Const::CM_HOK && !$hok) {
             $lastError = 'Holder-of-Key SubjectConfirmation received, but the Holder-of-Key profile is not enabled.';
             continue;
         }
         $scd = $sc->SubjectConfirmationData;
         if ($sc->Method === SAML2_Const::CM_HOK) {
             /* Check HoK Assertion */
             if (\SimpleSAML\Utils\HTTP::isHTTPS() === FALSE) {
                 $lastError = 'No HTTPS connection, but required for Holder-of-Key SSO';
                 continue;
             }
             if (isset($_SERVER['SSL_CLIENT_CERT']) && empty($_SERVER['SSL_CLIENT_CERT'])) {
                 $lastError = 'No client certificate provided during TLS Handshake with SP';
                 continue;
             }
             /* Extract certificate data (if this is a certificate). */
             $clientCert = $_SERVER['SSL_CLIENT_CERT'];
             $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
             if (!preg_match($pattern, $clientCert, $matches)) {
                 $lastError = 'Error while looking for client certificate during TLS handshake with SP, the client certificate does not ' . 'have the expected structure';
                 continue;
             }
             /* We have a valid client certificate from the browser. */
             $clientCert = str_replace(array("\r", "\n", " "), '', $matches[1]);
             foreach ($scd->info as $thing) {
                 if ($thing instanceof SAML2_XML_ds_KeyInfo) {
                     $keyInfo[] = $thing;
                 }
             }
             if (count($keyInfo) != 1) {
                 $lastError = 'Error validating Holder-of-Key assertion: Only one <ds:KeyInfo> element in <SubjectConfirmationData> allowed';
                 continue;
             }
             foreach ($keyInfo[0]->info as $thing) {
                 if ($thing instanceof SAML2_XML_ds_X509Data) {
                     $x509data[] = $thing;
                 }
             }
             if (count($x509data) != 1) {
                 $lastError = 'Error validating Holder-of-Key assertion: Only one <ds:X509Data> element in <ds:KeyInfo> within <SubjectConfirmationData> allowed';
                 continue;
             }
             foreach ($x509data[0]->data as $thing) {
                 if ($thing instanceof SAML2_XML_ds_X509Certificate) {
                     $x509cert[] = $thing;
                 }
             }
             if (count($x509cert) != 1) {
                 $lastError = 'Error validating Holder-of-Key assertion: Only one <ds:X509Certificate> element in <ds:X509Data> within <SubjectConfirmationData> allowed';
                 continue;
             }
             $HoKCertificate = $x509cert[0]->certificate;
             if ($HoKCertificate !== $clientCert) {
                 $lastError = 'Provided client certificate does not match the certificate bound to the Holder-of-Key assertion';
                 continue;
             }
         }
         if ($scd->NotBefore && $scd->NotBefore > time() + 60) {
             $lastError = 'NotBefore in SubjectConfirmationData is in the future: ' . $scd->NotBefore;
             continue;
         }
         if ($scd->NotOnOrAfter && $scd->NotOnOrAfter <= time() - 60) {
             $lastError = 'NotOnOrAfter in SubjectConfirmationData is in the past: ' . $scd->NotOnOrAfter;
             continue;
         }
         if ($scd->Recipient !== NULL && $scd->Recipient !== $currentURL) {
             $lastError = 'Recipient in SubjectConfirmationData does not match the current URL. Recipient is ' . var_export($scd->Recipient, TRUE) . ', current URL is ' . var_export($currentURL, TRUE) . '.';
             continue;
         }
         if ($scd->InResponseTo !== NULL && $response->getInResponseTo() !== NULL && $scd->InResponseTo !== $response->getInResponseTo()) {
             $lastError = 'InResponseTo in SubjectConfirmationData does not match the Response. Response has ' . var_export($response->getInResponseTo(), TRUE) . ', SubjectConfirmationData has ' . var_export($scd->InResponseTo, TRUE) . '.';
             continue;
         }
         $found = TRUE;
         break;
     }
     if (!$found) {
         throw new SimpleSAML_Error_Exception('Error validating SubjectConfirmation in Assertion: ' . $lastError);
     }
     /* As far as we can tell, the assertion is valid. */
     /* Maybe we need to base64 decode the attributes in the assertion? */
     if ($idpMetadata->getBoolean('base64attributes', FALSE)) {
         $attributes = $assertion->getAttributes();
         $newAttributes = array();
         foreach ($attributes as $name => $values) {
             $newAttributes[$name] = array();
             foreach ($values as $value) {
                 foreach (explode('_', $value) as $v) {
                     $newAttributes[$name][] = base64_decode($v);
                 }
             }
         }
         $assertion->setAttributes($newAttributes);
     }
     /* Decrypt the NameID element if it is encrypted. */
     if ($assertion->isNameIdEncrypted()) {
         try {
             $keys = self::getDecryptionKeys($idpMetadata, $spMetadata);
         } catch (Exception $e) {
             throw new SimpleSAML_Error_Exception('Error decrypting NameID: ' . $e->getMessage());
         }
         $blacklist = self::getBlacklistedAlgorithms($idpMetadata, $spMetadata);
         $lastException = NULL;
         foreach ($keys as $i => $key) {
             try {
                 $assertion->decryptNameId($key, $blacklist);
                 SimpleSAML_Logger::debug('Decryption with key #' . $i . ' succeeded.');
                 $lastException = NULL;
                 break;
             } catch (Exception $e) {
                 SimpleSAML_Logger::debug('Decryption with key #' . $i . ' failed with exception: ' . $e->getMessage());
                 $lastException = $e;
             }
         }
         if ($lastException !== NULL) {
             throw $lastException;
         }
     }
     return $assertion;
 }
Beispiel #17
0
 /**
  * @param SAML2_Response $response
  *
  * @return SAML2_Assertion[]
  */
 private function processAssertions(SAML2_Response $response)
 {
     $assertions = $response->getAssertions();
     if (empty($assertions)) {
         throw new SAML2_Response_Exception_NoAssertionsFoundException('No assertions found in response from IdP.');
     }
     if (!$this->responseIsSigned) {
         foreach ($assertions as $assertion) {
             if (!$assertion->getWasSignedAtConstruction()) {
                 throw new SAML2_Response_Exception_UnsignedResponseException('Both the response and the assertion it containes are not signed.');
             }
         }
     }
     return $this->assertionProcessor->processAssertions($assertions);
 }
 private function mockGlobals()
 {
     $_POST['ID'] = 'test';
     $_POST['consent'] = 'yes';
     $assertion = new SAML2_Assertion();
     $assertion->setAttributes(array('urn:mace:dir:attribute-def:mail' => '*****@*****.**'));
     $spRequest = new SAML2_AuthnRequest();
     $spRequest->setId('SPREQUEST');
     $spRequest->setIssuer('https://sp.example.edu');
     $spRequest = new EngineBlock_Saml2_AuthnRequestAnnotationDecorator($spRequest);
     $ebRequest = new SAML2_AuthnRequest();
     $ebRequest->setId('EBREQUEST');
     $ebRequest = new EngineBlock_Saml2_AuthnRequestAnnotationDecorator($ebRequest);
     $dummySessionLog = new Psr\Log\NullLogger();
     $authnRequestRepository = new EngineBlock_Saml2_AuthnRequestSessionRepository($dummySessionLog);
     $authnRequestRepository->store($spRequest);
     $authnRequestRepository->store($ebRequest);
     $authnRequestRepository->link($ebRequest, $spRequest);
     $sspResponse = new SAML2_Response();
     $sspResponse->setInResponseTo('EBREQUEST');
     $sspResponse->setAssertions(array($assertion));
     $_SESSION['consent']['test']['response'] = new EngineBlock_Saml2_ResponseAnnotationDecorator($sspResponse);
 }
 protected function _createBaseResponse(EngineBlock_Saml2_AuthnRequestAnnotationDecorator $request)
 {
     if ($request->getVoContext() && $request->isVoContextExplicit()) {
         $this->setVirtualOrganisationContext($request->getVoContext());
     }
     if ($keyId = $request->getKeyId()) {
         $this->setKeyId($keyId);
     }
     $requestWasUnsolicited = $request->isUnsolicited();
     $response = new SAML2_Response();
     /** @var SAML2_AuthnRequest $request */
     $response->setRelayState($request->getRelayState());
     $response->setId($this->getNewId(IdFrame::ID_USAGE_SAML2_RESPONSE));
     $response->setIssueInstant(time());
     if (!$requestWasUnsolicited) {
         $response->setInResponseTo($request->getId());
     }
     $response->setDestination($request->getIssuer());
     $response->setIssuer($this->getUrl('idpMetadataService', $request->getIssuer(), $request));
     $acs = $this->getRequestAssertionConsumer($request);
     $response->setDestination($acs->location);
     $response->setStatus(array('Code' => SAML2_Const::STATUS_SUCCESS));
     $response = new EngineBlock_Saml2_ResponseAnnotationDecorator($response);
     $response->setDeliverByBinding($acs->binding);
     return $response;
 }
Beispiel #20
0
 public function validate(SAML2_Response $response, SAML2_Response_Validation_Result $result)
 {
     if (!$response->isSuccess()) {
         $result->addError($this->buildMessage($response->getStatus()));
     }
 }
Beispiel #21
0
 /**
  * Process an assertion in a response.
  *
  * Will throw an exception if it is invalid.
  *
  * @param SimpleSAML_Configuration $spMetadata  The metadata of the service provider.
  * @param SimpleSAML_Configuration $idpMetadata  The metadata of the identity provider.
  * @param SAML2_Response $response  The response containing the assertion.
  * @param SAML2_Assertion|SAML2_EncryptedAssertion $assertion  The assertion.
  * @param bool $responseSigned  Whether the response is signed.
  * @return SAML2_Assertion  The assertion, if it is valid.
  */
 private static function processAssertion(SimpleSAML_Configuration $spMetadata, SimpleSAML_Configuration $idpMetadata, SAML2_Response $response, $assertion, $responseSigned)
 {
     assert('$assertion instanceof SAML2_Assertion || $assertion instanceof SAML2_EncryptedAssertion');
     assert('is_bool($responseSigned)');
     $assertion = self::decryptAssertion($idpMetadata, $spMetadata, $assertion);
     if (!self::checkSign($idpMetadata, $assertion)) {
         if (!$responseSigned) {
             throw new SimpleSAML_Error_Exception('Neither the assertion nor the response was signed.');
         }
     }
     /* At least one valid signature found. */
     $currentURL = SimpleSAML_Utilities::selfURLNoQuery();
     /* Check various properties of the assertion. */
     $notBefore = $assertion->getNotBefore();
     if ($notBefore > time() + 60) {
         throw new SimpleSAML_Error_Exception('Received an assertion that is valid in the future. Check clock synchronization on IdP and SP.');
     }
     $notOnOrAfter = $assertion->getNotOnOrAfter();
     if ($notOnOrAfter <= time() - 60) {
         throw new SimpleSAML_Error_Exception('Received an assertion that has expired. Check clock synchronization on IdP and SP.');
     }
     $sessionNotOnOrAfter = $assertion->getSessionNotOnOrAfter();
     if ($sessionNotOnOrAfter !== NULL && $sessionNotOnOrAfter <= time() - 60) {
         throw new SimpleSAML_Error_Exception('Received an assertion with a session that has expired. Check clock synchronization on IdP and SP.');
     }
     $validAudiences = $assertion->getValidAudiences();
     if ($validAudiences !== NULL) {
         $spEntityId = $spMetadata->getString('entityid');
         if (!in_array($spEntityId, $validAudiences, TRUE)) {
             $candidates = '[' . implode('], [', $validAudiences) . ']';
             throw new SimpleSAML_Error_Exception('This SP [' . $spEntityId . ']  is not a valid audience for the assertion. Candidates were: ' . $candidates);
         }
     }
     $found = FALSE;
     $lastError = 'No SubjectConfirmation element in Subject.';
     foreach ($assertion->getSubjectConfirmation() as $sc) {
         if ($sc->Method !== SAML2_Const::CM_BEARER) {
             $lastError = 'Invalid Method on SubjectConfirmation: ' . var_export($sc->Method, TRUE);
             continue;
         }
         $scd = $sc->SubjectConfirmationData;
         if ($scd->NotBefore && $scd->NotBefore > time() + 60) {
             $lastError = 'NotBefore in SubjectConfirmationData is in the future: ' . $scd->NotBefore;
             continue;
         }
         if ($scd->NotOnOrAfter && $scd->NotOnOrAfter <= time() - 60) {
             $lastError = 'NotOnOrAfter in SubjectConfirmationData is in the past: ' . $scd->NotOnOrAfter;
             continue;
         }
         if ($scd->Recipient !== NULL && $scd->Recipient !== $currentURL) {
             $lastError = 'Recipient in SubjectConfirmationData does not match the current URL. Recipient is ' . var_export($scd->Recipient, TRUE) . ', current URL is ' . var_export($currentURL, TRUE) . '.';
             continue;
         }
         if ($scd->InResponseTo !== NULL && $response->getInResponseTo() !== NULL && $scd->InResponseTo !== $response->getInResponseTo()) {
             $lastError = 'InResponseTo in SubjectConfirmationData does not match the Response. Response has ' . var_export($response->getInResponseTo(), TRUE) . ', SubjectConfirmationData has ' . var_export($scd->InResponseTo, TRUE) . '.';
             continue;
         }
         $found = TRUE;
         break;
     }
     if (!$found) {
         throw new SimpleSAML_Error_Exception('Error validating SubjectConfirmation in Assertion: ' . $lastError);
     }
     /* As far as we can tell, the assertion is valid. */
     /* Maybe we need to base64 decode the attributes in the assertion? */
     if ($idpMetadata->getBoolean('base64attributes', FALSE)) {
         $attributes = $assertion->getAttributes();
         $newAttributes = array();
         foreach ($attributes as $name => $values) {
             $newAttributes[$name] = array();
             foreach ($values as $value) {
                 foreach (explode('_', $value) as $v) {
                     $newAttributes[$name][] = base64_decode($v);
                 }
             }
         }
         $assertion->setAttributes($newAttributes);
     }
     /* Decrypt the NameID element if it is encrypted. */
     if ($assertion->isNameIdEncrypted()) {
         try {
             $keys = self::getDecryptionKeys($idpMetadata, $spMetadata);
         } catch (Exception $e) {
             throw new SimpleSAML_Error_Exception('Error decrypting NameID: ' . $e->getMessage());
         }
         $lastException = NULL;
         foreach ($keys as $i => $key) {
             try {
                 $assertion->decryptNameId($key);
                 SimpleSAML_Logger::debug('Decryption with key #' . $i . ' succeeded.');
                 $lastException = NULL;
                 break;
             } catch (Exception $e) {
                 SimpleSAML_Logger::debug('Decryption with key #' . $i . ' failed with exception: ' . $e->getMessage());
                 $lastException = $e;
             }
         }
         if ($lastException !== NULL) {
             throw $lastException;
         }
     }
     return $assertion;
 }
Beispiel #22
0
 /**
  * Build a authentication response based on information in the metadata.
  *
  * @param SimpleSAML_Configuration $idpMetadata  The metadata of the IdP.
  * @param SimpleSAML_Configuration $spMetadata  The metadata of the SP.
  * @param string $consumerURL  The Destination URL of the response.
  */
 private static function buildResponse(SimpleSAML_Configuration $idpMetadata, SimpleSAML_Configuration $spMetadata, $consumerURL)
 {
     $signResponse = $spMetadata->getBoolean('saml20.sign.response', NULL);
     if ($signResponse === NULL) {
         $signResponse = $idpMetadata->getBoolean('saml20.sign.response', TRUE);
     }
     $r = new SAML2_Response();
     $r->setIssuer($idpMetadata->getString('entityid'));
     $r->setDestination($consumerURL);
     if ($signResponse) {
         sspmod_saml_Message::addSign($idpMetadata, $spMetadata, $r);
     }
     return $r;
 }
Beispiel #23
0
    public function testLoop()
    {
        $fixtureResponseDom = new DOMDocument();
        $fixtureResponseDom->loadXML(<<<XML
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
                ID="s2a0da3504aff978b0f8c80f6a62c713c4a2f64c5b"
                InResponseTo="_bec424fa5103428909a30ff1e31168327f79474984"
                Version="2.0"
                IssueInstant="2007-12-10T11:39:48Z"
                Destination="http://moodle.bridge.feide.no/simplesaml/saml2/sp/AssertionConsumerService.php">
    <saml:Issuer>max.feide.no</saml:Issuer>
    <samlp:Extensions>
        <myns:AttributeList xmlns:myns="urn:mynamespace">
          <myns:Attribute name="UserName" value=""/>
      </myns:AttributeList>
    </samlp:Extensions>
    <samlp:Status>
        <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
    </samlp:Status>
    <saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xmlns:xs="http://www.w3.org/2001/XMLSchema"
                    Version="2.0"
                    ID="s2b7afe8e21a0910d027dfbc94ec4b862e1fbbd9ab"
                    IssueInstant="2007-12-10T11:39:48Z">
        <saml:Issuer>max.feide.no</saml:Issuer>
        <saml:Subject>
            <saml:NameID NameQualifier="max.feide.no" SPNameQualifier="urn:mace:feide.no:services:no.feide.moodle" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">UB/WJAaKAPrSHbqlbcKWu7JktcKY</saml:NameID>
            <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                <saml:SubjectConfirmationData NotOnOrAfter="2007-12-10T19:39:48Z" InResponseTo="_bec424fa5103428909a30ff1e31168327f79474984" Recipient="http://moodle.bridge.feide.no/simplesaml/saml2/sp/AssertionConsumerService.php"/>
            </saml:SubjectConfirmation>
        </saml:Subject>
        <saml:Conditions NotBefore="2007-12-10T11:29:48Z" NotOnOrAfter="2007-12-10T19:39:48Z">
            <saml:AudienceRestriction>
                <saml:Audience>urn:mace:feide.no:services:no.feide.moodle</saml:Audience>
            </saml:AudienceRestriction>
        </saml:Conditions>
        <saml:AuthnStatement AuthnInstant="2007-12-10T11:39:48Z" SessionIndex="s259fad9cad0cf7d2b3b68f42b17d0cfa6668e0201">
            <saml:AuthnContext>
                <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>
            </saml:AuthnContext>
        </saml:AuthnStatement>
        <saml:AttributeStatement>
            <saml:Attribute Name="givenName">
                <saml:AttributeValue xsi:type="xs:string">RkVJREUgVGVzdCBVc2VyIChnaXZlbk5hbWUpIMO4w6bDpcOYw4bDhQ==</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="eduPersonPrincipalName">
                <saml:AttributeValue xsi:type="xs:string">dGVzdEBmZWlkZS5ubw==</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="o">
                <saml:AttributeValue xsi:type="xs:string">VU5JTkVUVA==</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="ou">
                <saml:AttributeValue xsi:type="xs:string">VU5JTkVUVA==</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="eduPersonOrgDN">
                <saml:AttributeValue xsi:type="xs:string">ZGM9dW5pbmV0dCxkYz1ubw==</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="eduPersonPrimaryAffiliation">
                <saml:AttributeValue xsi:type="xs:string">c3R1ZGVudA==</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="mail">
                <saml:AttributeValue xsi:type="xs:string">bW9yaWEtc3VwcG9ydEB1bmluZXR0Lm5v</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="preferredLanguage">
                <saml:AttributeValue xsi:type="xs:string">bm8=</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="eduPersonOrgUnitDN">
                <saml:AttributeValue xsi:type="xs:string">b3U9dW5pbmV0dCxvdT1vcmdhbml6YXRpb24sZGM9dW5pbmV0dCxkYz1ubw==</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="sn">
                <saml:AttributeValue xsi:type="xs:string">RkVJREUgVGVzdCBVc2VyIChzbikgw7jDpsOlw5jDhsOF</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="cn">
                <saml:AttributeValue xsi:type="xs:string">RkVJREUgVGVzdCBVc2VyIChjbikgw7jDpsOlw5jDhsOF</saml:AttributeValue>
            </saml:Attribute>
            <saml:Attribute Name="eduPersonAffiliation">
                <saml:AttributeValue xsi:type="xs:string">ZW1wbG95ZWU=_c3RhZmY=_c3R1ZGVudA==</saml:AttributeValue>
            </saml:Attribute>
        </saml:AttributeStatement>
    </saml:Assertion>
</samlp:Response>
XML
);
        $request = new SAML2_Response($fixtureResponseDom->firstChild);
        $requestXml = $requestDocument = $request->toUnsignedXML()->ownerDocument->C14N();
        $fixtureXml = $fixtureResponseDom->C14N();
        $this->assertXmlStringEqualsXmlString($fixtureXml, $requestXml, 'Response after Unmarshalling and re-marshalling remains the same');
    }