Bump library to 2.10.3

Signed-off-by: Lukas Reschke <lukas@statuscode.ch>
This commit is contained in:
Lukas Reschke 2017-01-12 22:20:27 +01:00
parent 0535f8a496
commit 9040db160e
No known key found for this signature in database
GPG key ID: B9F6980CF6E759B1
17 changed files with 538 additions and 120 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.3",
"source": {
"type": "git",
"url": "https://github.com/onelogin/php-saml.git",
"reference": "f9543a05494633671ec587ae1611238dae6edfd4"
"reference": "eb6cfbca928106205558a596988923da0e47bd9d"
},
"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/eb6cfbca928106205558a596988923da0e47bd9d",
"reference": "eb6cfbca928106205558a596988923da0e47bd9d",
"shasum": ""
},
"require": {
@ -58,7 +58,7 @@
"onelogin",
"saml"
],
"time": "2016-11-15T15:34:53+00:00"
"time": "2017-01-11T17:18:41+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.3",
"version_normalized": "2.10.3.0",
"source": {
"type": "git",
"url": "https://github.com/onelogin/php-saml.git",
"reference": "f9543a05494633671ec587ae1611238dae6edfd4"
"reference": "eb6cfbca928106205558a596988923da0e47bd9d"
},
"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/eb6cfbca928106205558a596988923da0e47bd9d",
"reference": "eb6cfbca928106205558a596988923da0e47bd9d",
"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-01-11T17:18:41+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {

View file

@ -1,5 +1,13 @@
CHANGELOG
=========
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,7 @@ 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.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!
@ -1136,6 +1136,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 +1148,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 +1172,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 +1187,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 +1200,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

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

View file

@ -80,6 +80,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 +127,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);
@ -129,6 +149,7 @@ class OneLogin_Saml2_Auth
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();
@ -168,6 +189,7 @@ class OneLogin_Saml2_Auth
$this->_errors = array();
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();
@ -184,6 +206,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();
@ -198,6 +221,8 @@ class OneLogin_Saml2_Auth
$inResponseTo = $logoutRequest->id;
$responseBuilder = new OneLogin_Saml2_LogoutResponse($this->_settings);
$responseBuilder->build($inResponseTo);
$this->_lastResponse = $responseBuilder->getXML();
$logoutResponse = $responseBuilder->getResponse();
$parameters = array('SAMLResponse' => $logoutResponse);
@ -359,6 +384,7 @@ class OneLogin_Saml2_Auth
$authnRequest = new OneLogin_Saml2_AuthnRequest($this->_settings, $forceAuthn, $isPassive, $setNameIdPolicy);
$this->_lastRequest = $authnRequest->getXML();
$this->_lastRequestID = $authnRequest->getId();
$samlRequest = $authnRequest->getRequest();
@ -411,6 +437,7 @@ class OneLogin_Saml2_Auth
$logoutRequest = new OneLogin_Saml2_LogoutRequest($this->_settings, null, $nameId, $sessionIndex, $nameIdFormat);
$this->_lastRequest = $logoutRequest->getXML();
$this->_lastRequestID = $logoutRequest->id;
$samlRequest = $logoutRequest->getRequest();
@ -482,10 +509,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 +554,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 +582,37 @@ class OneLogin_Saml2_Auth
$signature = $objKey->signData($msg);
return base64_encode($signature);
}
/**
* 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

@ -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_NOT_FOUND_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

@ -182,7 +182,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 +201,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();
@ -298,7 +304,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 +317,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 +329,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,12 +342,18 @@ 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
);
}
}
}
@ -360,7 +381,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 +395,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');
throw new OneLogin_Saml2_ValidationError(
"Signature validation failed. Logout Request rejected",
OneLogin_Saml2_ValidationError::INVALID_SIGNATURE
);
}
}
@ -399,4 +429,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

@ -114,7 +114,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 +125,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 +148,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 +188,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 +202,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');
throw new OneLogin_Saml2_ValidationError(
"Signature validation failed. Logout Response rejected",
OneLogin_Saml2_ValidationError::INVALID_SIGNATURE
);
}
}
return true;
@ -277,4 +301,15 @@ LOGOUTRESPONSE;
{
return $this->_error;
}
/**
* 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

@ -66,7 +66,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 +96,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 +139,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 +167,75 @@ 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");
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
);
}
}
}
@ -206,23 +244,31 @@ 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");
throw new OneLogin_Saml2_ValidationError(
"$spEntityId is not a valid audience for this Response",
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",
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");
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 +285,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;
}
}
@ -267,15 +313,24 @@ class OneLogin_Saml2_Response
}
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 +338,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 +357,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;
@ -333,8 +400,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
);
}
}
@ -401,14 +470,20 @@ class OneLogin_Saml2_Response
if ($responseIssuer->length == 1) {
$issuers[] = $responseIssuer->item(0)->textContent;
} else {
throw new Exception("Issuer of the Response not found or multiple.");
throw new OneLogin_Saml2_ValidationError(
"Issuer of the Response not found or multiple.",
OneLogin_Saml2_ValidationError::ISSUER_NOT_FOUND_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 +519,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 +539,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);
@ -564,7 +648,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();
@ -626,17 +713,26 @@ class OneLogin_Saml2_Response
$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 +744,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 +770,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_ELEMENT
);
}
}
return $signedElements;
@ -689,10 +797,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 +839,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 +942,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;
@ -895,4 +1024,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(

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

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