Merge pull request #101 from nextcloud/stable10-bump-to-2-10-5

[stable10] Bump to 2.10.5
This commit is contained in:
Lukas Reschke 2017-03-16 20:56:25 +01:00 committed by GitHub
commit 28a2256c52
20 changed files with 772 additions and 159 deletions

10
3rdparty/composer.lock generated vendored
View File

@ -8,16 +8,16 @@
"packages": [
{
"name": "onelogin/php-saml",
"version": "2.10.2",
"version": "2.10.5",
"source": {
"type": "git",
"url": "https://github.com/onelogin/php-saml.git",
"reference": "f9543a05494633671ec587ae1611238dae6edfd4"
"reference": "3319d7707f342e38291eee6b01a4a5f8df1b333b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/onelogin/php-saml/zipball/f9543a05494633671ec587ae1611238dae6edfd4",
"reference": "f9543a05494633671ec587ae1611238dae6edfd4",
"url": "https://api.github.com/repos/onelogin/php-saml/zipball/3319d7707f342e38291eee6b01a4a5f8df1b333b",
"reference": "3319d7707f342e38291eee6b01a4a5f8df1b333b",
"shasum": ""
},
"require": {
@ -58,7 +58,7 @@
"onelogin",
"saml"
],
"time": "2016-11-15T15:34:53+00:00"
"time": "2017-03-13T09:56:49+00:00"
}
],
"packages-dev": [],

View File

@ -16,6 +16,7 @@ return array(
'OneLogin_Saml2_Response' => $vendorDir . '/onelogin/php-saml/lib/Saml2/Response.php',
'OneLogin_Saml2_Settings' => $vendorDir . '/onelogin/php-saml/lib/Saml2/Settings.php',
'OneLogin_Saml2_Utils' => $vendorDir . '/onelogin/php-saml/lib/Saml2/Utils.php',
'OneLogin_Saml2_ValidationError' => $vendorDir . '/onelogin/php-saml/lib/Saml2/Error.php',
'OneLogin_Saml_AuthRequest' => $vendorDir . '/onelogin/php-saml/lib/Saml/AuthRequest.php',
'OneLogin_Saml_Metadata' => $vendorDir . '/onelogin/php-saml/lib/Saml/Metadata.php',
'OneLogin_Saml_Response' => $vendorDir . '/onelogin/php-saml/lib/Saml/Response.php',

View File

@ -17,6 +17,7 @@ class ComposerStaticInitcc75f134f7630c1ee3a8e4d7c86f3bcc
'OneLogin_Saml2_Response' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml2/Response.php',
'OneLogin_Saml2_Settings' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml2/Settings.php',
'OneLogin_Saml2_Utils' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml2/Utils.php',
'OneLogin_Saml2_ValidationError' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml2/Error.php',
'OneLogin_Saml_AuthRequest' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml/AuthRequest.php',
'OneLogin_Saml_Metadata' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml/Metadata.php',
'OneLogin_Saml_Response' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml/Response.php',

View File

@ -1,17 +1,17 @@
[
{
"name": "onelogin/php-saml",
"version": "2.10.2",
"version_normalized": "2.10.2.0",
"version": "2.10.5",
"version_normalized": "2.10.5.0",
"source": {
"type": "git",
"url": "https://github.com/onelogin/php-saml.git",
"reference": "f9543a05494633671ec587ae1611238dae6edfd4"
"reference": "3319d7707f342e38291eee6b01a4a5f8df1b333b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/onelogin/php-saml/zipball/f9543a05494633671ec587ae1611238dae6edfd4",
"reference": "f9543a05494633671ec587ae1611238dae6edfd4",
"url": "https://api.github.com/repos/onelogin/php-saml/zipball/3319d7707f342e38291eee6b01a4a5f8df1b333b",
"reference": "3319d7707f342e38291eee6b01a4a5f8df1b333b",
"shasum": ""
},
"require": {
@ -33,7 +33,7 @@
"ext-mcrypt": "Install mcrypt and php5-mcrypt libs in order to support encryption",
"lib-openssl": "Install openssl lib in order to handle with x509 certs (require to support sign and encryption)"
},
"time": "2016-11-15T15:34:53+00:00",
"time": "2017-03-13T09:56:49+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {

View File

@ -4,7 +4,7 @@ php:
- 5.6
- 5.5
- 5.4
- 5.3
# - 5.3
- 7.0
env:

View File

@ -1,5 +1,30 @@
CHANGELOG
=========
v.2.10.5
* Be able to get at the auth object the last processed ID
* Improve NameID Format support
* Reset errorReason attribute of the auth object after each Process method
* Validate serial number as string to work around libxml2 limitation
* Make the Issuer on the Response Optional
v.2.10.4
* [+](https://github.com/onelogin/php-saml/commit/949359f5cad5e1d085c4e5447d9aa8f49a6e82a1) Security update for signature validation on LogoutRequest/LogoutResponse
* [#192](https://github.com/onelogin/php-saml/pull/192) Added ability to configure DigestAlgorithm in settings
* [#183](https://github.com/onelogin/php-saml/pull/183) Fix strpos bug when decrypting assertions
* [#186](https://github.com/onelogin/php-saml/pull/186) Improve info on entityId validation Exception
* [#188](https://github.com/onelogin/php-saml/pull/188) Fixed issue with undefined constant of UNEXPECTED_SIGNED_ELEMENT
* Read ACS binding on AuthNRequest builder from settings
* Be able to relax Destination validation on SAMLResponses and let this
attribute to be empty with the 'relaxDestinationValidation' setting
v.2.10.3
* Implement a more specific exception class for handling some validation errors
* Minor changes on time validation/exceptions
* Add hooks to retrieve last-sent and last-received requests and responses
* Improve/Fix tests
* Add DigestAlgorithm support on addSign
* [#177](https://github.com/onelogin/php-saml/pull/177) Add error message for bad OneLogin_Saml2_Settings argument
v.2.10.2
* [#175](https://github.com/onelogin/php-saml/pull/175) Allow overriding of host, port, protocol and url path for URL building
* [#173](https://github.com/onelogin/php-saml/pull/173) Provide better support to NameIdFormat

View File

@ -10,7 +10,10 @@ and supported by OneLogin Inc.
Warning
-------
Update php-saml to 2.10.0, this version includes a security patch that contains extra validations that will prevent signature wrapping attacks.
Update php-saml to 2.10.4, this version includes a security patch related to
[signature validations on LogoutRequests/LogoutResponses](https://github.com/onelogin/php-saml/commit/949359f5cad5e1d085c4e5447d9aa8f49a6e82a1)
Update php-saml to 2.10.0, this version includes a security patch that contains extra validations that will prevent signature wrapping attacks. [CVE-2016-1000253](https://github.com/distributedweaknessfiling/DWF-Database-Artifacts/blob/ab8ae6e845eb506fbeb10a7e4ccb379f0b4222ca/DWF/2016/1000253/CVE-2016-1000253.json)
php-saml < v2.10.0 is vulnerable and allows signature wrapping!
@ -458,6 +461,10 @@ $advancedSettings = array (
// (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true).
'wantXMLValidation' => true,
// If true, SAMLResponses with an empty value at its Destination
// attribute will not be rejected for this fact.
'relaxDestinationValidation' => false,
// Algorithm that the toolkit will use on signing process. Options:
// 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
// 'http://www.w3.org/2000/09/xmldsig#dsa-sha1'
@ -466,6 +473,13 @@ $advancedSettings = array (
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'
'signatureAlgorithm' => 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
// Algorithm that the toolkit will use on digest process. Options:
// 'http://www.w3.org/2000/09/xmldsig#sha1'
// 'http://www.w3.org/2001/04/xmlenc#sha256'
// 'http://www.w3.org/2001/04/xmldsig-more#sha384'
// 'http://www.w3.org/2001/04/xmlenc#sha512'
'digestAlgorithm' => 'http://www.w3.org/2000/09/xmldsig#sha1',
// ADFS URL-Encodes SAML data as lowercase, and the toolkit by default uses
// uppercase. Turn it True for ADFS compatibility on signature verification
'lowercaseUrlencoding' => false,
@ -1061,6 +1075,23 @@ Also a developer can use setSelfProtocol, setSelfHost, setSelfPort and getBaseUR
At the settings the developer will be able to set a 'baseurl' parameter that automatically will use setBaseURL to set values for setSelfProtocol, setSelfHost, setSelfPort and setBaseURLPath.
### Working behind load balancer ###
Is possible that asserting request URL and Destination attribute of SAML response fails when working behind load balancer with SSL offload.
You should be able to workaround this by configuring your server so that it is aware of the proxy and returns the original url when requested.
Or by using the method described on the previous section.
### Reply attacks ###
In order to avoid reply attacks, you can store the ID of the SAML messages already processed, to avoid processing them twice. Since the Messages expires and will be invalidated due that fact, you don't need to store those IDs longer than the time frame that you currently accepting.
Get the ID of the last processed message/assertion with the getLastMessageId/getLastAssertionId method of the Auth object.
### Main classes and methods ###
Described below are the main classes and methods that can be invoked.
@ -1136,6 +1167,10 @@ Main class of OneLogin PHP Toolkit
* `buildResponseSignature` - Generates the Signature for a SAML Response
* `getSettings` - Returns the settings info
* `setStrict` - Set the strict mode active/disable
* `getLastRequestID` - Gets the ID of the last AuthNRequest or LogoutRequest generated by the Service Provider.
* `getLastRequestXML` - Returns the most recently-constructed/processed XML SAML request (AuthNRequest, LogoutRequest)
* `getLastResponseXML` - Returns the most recently-constructed/processed XML SAML response (SAMLResponse, LogoutResponse). If the SAMLResponse had an encrypted assertion, decrypts it.
##### OneLogin_Saml2_AuthnRequest - `AuthnRequest.php` #####
@ -1144,6 +1179,7 @@ SAML 2 Authentication Request class
* `OneLogin_Saml2_AuthnRequest` - Constructs the `AuthnRequest` object.
* `getRequest` - Returns deflated, base64 encoded, unsigned `AuthnRequest`.
* `getId` - Returns the `AuthNRequest` ID.
* `getXML` - Returns the XML that will be sent as part of the request.
##### OneLogin_Saml2_Response - `Response.php` #####
@ -1167,6 +1203,7 @@ SAML 2 Authentication Response class
* `validateTimestamps` - Verifies that the document is still valid according
Conditions Element.
* `getError` - After executing a validation process, if it fails, this method returns the cause
* `getXMLDocument` - Returns the SAML Response document (If contains an encrypted assertion, decrypts it)
##### OneLogin_Saml2_LogoutRequest - `LogoutRequest.php` #####
@ -1181,6 +1218,7 @@ SAML 2 Logout Request class
* `getSessionIndexes` - Gets the SessionIndexes from the Logout Request.
* `isValid` - Checks if the Logout Request received is valid.
* `getError` - After executing a validation process, if it fails, this method returns the cause
* `getXML` - Returns the XML that will be sent as part of the request or that was received at the SP.
##### OneLogin_Saml2_LogoutResponse - `LogoutResponse.php` #####
@ -1193,7 +1231,8 @@ SAML 2 Logout Response class
* `isValid` - Determines if the SAML LogoutResponse is valid
* `build` - Generates a Logout Response object.
* `getResponse` - Returns a Logout Response object.
* `getError` - After executing a validation process, if it fails, this method returns the cause
* `getError` - After executing a validation process, if it fails, this method returns the cause.
* `getXML` - Returns the XML that will be sent as part of the response or that was received at the SP.
##### OneLogin_Saml2_Settings - `Settings.php` #####

View File

@ -77,6 +77,10 @@ $advancedSettings = array (
// (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true).
'wantXMLValidation' => true,
// If true, SAMLResponses with an empty value at its Destination
// attribute will not be rejected for this fact.
'relaxDestinationValidation' => false,
// Algorithm that the toolkit will use on signing process. Options:
// 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
// 'http://www.w3.org/2000/09/xmldsig#dsa-sha1'
@ -85,6 +89,13 @@ $advancedSettings = array (
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'
'signatureAlgorithm' => 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
// Algorithm that the toolkit will use on digest process. Options:
// 'http://www.w3.org/2000/09/xmldsig#sha1'
// 'http://www.w3.org/2001/04/xmlenc#sha256'
// 'http://www.w3.org/2001/04/xmldsig-more#sha384'
// 'http://www.w3.org/2001/04/xmlenc#sha512'
'digestAlgorithm' => 'http://www.w3.org/2000/09/xmldsig#sha1',
// ADFS URL-Encodes SAML data as lowercase, and the toolkit by default uses
// uppercase. Turn it True for ADFS compatibility on signature verification
'lowercaseUrlencoding' => false,

View File

@ -2,7 +2,7 @@
"name": "onelogin/php-saml",
"description": "OneLogin PHP SAML Toolkit",
"license": "MIT",
"version": "2.10.2",
"version": "2.10.5",
"homepage": "https://onelogin.zendesk.com/hc/en-us/sections/200245634-SAML-Toolkits",
"keywords": ["saml", "saml2", "onelogin"],
"autoload": {

View File

@ -6,7 +6,6 @@
*/
class OneLogin_Saml2_Auth
{
/**
* Settings data.
*
@ -59,6 +58,28 @@ class OneLogin_Saml2_Auth
*/
private $_sessionExpiration;
/**
* The ID of the last message processed
*
* @var string
*/
private $_lastMessageId;
/**
* The ID of the last assertion processed
*
* @var string
*/
private $_lastAssertionId;
/**
* The NotOnOrAfter value of the valid SubjectConfirmationData
* node (if any) of the last assertion processed
*
* @var DateTime
*/
private $_lastAssertionNotOnOrAfter;
/**
* If any error.
*
@ -80,6 +101,23 @@ class OneLogin_Saml2_Auth
*/
private $_lastRequestID;
/**
* The most recently-constructed/processed XML SAML request
* (AuthNRequest, LogoutRequest)
*
* @var string
*/
private $_lastRequest;
/**
* The most recently-constructed/processed XML SAML response
* (SAMLResponse, LogoutResponse). If the SAMLResponse was
* encrypted, by default tries to return the decrypted XML
*
* @var string
*/
private $_lastResponse;
/**
* Initializes the SP SAML instance.
*
@ -110,7 +148,10 @@ class OneLogin_Saml2_Auth
public function setStrict($value)
{
if (! (is_bool($value))) {
throw new Exception('Invalid value passed to setStrict()');
throw new OneLogin_Saml2_Error(
'Invalid value passed to setStrict()',
OneLogin_Saml2_Error::SETTINGS_INVALID_SYNTAX
);
}
$this->_settings->setStrict($value);
@ -126,9 +167,11 @@ class OneLogin_Saml2_Auth
public function processResponse($requestId = null)
{
$this->_errors = array();
$this->_errorReason = null;
if (isset($_POST) && isset($_POST['SAMLResponse'])) {
// AuthnResponse -- HTTP_POST Binding
$response = new OneLogin_Saml2_Response($this->_settings, $_POST['SAMLResponse']);
$this->_lastResponse = $response->getXMLDocument();
if ($response->isValid($requestId)) {
$this->_attributes = $response->getAttributes();
@ -137,6 +180,9 @@ class OneLogin_Saml2_Auth
$this->_authenticated = true;
$this->_sessionIndex = $response->getSessionIndex();
$this->_sessionExpiration = $response->getSessionNotOnOrAfter();
$this->_lastMessageId = $response->getId();
$this->_lastAssertionId = $response->getAssertionId();
$this->_lastAssertionNotOnOrAfter = $response->getAssertionNotOnOrAfter();
} else {
$this->_errors[] = 'invalid_response';
$this->_errorReason = $response->getError();
@ -163,17 +209,20 @@ class OneLogin_Saml2_Auth
*
* @throws OneLogin_Saml2_Error
*/
public function processSLO($keepLocalSession = false, $requestId = null, $retrieveParametersFromServer = false, $cbDeleteSession = null, $stay=false)
public function processSLO($keepLocalSession = false, $requestId = null, $retrieveParametersFromServer = false, $cbDeleteSession = null, $stay = false)
{
$this->_errors = array();
$this->_errorReason = null;
if (isset($_GET) && isset($_GET['SAMLResponse'])) {
$logoutResponse = new OneLogin_Saml2_LogoutResponse($this->_settings, $_GET['SAMLResponse']);
$this->_lastResponse = $logoutResponse->getXML();
if (!$logoutResponse->isValid($requestId, $retrieveParametersFromServer)) {
$this->_errors[] = 'invalid_logout_response';
$this->_errorReason = $logoutResponse->getError();
} else if ($logoutResponse->getStatus() !== OneLogin_Saml2_Constants::STATUS_SUCCESS) {
$this->_errors[] = 'logout_not_success';
} else {
$this->_lastMessageId = $logoutResponse->id;
if (!$keepLocalSession) {
if ($cbDeleteSession === null) {
OneLogin_Saml2_Utils::deleteLocalSession();
@ -184,6 +233,7 @@ class OneLogin_Saml2_Auth
}
} else if (isset($_GET) && isset($_GET['SAMLRequest'])) {
$logoutRequest = new OneLogin_Saml2_LogoutRequest($this->_settings, $_GET['SAMLRequest']);
$this->_lastRequest = $logoutRequest->getXML();
if (!$logoutRequest->isValid($retrieveParametersFromServer)) {
$this->_errors[] = 'invalid_logout_request';
$this->_errorReason = $logoutRequest->getError();
@ -196,8 +246,11 @@ class OneLogin_Saml2_Auth
}
}
$inResponseTo = $logoutRequest->id;
$this->_lastMessageId = $logoutRequest->id;
$responseBuilder = new OneLogin_Saml2_LogoutResponse($this->_settings);
$responseBuilder->build($inResponseTo);
$this->_lastResponse = $responseBuilder->getXML();
$logoutResponse = $responseBuilder->getResponse();
$parameters = array('SAMLResponse' => $logoutResponse);
@ -353,12 +406,13 @@ class OneLogin_Saml2_Auth
*
* @return If $stay is True, it return a string with the SLO URL + LogoutRequest + parameters
*/
public function login($returnTo = null, $parameters = array(), $forceAuthn = false, $isPassive = false, $stay=false, $setNameIdPolicy = true)
public function login($returnTo = null, $parameters = array(), $forceAuthn = false, $isPassive = false, $stay = false, $setNameIdPolicy = true)
{
assert('is_array($parameters)');
$authnRequest = new OneLogin_Saml2_AuthnRequest($this->_settings, $forceAuthn, $isPassive, $setNameIdPolicy);
$this->_lastRequest = $authnRequest->getXML();
$this->_lastRequestID = $authnRequest->getId();
$samlRequest = $authnRequest->getRequest();
@ -393,7 +447,7 @@ class OneLogin_Saml2_Auth
*
* @throws OneLogin_Saml2_Error
*/
public function logout($returnTo = null, $parameters = array(), $nameId = null, $sessionIndex = null, $stay=false, $nameIdFormat = null)
public function logout($returnTo = null, $parameters = array(), $nameId = null, $sessionIndex = null, $stay = false, $nameIdFormat = null)
{
assert('is_array($parameters)');
@ -408,9 +462,13 @@ class OneLogin_Saml2_Auth
if (empty($nameId) && !empty($this->_nameid)) {
$nameId = $this->_nameid;
}
if (empty($nameIdFormat) && !empty($this->_nameidFormat)) {
$nameIdFormat = $this->_nameidFormat;
}
$logoutRequest = new OneLogin_Saml2_LogoutRequest($this->_settings, null, $nameId, $sessionIndex, $nameIdFormat);
$this->_lastRequest = $logoutRequest->getXML();
$this->_lastRequestID = $logoutRequest->id;
$samlRequest = $logoutRequest->getRequest();
@ -482,10 +540,11 @@ class OneLogin_Saml2_Auth
*/
public function buildRequestSignature($samlRequest, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA1)
{
if (!$this->_settings->checkSPCerts()) {
$key = $this->_settings->getSPkey();
if (empty($key)) {
throw new OneLogin_Saml2_Error(
"Trying to sign the SAML Request but can't load the SP certs",
OneLogin_Saml2_Error::SP_CERTS_NOT_FOUND
"Trying to sign the SAML Request but can't load the SP private key",
OneLogin_Saml2_Error::PRIVATE_KEY_NOT_FOUND
);
}
@ -526,15 +585,14 @@ class OneLogin_Saml2_Auth
*/
public function buildResponseSignature($samlResponse, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA1)
{
if (!$this->_settings->checkSPCerts()) {
$key = $this->_settings->getSPkey();
if (empty($key)) {
throw new OneLogin_Saml2_Error(
"Trying to sign the SAML Response but can't load the SP certs",
OneLogin_Saml2_Error::SP_CERTS_NOT_FOUND
"Trying to sign the SAML Response but can't load the SP private key",
OneLogin_Saml2_Error::PRIVATE_KEY_NOT_FOUND
);
}
$key = $this->_settings->getSPkey();
$objKey = new XMLSecurityKey($signAlgorithm, array('type' => 'private'));
$objKey->loadKey($key, false);
@ -555,4 +613,63 @@ class OneLogin_Saml2_Auth
$signature = $objKey->signData($msg);
return base64_encode($signature);
}
/**
* @return string The ID of the last message processed
*/
public function getLastMessageId()
{
return $this->_lastMessageId;
}
/**
* @return string The ID of the last assertion processed
*/
public function getLastAssertionId()
{
return $this->_lastAssertionId;
}
/**
* @return The NotOnOrAfter value of the valid
* SubjectConfirmationData node (if any)
* of the last assertion processed
*/
public function getLastAssertionNotOnOrAfter()
{
return $this->_lastAssertionNotOnOrAfter;
}
/**
* Returns the most recently-constructed/processed
* XML SAML request (AuthNRequest, LogoutRequest)
*
* @return string The Request XML
*/
public function getLastRequestXML()
{
return $this->_lastRequest;
}
/**
* Returns the most recently-constructed/processed
* XML SAML response (SAMLResponse, LogoutResponse).
* If the SAMLResponse was encrypted, by default tries
* to return the decrypted XML.
*
* @return string The Response XML
*/
public function getLastResponseXML()
{
$response = null;
if (isset($this->_lastResponse)) {
if (is_string($this->_lastResponse)) {
$response = $this->_lastResponse;
} else {
$response = $this->_lastResponse->saveXML();
}
}
return $response;
}
}

View File

@ -123,7 +123,7 @@ REQUESTEDAUTHN;
{$providerNameStr}{$forceAuthnStr}{$isPassiveStr}
IssueInstant="$issueInstant"
Destination="{$idpData['singleSignOnService']['url']}"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
ProtocolBinding="{$spData['assertionConsumerService']['binding']}"
AssertionConsumerServiceURL="{$spData['assertionConsumerService']['url']}">
<saml:Issuer>{$spData['entityId']}</saml:Issuer>
{$nameIdPolicyStr}
@ -137,7 +137,7 @@ AUTHNREQUEST;
/**
* Returns deflated, base64 encoded, unsigned AuthnRequest.
*
*
* @param bool|null $deflate Whether or not we should 'gzdeflate' the request body before we return it.
*/
public function getRequest($deflate = null)
@ -165,4 +165,14 @@ AUTHNREQUEST;
{
return $this->_id;
}
/**
* Returns the XML that will be sent as part of the request
*
* @return string
*/
public function getXML()
{
return $this->_authnRequest;
}
}

View File

@ -13,6 +13,8 @@ class OneLogin_Saml2_Error extends Exception
const SETTINGS_INVALID = 2;
const METADATA_SP_INVALID = 3;
const SP_CERTS_NOT_FOUND = 4;
// SP_CERTS_NOT_FOUND is deprecated, use CERT_NOT_FOUND instead
const CERT_NOT_FOUND = 4;
const REDIRECT_INVALID_URL = 5;
const PUBLIC_CERT_FILE_NOT_FOUND = 6;
const PRIVATE_KEY_FILE_NOT_FOUND = 7;
@ -21,8 +23,84 @@ class OneLogin_Saml2_Error extends Exception
const SAML_LOGOUTREQUEST_INVALID = 10;
const SAML_LOGOUTRESPONSE_INVALID = 11;
const SAML_SINGLE_LOGOUT_NOT_SUPPORTED = 12;
const PUBLIC_CERT_NOT_FOUND = 13;
const PRIVATE_KEY_NOT_FOUND = 14;
const PRIVATE_KEY_NOT_FOUND = 13;
const UNSUPPORTED_SETTINGS_OBJECT = 14;
/**
* Constructor
*
* @param string $msg Describes the error.
* @param int $code The code error (defined in the error class).
* @param array|null $args Arguments used in the message that describes the error.
*/
public function __construct($msg, $code = 0, $args = null)
{
assert('is_string($msg)');
assert('is_int($code)');
$message = OneLogin_Saml2_Utils::t($msg, $args);
parent::__construct($message, $code);
}
}
/**
* This class implements another custom Exception handler,
* related to exceptions that happens during validation process.
*/
class OneLogin_Saml2_ValidationError extends Exception
{
# Validation Errors
const UNSUPPORTED_SAML_VERSION = 0;
const MISSING_ID = 1;
const WRONG_NUMBER_OF_ASSERTIONS = 2;
const MISSING_STATUS = 3;
const MISSING_STATUS_CODE = 4;
const STATUS_CODE_IS_NOT_SUCCESS = 5;
const WRONG_SIGNED_ELEMENT = 6;
const ID_NOT_FOUND_IN_SIGNED_ELEMENT = 7;
const DUPLICATED_ID_IN_SIGNED_ELEMENTS = 8;
const INVALID_SIGNED_ELEMENT = 9;
const DUPLICATED_REFERENCE_IN_SIGNED_ELEMENTS = 10;
const UNEXPECTED_SIGNED_ELEMENTS = 11;
const WRONG_NUMBER_OF_SIGNATURES_IN_RESPONSE = 12;
const WRONG_NUMBER_OF_SIGNATURES_IN_ASSERTION = 13;
const INVALID_XML_FORMAT = 14;
const WRONG_INRESPONSETO = 15;
const NO_ENCRYPTED_ASSERTION = 16;
const NO_ENCRYPTED_NAMEID = 17;
const MISSING_CONDITIONS = 18;
const ASSERTION_TOO_EARLY = 19;
const ASSERTION_EXPIRED = 20;
const WRONG_NUMBER_OF_AUTHSTATEMENTS = 21;
const NO_ATTRIBUTESTATEMENT = 22;
const ENCRYPTED_ATTRIBUTES = 23;
const WRONG_DESTINATION = 24;
const EMPTY_DESTINATION = 25;
const WRONG_AUDIENCE = 26;
const ISSUER_MULTIPLE_IN_RESPONSE = 27;
const ISSUER_NOT_FOUND_IN_ASSERTION = 28;
const WRONG_ISSUER = 29;
const SESSION_EXPIRED = 30;
const WRONG_SUBJECTCONFIRMATION = 31;
const NO_SIGNED_MESSAGE = 32;
const NO_SIGNED_ASSERTION = 33;
const NO_SIGNATURE_FOUND = 34;
const KEYINFO_NOT_FOUND_IN_ENCRYPTED_DATA = 35;
const CHILDREN_NODE_NOT_FOUND_IN_KEYINFO = 36;
const UNSUPPORTED_RETRIEVAL_METHOD = 37;
const NO_NAMEID = 38;
const EMPTY_NAMEID = 39;
const SP_NAME_QUALIFIER_NAME_MISMATCH = 40;
const DUPLICATED_ATTRIBUTE_NAME_FOUND = 41;
const INVALID_SIGNATURE = 42;
const WRONG_NUMBER_OF_SIGNATURES = 43;
const RESPONSE_EXPIRED = 44;
const UNEXPECTED_REFERENCE = 45;
const NOT_SUPPORTED = 46;
const KEY_ALGORITHM_ERROR = 47;
const MISSING_ENCRYPTED_ELEMENT = 48;
/**
* Constructor

View File

@ -6,7 +6,6 @@
*/
class OneLogin_Saml2_LogoutRequest
{
/**
* Contains the ID of the Logout Request
* @var string
@ -50,7 +49,6 @@ class OneLogin_Saml2_LogoutRequest
}
if (!isset($request) || empty($request)) {
$spData = $this->_settings->getSPData();
$idpData = $this->_settings->getIdPData();
$security = $this->_settings->getSecurityData();
@ -182,7 +180,10 @@ LOGOUTREQUEST;
$encryptedData = $encryptedDataNodes->item(0);
if (empty($key)) {
throw new Exception("Key is required in order to decrypt the NameID");
throw new OneLogin_Saml2_Error(
"Private Key is required in order to decrypt the NameID, check settings",
OneLogin_Saml2_Error::PRIVATE_KEY_NOT_FOUND
);
}
$seckey = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, array('type'=>'private'));
@ -198,7 +199,10 @@ LOGOUTREQUEST;
}
if (!isset($nameId)) {
throw new Exception("Not NameID found in the Logout Request");
throw new OneLogin_Saml2_ValidationError(
"NameID not found in the Logout Request",
OneLogin_Saml2_ValidationError::NO_NAMEID
);
}
$nameIdData = array();
@ -282,7 +286,7 @@ LOGOUTREQUEST;
*
* @return bool If the Logout Request is or not valid
*/
public function isValid($retrieveParametersFromServer=false)
public function isValid($retrieveParametersFromServer = false)
{
$this->_error = null;
try {
@ -298,7 +302,10 @@ LOGOUTREQUEST;
if ($security['wantXMLValidation']) {
$res = OneLogin_Saml2_Utils::validateXML($dom, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
if (!$res instanceof DOMDocument) {
throw new Exception("Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd");
throw new OneLogin_Saml2_ValidationError(
"Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd",
OneLogin_Saml2_ValidationError::INVALID_XML_FORMAT
);
}
}
@ -308,7 +315,10 @@ LOGOUTREQUEST;
if ($dom->documentElement->hasAttribute('NotOnOrAfter')) {
$na = OneLogin_Saml2_Utils::parseSAML2Time($dom->documentElement->getAttribute('NotOnOrAfter'));
if ($na <= time()) {
throw new Exception('Timing issues (please check your clock settings)');
throw new OneLogin_Saml2_ValidationError(
"Could not validate timestamp: expired. Check system clock.",
OneLogin_Saml2_ValidationError::RESPONSE_EXPIRED
);
}
}
@ -317,7 +327,10 @@ LOGOUTREQUEST;
$destination = $dom->documentElement->getAttribute('Destination');
if (!empty($destination)) {
if (strpos($destination, $currentURL) === false) {
throw new Exception("The LogoutRequest was received at $currentURL instead of $destination");
throw new OneLogin_Saml2_ValidationError(
"The LogoutRequest was received at $currentURL instead of $destination",
OneLogin_Saml2_ValidationError::WRONG_DESTINATION
);
}
}
}
@ -327,18 +340,23 @@ LOGOUTREQUEST;
// Check issuer
$issuer = $this->getIssuer($dom);
if (!empty($issuer) && $issuer != $idPEntityId) {
throw new Exception("Invalid issuer in the Logout Request");
throw new OneLogin_Saml2_ValidationError(
"Invalid issuer in the Logout Request",
OneLogin_Saml2_ValidationError::WRONG_ISSUER
);
}
if ($security['wantMessagesSigned']) {
if (!isset($_GET['Signature'])) {
throw new Exception("The Message of the Logout Request is not signed and the SP require it");
throw new OneLogin_Saml2_ValidationError(
"The Message of the Logout Request is not signed and the SP require it",
OneLogin_Saml2_ValidationError::NO_SIGNED_MESSAGE
);
}
}
}
if (isset($_GET['Signature'])) {
if (!isset($_GET['SigAlg'])) {
$signAlg = XMLSecurityKey::RSA_SHA1;
} else {
@ -360,7 +378,10 @@ LOGOUTREQUEST;
}
if (!isset($idpData['x509cert']) || empty($idpData['x509cert'])) {
throw new Exception('In order to validate the sign on the Logout Request, the x509cert of the IdP is required');
throw new OneLogin_Saml2_Error(
"In order to validate the sign on the Logout Request, the x509cert of the IdP is required",
OneLogin_Saml2_Error::CERT_NOT_FOUND
);
}
$cert = $idpData['x509cert'];
@ -371,12 +392,18 @@ LOGOUTREQUEST;
try {
$objKey = OneLogin_Saml2_Utils::castKey($objKey, $signAlg, 'public');
} catch (Exception $e) {
throw new Exception('Invalid signAlg in the recieved Logout Request');
throw new OneLogin_Saml2_ValidationError(
"Invalid signAlg in the recieved Logout Request",
OneLogin_Saml2_ValidationError::INVALID_SIGNATURE
);
}
}
if (!$objKey->verifySignature($signedQuery, base64_decode($_GET['Signature']))) {
throw new Exception('Signature validation failed. Logout Request rejected');
if ($objKey->verifySignature($signedQuery, base64_decode($_GET['Signature'])) !== 1) {
throw new OneLogin_Saml2_ValidationError(
"Signature validation failed. Logout Request rejected",
OneLogin_Saml2_ValidationError::INVALID_SIGNATURE
);
}
}
@ -399,4 +426,15 @@ LOGOUTREQUEST;
{
return $this->_error;
}
/**
* Returns the XML that will be sent as part of the request
* or that was received at the SP
*
* @return string
*/
public function getXML()
{
return $this->_logoutRequest;
}
}

View File

@ -6,6 +6,11 @@
*/
class OneLogin_Saml2_LogoutResponse
{
/**
* Contains the ID of the Logout Response
* @var string
*/
public $id;
/**
* Object that represents the setting info
@ -57,6 +62,10 @@ class OneLogin_Saml2_LogoutResponse
}
$this->document = new DOMDocument();
$this->document = OneLogin_Saml2_Utils::loadXML($this->document, $this->_logoutResponse);
if ($this->document->documentElement->hasAttribute('ID')) {
$this->id = $this->document->documentElement->getAttribute('ID');
}
}
}
@ -100,11 +109,10 @@ class OneLogin_Saml2_LogoutResponse
*
* @throws Exception
*/
public function isValid($requestId = null, $retrieveParametersFromServer=false)
public function isValid($requestId = null, $retrieveParametersFromServer = false)
{
$this->_error = null;
try {
$idpData = $this->_settings->getIdPData();
$idPEntityId = $idpData['entityId'];
@ -114,7 +122,10 @@ class OneLogin_Saml2_LogoutResponse
if ($security['wantXMLValidation']) {
$res = OneLogin_Saml2_Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
if (!$res instanceof DOMDocument) {
throw new Exception("Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd");
throw new OneLogin_Saml2_ValidationError(
"Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd",
OneLogin_Saml2_ValidationError::INVALID_XML_FORMAT
);
}
}
@ -122,14 +133,20 @@ class OneLogin_Saml2_LogoutResponse
if (isset($requestId) && $this->document->documentElement->hasAttribute('InResponseTo')) {
$inResponseTo = $this->document->documentElement->getAttribute('InResponseTo');
if ($requestId != $inResponseTo) {
throw new Exception("The InResponseTo of the Logout Response: $inResponseTo, does not match the ID of the Logout request sent by the SP: $requestId");
throw new OneLogin_Saml2_ValidationError(
"The InResponseTo of the Logout Response: $inResponseTo, does not match the ID of the Logout request sent by the SP: $requestId",
OneLogin_Saml2_ValidationError::WRONG_INRESPONSETO
);
}
}
// Check issuer
$issuer = $this->getIssuer();
if (!empty($issuer) && $issuer != $idPEntityId) {
throw new Exception("Invalid issuer in the Logout Response");
throw new OneLogin_Saml2_ValidationError(
"Invalid issuer in the Logout Response",
OneLogin_Saml2_ValidationError::WRONG_ISSUER
);
}
$currentURL = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
@ -139,14 +156,20 @@ class OneLogin_Saml2_LogoutResponse
$destination = $this->document->documentElement->getAttribute('Destination');
if (!empty($destination)) {
if (strpos($destination, $currentURL) === false) {
throw new Exception("The LogoutResponse was received at $currentURL instead of $destination");
throw new OneLogin_Saml2_ValidationError(
"The LogoutResponse was received at $currentURL instead of $destination",
OneLogin_Saml2_ValidationError::WRONG_DESTINATION
);
}
}
}
if ($security['wantMessagesSigned']) {
if (!isset($_GET['Signature'])) {
throw new Exception("The Message of the Logout Response is not signed and the SP requires it");
throw new OneLogin_Saml2_ValidationError(
"The Message of the Logout Response is not signed and the SP requires it",
OneLogin_Saml2_ValidationError::NO_SIGNED_MESSAGE
);
}
}
}
@ -173,7 +196,10 @@ class OneLogin_Saml2_LogoutResponse
}
if (!isset($idpData['x509cert']) || empty($idpData['x509cert'])) {
throw new Exception('In order to validate the sign on the Logout Response, the x509cert of the IdP is required');
throw new OneLogin_Saml2_Error(
"In order to validate the sign on the Logout Response, the x509cert of the IdP is required",
OneLogin_Saml2_Error::CERT_NOT_FOUND
);
}
$cert = $idpData['x509cert'];
@ -184,12 +210,18 @@ class OneLogin_Saml2_LogoutResponse
try {
$objKey = OneLogin_Saml2_Utils::castKey($objKey, $signAlg, 'public');
} catch (Exception $e) {
throw new Exception('Invalid signAlg in the recieved Logout Response');
throw new OneLogin_Saml2_ValidationError(
"Invalid signAlg in the recieved Logout Response",
OneLogin_Saml2_ValidationError::INVALID_SIGNATURE
);
}
}
if (!$objKey->verifySignature($signedQuery, base64_decode($_GET['Signature']))) {
throw new Exception('Signature validation failed. Logout Response rejected');
if ($objKey->verifySignature($signedQuery, base64_decode($_GET['Signature'])) !== 1) {
throw new OneLogin_Saml2_ValidationError(
"Signature validation failed. Logout Response rejected",
OneLogin_Saml2_ValidationError::INVALID_SIGNATURE
);
}
}
return true;
@ -227,13 +259,13 @@ class OneLogin_Saml2_LogoutResponse
$spData = $this->_settings->getSPData();
$idpData = $this->_settings->getIdPData();
$id = OneLogin_Saml2_Utils::generateUniqueID();
$this->id = OneLogin_Saml2_Utils::generateUniqueID();
$issueInstant = OneLogin_Saml2_Utils::parseTime2SAML(time());
$logoutResponse = <<<LOGOUTRESPONSE
<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="{$id}"
ID="{$this->id}"
Version="2.0"
IssueInstant="{$issueInstant}"
Destination="{$idpData['singleLogoutService']['url']}"
@ -250,9 +282,9 @@ LOGOUTRESPONSE;
/**
* Returns a Logout Response object.
*
*
* @param bool|null $deflate Whether or not we should 'gzdeflate' the response body before we return it.
*
*
* @return string Logout Response deflated and base64 encoded
*/
public function getResponse($deflate = null)
@ -277,4 +309,23 @@ LOGOUTRESPONSE;
{
return $this->_error;
}
/**
* @return the ID of the Response
*/
public function getId()
{
return $this->id;
}
/**
* Returns the XML that will be sent as part of the response
* or that was received at the SP
*
* @return string
*/
public function getXML()
{
return $this->_logoutResponse;
}
}

View File

@ -170,15 +170,17 @@ METADATA_TEMPLATE;
/**
* Signs the metadata with the key/cert provided
*
* @param string $metadata SAML Metadata XML
* @param string $key x509 key
* @param string $cert x509 cert
* @param string $metadata SAML Metadata XML
* @param string $key x509 key
* @param string $cert x509 cert
* @param string $signAlgorithm Signature algorithm method
* @param string $digestAlgorithm Digest algorithm method
*
* @return string Signed Metadata
*/
public static function signMetadata($metadata, $key, $cert, $signAlgorithm = XMLSecurityKey::RSA_SHA1)
public static function signMetadata($metadata, $key, $cert, $signAlgorithm = XMLSecurityKey::RSA_SHA1, $digestAlgorithm = XMLSecurityDSig::SHA1)
{
return OneLogin_Saml2_Utils::addSign($metadata, $key, $cert, $signAlgorithm);
return OneLogin_Saml2_Utils::addSign($metadata, $key, $cert, $signAlgorithm, $digestAlgorithm);
}
/**

View File

@ -44,6 +44,13 @@ class OneLogin_Saml2_Response
*/
private $_error;
/**
* NotOnOrAfter value of a valid SubjectConfirmationData node
*
* @var DateTime
*/
private $_validSCDNotOnOrAfter;
/**
* Constructs the SAML Response object.
*
@ -66,7 +73,10 @@ class OneLogin_Saml2_Response
$this->document = new DOMDocument();
$this->document = OneLogin_Saml2_Utils::loadXML($this->document, $this->response);
if (!$this->document) {
throw new Exception('SAML Response could not be processed');
throw new OneLogin_Saml2_ValidationError(
"SAML Response could not be processed",
OneLogin_Saml2_ValidationError::INVALID_XML_FORMAT
);
}
// Quick check for the presence of EncryptedAssertion
@ -93,18 +103,27 @@ class OneLogin_Saml2_Response
try {
// Check SAML version
if ($this->document->documentElement->getAttribute('Version') != '2.0') {
throw new Exception('Unsupported SAML version');
throw new OneLogin_Saml2_ValidationError(
"Unsupported SAML version",
OneLogin_Saml2_ValidationError::UNSUPPORTED_SAML_VERSION
);
}
if (!$this->document->documentElement->hasAttribute('ID')) {
throw new Exception('Missing ID attribute on SAML Response');
throw new OneLogin_Saml2_ValidationError(
"Missing ID attribute on SAML Response",
OneLogin_Saml2_ValidationError::MISSING_ID
);
}
$status = $this->checkStatus();
$singleAssertion = $this->validateNumAssertions();
if (!$singleAssertion) {
throw new Exception('SAML Response must contain 1 assertion');
throw new OneLogin_Saml2_ValidationError(
"SAML Response must contain 1 assertion",
OneLogin_Saml2_ValidationError::WRONG_NUMBER_OF_ASSERTIONS
);
}
$idpData = $this->_settings->getIdPData();
@ -127,15 +146,20 @@ class OneLogin_Saml2_Response
$errorXmlMsg = "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd";
$res = OneLogin_Saml2_Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
if (!$res instanceof DOMDocument) {
throw new Exception($errorXmlMsg);
throw new OneLogin_Saml2_ValidationError(
$errorXmlMsg,
OneLogin_Saml2_ValidationError::INVALID_XML_FORMAT
);
}
# If encrypted, check also the decrypted document
if ($this->encrypted) {
$res = OneLogin_Saml2_Utils::validateXML($this->decryptedDocument, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
if (!$res instanceof DOMDocument) {
throw new Exception($errorXmlMsg);
throw new OneLogin_Saml2_ValidationError(
$errorXmlMsg,
OneLogin_Saml2_ValidationError::INVALID_XML_FORMAT
);
}
}
@ -150,54 +174,77 @@ class OneLogin_Saml2_Response
// Check if the InResponseTo of the Response matchs the ID of the AuthNRequest (requestId) if provided
if (isset($requestId) && isset($responseInResponseTo)) {
if ($requestId != $responseInResponseTo) {
throw new Exception("The InResponseTo of the Response: $responseInResponseTo, does not match the ID of the AuthNRequest sent by the SP: $requestId");
throw new OneLogin_Saml2_ValidationError(
"The InResponseTo of the Response: $responseInResponseTo, does not match the ID of the AuthNRequest sent by the SP: $requestId",
OneLogin_Saml2_ValidationError::WRONG_INRESPONSETO
);
}
}
if (!$this->encrypted && $security['wantAssertionsEncrypted']) {
throw new Exception("The assertion of the Response is not encrypted and the SP requires it");
throw new OneLogin_Saml2_ValidationError(
"The assertion of the Response is not encrypted and the SP requires it",
OneLogin_Saml2_ValidationError::NO_ENCRYPTED_ASSERTION
);
}
if ($security['wantNameIdEncrypted']) {
$encryptedIdNodes = $this->_queryAssertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData');
if ($encryptedIdNodes->length != 1) {
throw new Exception("The NameID of the Response is not encrypted and the SP requires it");
throw new OneLogin_Saml2_ValidationError(
"The NameID of the Response is not encrypted and the SP requires it",
OneLogin_Saml2_ValidationError::NO_ENCRYPTED_NAMEID
);
}
}
// Validate Conditions element exists
if (!$this->checkOneCondition()) {
throw new Exception("The Assertion must include a Conditions element");
throw new OneLogin_Saml2_ValidationError(
"The Assertion must include a Conditions element",
OneLogin_Saml2_ValidationError::MISSING_CONDITIONS
);
}
// Validate Asserion timestamps
$validTimestamps = $this->validateTimestamps();
if (!$validTimestamps) {
throw new Exception('Timing issues (please check your clock settings)');
}
$this->validateTimestamps();
// Validate AuthnStatement element exists and is unique
if (!$this->checkOneAuthnStatement()) {
throw new Exception("The Assertion must include an AuthnStatement element");
throw new OneLogin_Saml2_ValidationError(
"The Assertion must include an AuthnStatement element",
OneLogin_Saml2_ValidationError::WRONG_NUMBER_OF_AUTHSTATEMENTS
);
}
// EncryptedAttributes are not supported
$encryptedAttributeNodes = $this->_queryAssertion('/saml:AttributeStatement/saml:EncryptedAttribute');
if ($encryptedAttributeNodes->length > 0) {
throw new Exception("There is an EncryptedAttribute in the Response and this SP not support them");
throw new OneLogin_Saml2_ValidationError(
"There is an EncryptedAttribute in the Response and this SP not support them",
OneLogin_Saml2_ValidationError::ENCRYPTED_ATTRIBUTES
);
}
// Check destination
if ($this->document->documentElement->hasAttribute('Destination')) {
$destination = trim($this->document->documentElement->getAttribute('Destination'));
if (empty($destination)) {
throw new Exception("The response has an empty Destination value");
if (!$security['relaxDestinationValidation']) {
throw new OneLogin_Saml2_ValidationError(
"The response has an empty Destination value",
OneLogin_Saml2_ValidationError::EMPTY_DESTINATION
);
}
} else {
if (strpos($destination, $currentURL) !== 0) {
$currentURLNoRouted = OneLogin_Saml2_Utils::getSelfURLNoQuery();
if (strpos($destination, $currentURLNoRouted) !== 0) {
throw new Exception("The response was received at $currentURL instead of $destination");
throw new OneLogin_Saml2_ValidationError(
"The response was received at $currentURL instead of $destination",
OneLogin_Saml2_ValidationError::WRONG_DESTINATION
);
}
}
}
@ -205,24 +252,36 @@ class OneLogin_Saml2_Response
// Check audience
$validAudiences = $this->getAudiences();
if (!empty($validAudiences) && !in_array($spEntityId, $validAudiences)) {
throw new Exception("$spEntityId is not a valid audience for this Response");
if (!empty($validAudiences) && !in_array($spEntityId, $validAudiences, true)) {
throw new OneLogin_Saml2_ValidationError(
sprintf(
"Invalid audience for this Response (expected '%s', got '%s')",
$spEntityId,
implode(',', $validAudiences)
),
OneLogin_Saml2_ValidationError::WRONG_AUDIENCE
);
}
// Check the issuers
$issuers = $this->getIssuers();
foreach ($issuers as $issuer) {
$trimmedIssuer = trim($issuer);
if (empty($trimmedIssuer) || $trimmedIssuer !== $idPEntityId) {
throw new Exception("Invalid issuer in the Assertion/Response");
throw new OneLogin_Saml2_ValidationError(
"Invalid issuer in the Assertion/Response (expected '$idPEntityId', got '$trimmedIssuer')",
OneLogin_Saml2_ValidationError::WRONG_ISSUER
);
}
}
// Check the session Expiration
$sessionExpiration = $this->getSessionNotOnOrAfter();
if (!empty($sessionExpiration) && $sessionExpiration <= time()) {
throw new Exception("The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response");
if (!empty($sessionExpiration) && $sessionExpiration + OneLogin_Saml2_Constants::ALLOWED_CLOCK_DRIFT <= time()) {
throw new OneLogin_Saml2_ValidationError(
"The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response",
OneLogin_Saml2_ValidationError::SESSION_EXPIRED
);
}
// Check the SubjectConfirmation, at least one SubjectConfirmation must be valid
@ -239,7 +298,7 @@ class OneLogin_Saml2_Response
$scnData = $subjectConfirmationDataNodes->item(0);
if ($scnData->hasAttribute('InResponseTo')) {
$inResponseTo = $scnData->getAttribute('InResponseTo');
if ($responseInResponseTo != $inResponseTo) {
if (isset($responseInResponseTo) && $responseInResponseTo != $inResponseTo) {
continue;
}
}
@ -251,31 +310,45 @@ class OneLogin_Saml2_Response
}
if ($scnData->hasAttribute('NotOnOrAfter')) {
$noa = OneLogin_Saml2_Utils::parseSAML2Time($scnData->getAttribute('NotOnOrAfter'));
if ($noa <= time()) {
if ($noa + OneLogin_Saml2_Constants::ALLOWED_CLOCK_DRIFT <= time()) {
continue;
}
}
if ($scnData->hasAttribute('NotBefore')) {
$nb = OneLogin_Saml2_Utils::parseSAML2Time($scnData->getAttribute('NotBefore'));
if ($nb > time()) {
if ($nb > time() + OneLogin_Saml2_Constants::ALLOWED_CLOCK_DRIFT) {
continue;
}
}
// Save NotOnOrAfter value
if ($scnData->hasAttribute('NotOnOrAfter')) {
$this->_validSCDNotOnOrAfter = $noa;
}
$anySubjectConfirmation = true;
break;
}
}
if (!$anySubjectConfirmation) {
throw new Exception("A valid SubjectConfirmation was not found on this Response");
throw new OneLogin_Saml2_ValidationError(
"A valid SubjectConfirmation was not found on this Response",
OneLogin_Saml2_ValidationError::WRONG_SUBJECTCONFIRMATION
);
}
if ($security['wantAssertionsSigned'] && !$hasSignedAssertion) {
throw new Exception("The Assertion of the Response is not signed and the SP requires it");
throw new OneLogin_Saml2_ValidationError(
"The Assertion of the Response is not signed and the SP requires it",
OneLogin_Saml2_ValidationError::NO_SIGNED_ASSERTION
);
}
if ($security['wantMessagesSigned'] && !$hasSignedResponse) {
throw new Exception("The Message of the Response is not signed and the SP requires it");
throw new OneLogin_Saml2_ValidationError(
"The Message of the Response is not signed and the SP requires it",
OneLogin_Saml2_ValidationError::NO_SIGNED_MESSAGE
);
}
}
@ -283,12 +356,18 @@ class OneLogin_Saml2_Response
if ($this->encrypted) {
$encryptedIDNodes = OneLogin_Saml2_Utils::query($this->decryptedDocument, '/samlp:Response/saml:Assertion/saml:Subject/saml:EncryptedID');
if ($encryptedIDNodes->length > 0) {
throw new Exception('Unsigned SAML Response that contains a signed and encrypted Assertion with encrypted nameId is not supported.');
throw new OneLogin_Saml2_ValidationError(
'Unsigned SAML Response that contains a signed and encrypted Assertion with encrypted nameId is not supported.',
OneLogin_Saml2_ValidationError::NOT_SUPPORTED
);
}
}
if (empty($signedElements) || (!$hasSignedResponse && !$hasSignedAssertion)) {
throw new Exception('No Signature found. SAML Response rejected');
throw new OneLogin_Saml2_ValidationError(
'No Signature found. SAML Response rejected',
OneLogin_Saml2_ValidationError::NO_SIGNATURE_FOUND
);
} else {
$cert = $idpData['x509cert'];
$fingerprint = $idpData['certFingerprint'];
@ -296,13 +375,19 @@ class OneLogin_Saml2_Response
# If find a Signature on the Response, validates it checking the original response
if ($hasSignedResponse && !OneLogin_Saml2_Utils::validateSign($this->document, $cert, $fingerprint, $fingerprintalg, OneLogin_Saml2_Utils::RESPONSE_SIGNATURE_XPATH)) {
throw new Exception("Signature validation failed. SAML Response rejected");
throw new OneLogin_Saml2_ValidationError(
"Signature validation failed. SAML Response rejected",
OneLogin_Saml2_ValidationError::INVALID_SIGNATURE
);
}
# If find a Signature on the Assertion (decrypted assertion if was encrypted)
$documentToCheckAssertion = $this->encrypted ? $this->decryptedDocument : $this->document;
if ($hasSignedAssertion && !OneLogin_Saml2_Utils::validateSign($documentToCheckAssertion, $cert, $fingerprint, $fingerprintalg, OneLogin_Saml2_Utils::ASSERTION_SIGNATURE_XPATH)) {
throw new Exception("Signature validation failed. SAML Response rejected");
throw new OneLogin_Saml2_ValidationError(
"Signature validation failed. SAML Response rejected",
OneLogin_Saml2_ValidationError::INVALID_SIGNATURE
);
}
}
return true;
@ -316,9 +401,47 @@ class OneLogin_Saml2_Response
}
}
/**
* @return the ID of the Response
*/
public function getId()
{
$id = null;
if ($this->document->documentElement->hasAttribute('ID')) {
$id = $this->document->documentElement->getAttribute('ID');
}
return $id;
}
/**
* @return the ID of the assertion in the Response
*/
public function getAssertionId()
{
if (!$this->validateNumAssertions()) {
throw new IllegalArgumentException("SAML Response must contain 1 Assertion.");
}
$assertionNodes = $this->_queryAssertion("");
$id = null;
if ($assertionNodes->length == 1) {
if ($assertionNodes->item(0)->hasAttribute('ID')) {
$id = $assertionNodes->item(0)->getAttribute('ID');
}
}
return $id;
}
/**
* @return the NotOnOrAfter value of the valid SubjectConfirmationData * node if any
*/
public function getAssertionNotOnOrAfter()
{
return $this->_validSCDNotOnOrAfter;
}
/**
* Checks if the Status is success
*
*
* @throws $statusExceptionMsg If status is not success
*/
public function checkStatus()
@ -333,8 +456,10 @@ class OneLogin_Saml2_Response
if (!empty($status['msg'])) {
$statusExceptionMsg .= ' -> '.$status['msg'];
}
throw new Exception($statusExceptionMsg);
throw new OneLogin_Saml2_ValidationError(
$statusExceptionMsg,
OneLogin_Saml2_ValidationError::STATUS_CODE_IS_NOT_SUCCESS
);
}
}
@ -370,7 +495,7 @@ class OneLogin_Saml2_Response
/**
* Gets the audiences.
*
*
* @return array @audience The valid audiences of the response
*/
public function getAudiences()
@ -390,7 +515,7 @@ class OneLogin_Saml2_Response
/**
* Gets the Issuers (from Response and Assertion).
*
*
* @return array @issuers The issuers of the assertion/response
*/
public function getIssuers()
@ -398,17 +523,25 @@ class OneLogin_Saml2_Response
$issuers = array();
$responseIssuer = OneLogin_Saml2_Utils::query($this->document, '/samlp:Response/saml:Issuer');
if ($responseIssuer->length == 1) {
$issuers[] = $responseIssuer->item(0)->textContent;
} else {
throw new Exception("Issuer of the Response not found or multiple.");
if ($responseIssuer->length > 0) {
if ($responseIssuer->length == 1) {
$issuers[] = $responseIssuer->item(0)->textContent;
} else {
throw new OneLogin_Saml2_ValidationError(
"Issuer of the Response is multiple.",
OneLogin_Saml2_ValidationError::ISSUER_MULTIPLE_IN_RESPONSE
);
}
}
$assertionIssuer = $this->_queryAssertion('/saml:Issuer');
if ($assertionIssuer->length == 1) {
$issuers[] = $assertionIssuer->item(0)->textContent;
} else {
throw new Exception("Issuer of the Assertion not found or multiple.");
throw new OneLogin_Saml2_ValidationError(
"Issuer of the Assertion not found or multiple.",
OneLogin_Saml2_ValidationError::ISSUER_NOT_FOUND_IN_ASSERTION
);
}
return array_unique($issuers);
@ -444,11 +577,17 @@ class OneLogin_Saml2_Response
if (!isset($nameId)) {
$security = $this->_settings->getSecurityData();
if ($security['wantNameId']) {
throw new Exception("Not NameID found in the assertion of the Response");
throw new OneLogin_Saml2_ValidationError(
"NameID not found in the assertion of the Response",
OneLogin_Saml2_ValidationError::NO_NAMEID
);
}
} else {
if ($this->_settings->isStrict() && empty($nameId->nodeValue)) {
throw new Exception("An empty NameID value found");
throw new OneLogin_Saml2_ValidationError(
"An empty NameID value found",
OneLogin_Saml2_ValidationError::EMPTY_NAMEID
);
}
$nameIdData['Value'] = $nameId->nodeValue;
@ -458,7 +597,10 @@ class OneLogin_Saml2_Response
$spData = $this->_settings->getSPData();
$spEntityId = $spData['entityId'];
if ($spEntityId != $nameId->getAttribute($attr)) {
throw new Exception("The SPNameQualifier value mistmatch the SP entityID value.");
throw new OneLogin_Saml2_ValidationError(
"The SPNameQualifier value mistmatch the SP entityID value.",
OneLogin_Saml2_ValidationError::SP_NAME_QUALIFIER_NAME_MISMATCH
);
}
}
$nameIdData[$attr] = $nameId->getAttribute($attr);
@ -502,7 +644,7 @@ class OneLogin_Saml2_Response
/**
* Gets the SessionNotOnOrAfter from the AuthnStatement.
* Could be used to set the local session expiration
*
*
* @return int|null The SessionNotOnOrAfter value
*/
public function getSessionNotOnOrAfter()
@ -520,7 +662,7 @@ class OneLogin_Saml2_Response
* Could be used to be stored in the local session in order
* to be used in a future Logout Request that the SP could
* send to the SP, to set what specific session must be deleted
*
*
* @return string|null The SessionIndex value
*/
@ -536,7 +678,7 @@ class OneLogin_Saml2_Response
/**
* Gets the Attributes from the AttributeStatement element.
*
*
* @return array The attributes of the SAML Assertion
*/
public function getAttributes()
@ -564,7 +706,10 @@ class OneLogin_Saml2_Response
$attributeName = $entry->attributes->getNamedItem('Name')->nodeValue;
if (in_array($attributeName, array_keys($attributes))) {
throw new Exception("Found an Attribute element with duplicated Name");
throw new OneLogin_Saml2_ValidationError(
"Found an Attribute element with duplicated Name",
OneLogin_Saml2_ValidationError::DUPLICATED_ATTRIBUTE_NAME_FOUND
);
}
$attributeValues = array();
@ -619,24 +764,32 @@ class OneLogin_Saml2_Response
$signNodes = $this->document->getElementsByTagName('Signature');
}
foreach ($signNodes as $signNode) {
$responseTag = '{'.OneLogin_Saml2_Constants::NS_SAMLP.'}Response';
$assertionTag = '{'.OneLogin_Saml2_Constants::NS_SAML.'}Assertion';
$signedElement = '{'.$signNode->parentNode->namespaceURI.'}'.$signNode->parentNode->localName;
if ($signedElement != $responseTag && $signedElement != $assertionTag) {
throw new Exception('Invalid Signature Element '.$signedElement.' SAML Response rejected');
throw new OneLogin_Saml2_ValidationError(
"Invalid Signature Element $signedElement SAML Response rejected",
OneLogin_Saml2_ValidationError::WRONG_SIGNED_ELEMENT
);
}
# Check that reference URI matches the parent ID and no duplicate References or IDs
$idValue = $signNode->parentNode->getAttribute('ID');
if (empty($idValue)) {
throw new Exception('Signed Element must contain an ID. SAML Response rejected');
throw new OneLogin_Saml2_ValidationError(
'Signed Element must contain an ID. SAML Response rejected',
OneLogin_Saml2_ValidationError::ID_NOT_FOUND_IN_SIGNED_ELEMENT
);
}
if (in_array($idValue, $verifiedIds)) {
throw new Exception('Duplicated ID. SAML Response rejected');
throw new OneLogin_Saml2_ValidationError(
'Duplicated ID. SAML Response rejected',
OneLogin_Saml2_ValidationError::DUPLICATED_ID_IN_SIGNED_ELEMENTS
);
}
$verifiedIds[] = $idValue;
@ -648,16 +801,25 @@ class OneLogin_Saml2_Response
$sei = substr($sei, 1);
if ($sei != $idValue) {
throw new Exception('Found an invalid Signed Element. SAML Response rejected');
throw new OneLogin_Saml2_ValidationError(
'Found an invalid Signed Element. SAML Response rejected',
OneLogin_Saml2_ValidationError::INVALID_SIGNED_ELEMENT
);
}
if (in_array($sei, $verifiedSeis)) {
throw new Exception('Duplicated Reference URI. SAML Response rejected');
throw new OneLogin_Saml2_ValidationError(
'Duplicated Reference URI. SAML Response rejected',
OneLogin_Saml2_ValidationError::DUPLICATED_REFERENCE_IN_SIGNED_ELEMENTS
);
}
$verifiedSeis[] = $sei;
}
} else {
throw new Exception('Unexpected number of Reference nodes found for signature. SAML Response rejected.');
throw new OneLogin_Saml2_ValidationError(
'Unexpected number of Reference nodes found for signature. SAML Response rejected.',
OneLogin_Saml2_ValidationError::UNEXPECTED_REFERENCE
);
}
$signedElements[] = $signedElement;
}
@ -665,7 +827,10 @@ class OneLogin_Saml2_Response
if (!empty($signedElements)) {
// Check SignedElements
if (!$this->validateSignedElements($signedElements)) {
throw new Exception('Found an unexpected Signature Element. SAML Response rejected');
throw new OneLogin_Saml2_ValidationError(
'Found an unexpected Signature Element. SAML Response rejected',
OneLogin_Saml2_ValidationError::UNEXPECTED_SIGNED_ELEMENTS
);
}
}
return $signedElements;
@ -689,10 +854,16 @@ class OneLogin_Saml2_Response
$nbAttribute = $timestampNodes->item($i)->attributes->getNamedItem("NotBefore");
$naAttribute = $timestampNodes->item($i)->attributes->getNamedItem("NotOnOrAfter");
if ($nbAttribute && OneLogin_SAML2_Utils::parseSAML2Time($nbAttribute->textContent) > time() + OneLogin_Saml2_Constants::ALLOWED_CLOCK_DRIFT) {
return false;
throw new OneLogin_Saml2_ValidationError(
'Could not validate timestamp: not yet valid. Check system clock.',
OneLogin_Saml2_ValidationError::ASSERTION_TOO_EARLY
);
}
if ($naAttribute && OneLogin_SAML2_Utils::parseSAML2Time($naAttribute->textContent) + OneLogin_Saml2_Constants::ALLOWED_CLOCK_DRIFT <= time()) {
return false;
throw new OneLogin_Saml2_ValidationError(
'Could not validate timestamp: expired. Check system clock.',
OneLogin_Saml2_ValidationError::ASSERTION_EXPIRED
);
}
}
return true;
@ -725,14 +896,20 @@ class OneLogin_Saml2_Response
if (in_array($responseTag, $signedElements)) {
$expectedSignatureNodes = OneLogin_Saml2_Utils::query($this->document, OneLogin_Saml2_Utils::RESPONSE_SIGNATURE_XPATH);
if ($expectedSignatureNodes->length != 1) {
throw new Exception("Unexpected number of Response signatures found. SAML Response rejected.");
throw new OneLogin_Saml2_ValidationError(
"Unexpected number of Response signatures found. SAML Response rejected.",
OneLogin_Saml2_ValidationError::WRONG_NUMBER_OF_SIGNATURES_IN_RESPONSE
);
}
}
if (in_array($assertionTag, $signedElements)) {
$expectedSignatureNodes = $this->_query(OneLogin_Saml2_Utils::ASSERTION_SIGNATURE_XPATH);
if ($expectedSignatureNodes->length != 1) {
throw new Exception("Unexpected number of Assertion signatures found. SAML Response rejected.");
throw new OneLogin_Saml2_ValidationError(
"Unexpected number of Assertion signatures found. SAML Response rejected.",
OneLogin_Saml2_ValidationError::WRONG_NUMBER_OF_SIGNATURES_IN_ASSERTION
);
}
}
@ -822,19 +999,28 @@ class OneLogin_Saml2_Response
$pem = $this->_settings->getSPkey();
if (empty($pem)) {
throw new Exception("No private key available, check settings");
throw new OneLogin_Saml2_Error(
"No private key available, check settings",
OneLogin_Saml2_Error::PRIVATE_KEY_NOT_FOUND
);
}
$objenc = new XMLSecEnc();
$encData = $objenc->locateEncryptedData($dom);
if (!$encData) {
throw new Exception("Cannot locate encrypted assertion");
throw new OneLogin_Saml2_ValidationError(
"Cannot locate encrypted assertion",
OneLogin_Saml2_ValidationError::MISSING_ENCRYPTED_ELEMENT
);
}
$objenc->setNode($encData);
$objenc->type = $encData->getAttribute("Type");
if (!$objKey = $objenc->locateKey()) {
throw new Exception("Unknown algorithm");
throw new OneLogin_Saml2_ValidationError(
"Unknown algorithm",
OneLogin_Saml2_ValidationError::KEY_ALGORITHM_ERROR
);
}
$key = null;
@ -858,7 +1044,6 @@ class OneLogin_Saml2_Response
if ($decrypted instanceof DOMDocument) {
return $decrypted;
} else {
$encryptedAssertion = $decrypted->parentNode;
$container = $encryptedAssertion->parentNode;
@ -869,10 +1054,9 @@ class OneLogin_Saml2_Response
!$container->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml') &&
!$container->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml2')
) {
if (strpos($encryptedAssertion->tagName, 'saml2:') !== false) {
$ns = 'xmlns:saml2';
} else if (strpos($encryptedAssertion->tagName, 'saml:') != false) {
} else if (strpos($encryptedAssertion->tagName, 'saml:') !== false) {
$ns = 'xmlns:saml';
} else {
$ns = 'xmlns';
@ -895,4 +1079,18 @@ class OneLogin_Saml2_Response
{
return $this->_error;
}
/*
* Returns the SAML Response document (If contains an encrypted assertion, decrypts it)
*
* @return DomDocument SAML Response
*/
public function getXMLDocument()
{
if ($this->encrypted) {
return $this->decryptedDocument;
} else {
return $this->document;
}
}
}

View File

@ -100,6 +100,7 @@ class OneLogin_Saml2_Settings
* @param array|object|null $settings SAML Toolkit Settings
*
* @throws OneLogin_Saml2_Error If any settings parameter is invalid
* @throws Exception If OneLogin_Saml2_Settings is incorrectly supplied
*/
public function __construct($settings = null, $spValidationOnly = false)
{
@ -123,6 +124,12 @@ class OneLogin_Saml2_Settings
array(implode(', ', $this->_errors))
);
}
} else if ($settings instanceof OneLogin_Saml2_Settings) {
throw new OneLogin_Saml2_Error(
'Only instances of OneLogin_Saml_Settings are supported.',
OneLogin_Saml2_Error::UNSUPPORTED_SETTINGS_OBJECT,
array(implode(', ', $this->_errors))
);
} else {
if (!$this->_loadSettingsFromArray($settings->getValues())) {
throw new OneLogin_Saml2_Error(
@ -364,6 +371,11 @@ class OneLogin_Saml2_Settings
$this->_security['wantNameId'] = true;
}
// Relax Destination validation
if (!isset($this->_security['relaxDestinationValidation'])) {
$this->_security['relaxDestinationValidation'] = false;
}
// encrypt expected
if (!isset($this->_security['wantAssertionsEncrypted'])) {
$this->_security['wantAssertionsEncrypted'] = false;
@ -377,11 +389,16 @@ class OneLogin_Saml2_Settings
$this->_security['wantXMLValidation'] = true;
}
// Algorithm
// SignatureAlgorithm
if (!isset($this->_security['signatureAlgorithm'])) {
$this->_security['signatureAlgorithm'] = XMLSecurityKey::RSA_SHA1;
}
// DigestAlgorithm
if (!isset($this->_security['digestAlgorithm'])) {
$this->_security['digestAlgorithm'] = XMLSecurityDSig::SHA1;
}
if (!isset($this->_security['lowercaseUrlencoding'])) {
$this->_security['lowercaseUrlencoding'] = false;
}
@ -828,7 +845,8 @@ class OneLogin_Saml2_Settings
}
$signatureAlgorithm = $this->_security['signatureAlgorithm'];
$metadata = OneLogin_Saml2_Metadata::signMetadata($metadata, $keyMetadata, $certMetadata, $signatureAlgorithm);
$digestAlgorithm = $this->_security['digestAlgorithm'];
$metadata = OneLogin_Saml2_Metadata::signMetadata($metadata, $keyMetadata, $certMetadata, $signatureAlgorithm, $digestAlgorithm);
}
return $metadata;
}

View File

@ -152,7 +152,7 @@ class OneLogin_Saml2_Utils
* @param string $cert A x509 unformated cert
* @param bool $heads True if we want to include head and footer
*
* @return string $x509 Formated cert
* @return string $x509 Formatted cert
*/
public static function formatCert($cert, $heads = true)
@ -177,7 +177,7 @@ class OneLogin_Saml2_Utils
* @param string $key A private key
* @param bool $heads True if we want to include head and footer
*
* @return string $rsaKey Formated private key
* @return string $rsaKey Formatted private key
*/
public static function formatPrivateKey($key, $heads = true)
@ -880,7 +880,7 @@ class OneLogin_Saml2_Utils
*
* @param string $x509cert x509 cert
*
* @return null|string Formated fingerprint
* @return null|string Formatted fingerprint
*/
public static function calculateX509Fingerprint($x509cert, $alg='sha1')
{
@ -928,7 +928,7 @@ class OneLogin_Saml2_Utils
*
* @param string $fingerprint fingerprint
*
* @return string Formated fingerprint
* @return string Formatted fingerprint
*/
public static function formatFingerPrint($fingerprint)
{
@ -1005,12 +1005,18 @@ class OneLogin_Saml2_Utils
$statusEntry = self::query($dom, '/samlp:Response/samlp:Status');
if ($statusEntry->length != 1) {
throw new Exception('Missing valid Status on response');
throw new OneLogin_Saml2_ValidationError(
"Missing Status on response",
OneLogin_Saml2_ValidationError::MISSING_STATUS
);
}
$codeEntry = self::query($dom, '/samlp:Response/samlp:Status/samlp:StatusCode', $statusEntry->item(0));
if ($codeEntry->length != 1) {
throw new Exception('Missing valid Status Code on response');
throw new OneLogin_Saml2_ValidationError(
"Missing Status Code on response",
OneLogin_Saml2_ValidationError::MISSING_STATUS_CODE
);
}
$code = $codeEntry->item(0)->getAttribute('Value');
$status['code'] = $code;
@ -1050,12 +1056,18 @@ class OneLogin_Saml2_Utils
$symmetricKey = $enc->locateKey($encryptedData);
if (!$symmetricKey) {
throw new Exception('Could not locate key algorithm in encrypted data.');
throw new OneLogin_Saml2_ValidationError(
'Could not locate key algorithm in encrypted data.',
OneLogin_Saml2_ValidationError::KEY_ALGORITHM_ERROR
);
}
$symmetricKeyInfo = $enc->locateKeyInfo($symmetricKey);
if (!$symmetricKeyInfo) {
throw new Exception('Could not locate <dsig:KeyInfo> for the encrypted key.');
throw new OneLogin_Saml2_ValidationError(
"Could not locate <dsig:KeyInfo> for the encrypted key.",
OneLogin_Saml2_ValidationError::KEYINFO_NOT_FOUND_IN_ENCRYPTED_DATA
);
}
$inputKeyAlgo = $inputKey->getAlgorithm();
@ -1067,11 +1079,12 @@ class OneLogin_Saml2_Utils
}
if ($inputKeyAlgo !== $symKeyInfoAlgo) {
throw new Exception(
throw new OneLogin_Saml2_ValidationError(
'Algorithm mismatch between input key and key used to encrypt ' .
' the symmetric key for the message. Key was: ' .
var_export($inputKeyAlgo, true) . '; message was: ' .
var_export($symKeyInfoAlgo, true)
var_export($symKeyInfoAlgo, true),
OneLogin_Saml2_ValidationError::KEY_ALGORITHM_ERROR
);
}
@ -1080,7 +1093,10 @@ class OneLogin_Saml2_Utils
$keySize = $symmetricKey->getSymmetricKeySize();
if ($keySize === null) {
// To protect against "key oracle" attacks
throw new Exception('Unknown key size for encryption algorithm: ' . var_export($symmetricKey->type, true));
throw new OneLogin_Saml2_ValidationError(
'Unknown key size for encryption algorithm: ' . var_export($symmetricKey->type, true),
OneLogin_Saml2_ValidationError::KEY_ALGORITHM_ERROR
);
}
$key = $encKey->decryptKey($symmetricKeyInfo);
@ -1101,10 +1117,11 @@ class OneLogin_Saml2_Utils
} else {
$symKeyAlgo = $symmetricKey->getAlgorithm();
if ($inputKeyAlgo !== $symKeyAlgo) {
throw new Exception(
throw new OneLogin_Saml2_ValidationError(
'Algorithm mismatch between input key and key in message. ' .
'Key was: ' . var_export($inputKeyAlgo, true) . '; message was: ' .
var_export($symKeyAlgo, true)
var_export($symKeyAlgo, true),
OneLogin_Saml2_ValidationError::KEY_ALGORITHM_ERROR
);
}
$symmetricKey = $inputKey;
@ -1118,12 +1135,18 @@ class OneLogin_Saml2_Utils
$newDoc->formatOutput = true;
$newDoc = self::loadXML($newDoc, $xml);
if (!$newDoc) {
throw new Exception('Failed to parse decrypted XML.');
throw new OneLogin_Saml2_ValidationError(
'Failed to parse decrypted XML.',
OneLogin_Saml2_ValidationError::INVALID_XML_FORMAT
);
}
$decryptedElement = $newDoc->firstChild->firstChild;
if ($decryptedElement === null) {
throw new Exception('Missing encrypted element.');
throw new OneLogin_Saml2_ValidationError(
'Missing encrypted element.',
OneLogin_Saml2_ValidationError::MISSING_ENCRYPTED_ELEMENT
);
}
return $decryptedElement;
@ -1167,12 +1190,13 @@ class OneLogin_Saml2_Utils
* @param string $key The private key
* @param string $cert The public
* @param string $signAlgorithm Signature algorithm method
* @param string $digestAlgorithm Digest algorithm method
*
* @return string
*
* @throws Exception
*/
public static function addSign($xml, $key, $cert, $signAlgorithm = XMLSecurityKey::RSA_SHA1)
public static function addSign($xml, $key, $cert, $signAlgorithm = XMLSecurityKey::RSA_SHA1, $digestAlgorithm = XMLSecurityDSig::SHA1)
{
if ($xml instanceof DOMDocument) {
$dom = $xml;
@ -1197,7 +1221,7 @@ class OneLogin_Saml2_Utils
$objXMLSecDSig->addReferenceList(
array($rootNode),
XMLSecurityDSig::SHA1,
$digestAlgorithm,
array('http://www.w3.org/2000/09/xmldsig#enveloped-signature', XMLSecurityDSig::EXC_C14N),
array('id_name' => 'ID')
);

View File

@ -188,7 +188,7 @@
<complexType name="X509IssuerSerialType">
<sequence>
<element name="X509IssuerName" type="string"/>
<element name="X509SerialNumber" type="integer"/>
<element name="X509SerialNumber" type="string"/>
</sequence>
</complexType>

View File

@ -1,6 +1,6 @@
{
"php-saml": {
"version": "2.10.2",
"released": "15/11/2016"
"version": "2.10.5",
"released": "13/03/2017"
}
}