sspmod_saml_Message::processAssertion PHP Method

processAssertion() private static method

Will throw an exception if it is invalid.
private static processAssertion ( SimpleSAML_Configuration $spMetadata, SimpleSAML_Configuration $idpMetadata, SAML2\Response $response, Assertion | SAML2\EncryptedAssertion $assertion, boolean $responseSigned ) : Assertion
$spMetadata SimpleSAML_Configuration The metadata of the service provider.
$idpMetadata SimpleSAML_Configuration The metadata of the identity provider.
$response SAML2\Response The response containing the assertion.
$assertion SAML2\Assertion | SAML2\EncryptedAssertion The assertion.
$responseSigned boolean 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\Constants::CM_BEARER, \SAML2\Constants::CM_HOK, \SAML2\Constants::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\Constants::CM_BEARER && $hok) {
                $lastError = 'Bearer SubjectConfirmation received, but Holder-of-Key SubjectConfirmation needed';
                continue;
            }
            if ($sc->Method === \SAML2\Constants::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\Constants::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;
    }