Update SAML lib

This commit is contained in:
Lukas Reschke 2016-11-14 13:46:29 +01:00
parent 314ae475f6
commit f768e4339e
No known key found for this signature in database
GPG key ID: B9F6980CF6E759B1
21 changed files with 762 additions and 246 deletions

10
3rdparty/composer.lock generated vendored
View file

@ -9,16 +9,16 @@
"packages": [
{
"name": "onelogin/php-saml",
"version": "2.9.0",
"version": "2.10.1",
"source": {
"type": "git",
"url": "https://github.com/onelogin/php-saml.git",
"reference": "64aff7d58e68d98eaa9220e1041da2bc9214ab51"
"reference": "1017afe7fe6da1def37cc92af37434fbba893d03"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/onelogin/php-saml/zipball/64aff7d58e68d98eaa9220e1041da2bc9214ab51",
"reference": "64aff7d58e68d98eaa9220e1041da2bc9214ab51",
"url": "https://api.github.com/repos/onelogin/php-saml/zipball/1017afe7fe6da1def37cc92af37434fbba893d03",
"reference": "1017afe7fe6da1def37cc92af37434fbba893d03",
"shasum": ""
},
"require": {
@ -59,7 +59,7 @@
"onelogin",
"saml"
],
"time": "2016-06-27 09:24:27"
"time": "2016-10-26 11:31:56"
}
],
"packages-dev": [],

View file

@ -23,19 +23,26 @@ class ComposerAutoloaderInitcc75f134f7630c1ee3a8e4d7c86f3bcc
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitcc75f134f7630c1ee3a8e4d7c86f3bcc', 'loadClassLoader'));
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
call_user_func(\Composer\Autoload\ComposerStaticInitcc75f134f7630c1ee3a8e4d7c86f3bcc::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);

View file

@ -0,0 +1,37 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInitcc75f134f7630c1ee3a8e4d7c86f3bcc
{
public static $classMap = array (
'OneLogin_Saml2_Auth' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml2/Auth.php',
'OneLogin_Saml2_AuthnRequest' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml2/AuthnRequest.php',
'OneLogin_Saml2_Constants' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml2/Constants.php',
'OneLogin_Saml2_Error' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml2/Error.php',
'OneLogin_Saml2_LogoutRequest' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml2/LogoutRequest.php',
'OneLogin_Saml2_LogoutResponse' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml2/LogoutResponse.php',
'OneLogin_Saml2_Metadata' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml2/Metadata.php',
'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_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',
'OneLogin_Saml_Settings' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml/Settings.php',
'OneLogin_Saml_XmlSec' => __DIR__ . '/..' . '/onelogin/php-saml/lib/Saml/XmlSec.php',
'XMLSecEnc' => __DIR__ . '/..' . '/onelogin/php-saml/extlib/xmlseclibs/xmlseclibs.php',
'XMLSecurityDSig' => __DIR__ . '/..' . '/onelogin/php-saml/extlib/xmlseclibs/xmlseclibs.php',
'XMLSecurityKey' => __DIR__ . '/..' . '/onelogin/php-saml/extlib/xmlseclibs/xmlseclibs.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->classMap = ComposerStaticInitcc75f134f7630c1ee3a8e4d7c86f3bcc::$classMap;
}, null, ClassLoader::class);
}
}

View file

@ -1,17 +1,17 @@
[
{
"name": "onelogin/php-saml",
"version": "2.9.0",
"version_normalized": "2.9.0.0",
"version": "2.10.1",
"version_normalized": "2.10.1.0",
"source": {
"type": "git",
"url": "https://github.com/onelogin/php-saml.git",
"reference": "64aff7d58e68d98eaa9220e1041da2bc9214ab51"
"reference": "1017afe7fe6da1def37cc92af37434fbba893d03"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/onelogin/php-saml/zipball/64aff7d58e68d98eaa9220e1041da2bc9214ab51",
"reference": "64aff7d58e68d98eaa9220e1041da2bc9214ab51",
"url": "https://api.github.com/repos/onelogin/php-saml/zipball/1017afe7fe6da1def37cc92af37434fbba893d03",
"reference": "1017afe7fe6da1def37cc92af37434fbba893d03",
"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-06-27 09:24:27",
"time": "2016-10-26 11:31:56",
"type": "library",
"installation-source": "dist",
"autoload": {

View file

@ -10,15 +10,18 @@ php:
env:
- TRAVIS=true
matrix:
fast_finish: true
before_install:
- curl -s https://getcomposer.org/installer | php
- php composer.phar install --prefer-source --no-interaction
- composer self-update || true
- composer install --prefer-source --no-interaction
before_script:
- phpenv config-rm xdebug.ini
script:
- phpunit --bootstrap tests/bootstrap.php --configuration tests/phpunit.xml
- vendor/bin/phpunit --bootstrap tests/bootstrap.php --configuration tests/phpunit.xml
- php vendor/bin/phpcpd --exclude tests --exclude vendor .
- php vendor/bin/phploc . --exclude vendor
- php vendor/bin/phploc lib/.

View file

@ -1,5 +1,35 @@
CHANGELOG
=========
v.2.10.1
* Fix error message on SignMetadata process
* Fix issue on Assertion Signature validation when the assertion contains no namespace and it was encrypted
v.2.10.0
* Several security improvements:
* Conditions element required and unique.
* AuthnStatement element required and unique.
* SPNameQualifier must math the SP EntityID
* Reject saml:Attribute element with same “Name” attribute
* Reject empty nameID
* Require Issuer element. (Must match IdP EntityID).
* Destination value can't be blank (if present must match ACS URL).
* Check that the EncryptedAssertion element only contains 1 Assertion element.
* Improve Signature validation process
* AttributeConsumingService support
* Support lowercase Urlencoding (ADFS compatibility).
* [#154](https://github.com/onelogin/php-saml/pull/154) getSelfHost no longer returns a port number
* [#156](https://github.com/onelogin/php-saml/pull/156) Use correct host on response destination fallback check
* [#158](https://github.com/onelogin/php-saml/pull/158) NEW Control usage of X-Forwarded-* headers
* Fix issue with buildRequestSignature. Added RelayState to the SignQuery only if is not null.
* Add Signature Wrapping prevention Test
* Improve _decryptAssertion in order to take care of Assertions with problems with namespaces
* Improve documentation
v.2.9.1
.......
* [134](https://github.com/onelogin/php-saml/pull/134) PHP7 production settings compiles out assert(), throw an exception explicitly
* [132](https://github.com/onelogin/php-saml/pull/132) Add note for "wantAssertionsEncrypted"
* Update copyright on LICENSE
v.2.9.0
-------

View file

@ -1,19 +1,23 @@
Copyright (c) 2010-2014 OneLogin, LLC
Copyright (c) 2010-2016 OneLogin, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -7,14 +7,28 @@ Forget those complicated libraries and use that open source library provided
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.
php-saml < v2.10.0 is vulnerable and allows signature wrapping!
Security Guidelines
-------------------
If you believe you have discovered a security vulnerability in this toolkit, please report it at https://www.onelogin.com/security with a description. We follow responsible disclosure guidelines, and will work with you to quickly find a resolution.
Why add SAML support to my software?
------------------------------------
SAML is an XML-based standard for web browser single sign-on and is defined by
the OASIS Security Services Technical Committee. The standard has been around
the OASIS Security Services Technical Committee. The standard has been around
since 2002, but lately it is becoming popular due its advantages:
* **Usability** - One-click access from portals or intranets, deep linking,
* **Usability** - One-click access from portals or intranets, deep linking,
password elimination and automatically renewing sessions make life
easier for the user.
* **Security** - Based on strong digital signatures for authentication and
@ -27,7 +41,7 @@ since 2002, but lately it is becoming popular due its advantages:
* **IT Friendly** - SAML simplifies life for IT because it centralizes
authentication, provides greater visibility and makes directory
integration easier.
* **Opportunity** - B2B cloud vendor should support SAML to facilitate the
* **Opportunity** - B2B cloud vendor should support SAML to facilitate the
integration of their product.
@ -52,7 +66,7 @@ Key features:
* **saml2int** - Implements the SAML 2.0 Web Browser SSO Profile.
* **Session-less** - Forget those common conflicts between the SP and
the final app, the toolkit delegate session in the final app.
* **Easy to use** - Programmer will be allowed to code high-level and
* **Easy to use** - Programmer will be allowed to code high-level and
low-level programming, 2 easy to use APIs are available.
* **Tested** - Thoroughly tested.
* **Popular** - OneLogin's customers use it. Many PHP SAML plugins uses it.
@ -70,7 +84,7 @@ Installation
encrypted data (`nameID`, `assertions`).
* `gettext`. Install that library and its php driver. It handles translations.
Since [PHP 5.3 is officially unssuported](http://php.net/eol.php) we recommend you to use a newer PHP version.
Since [PHP 5.3 is officially unsupported](http://php.net/eol.php) we recommend you to use a newer PHP version.
### Code ###
@ -80,7 +94,7 @@ The toolkit is hosted on github. You can download it from:
* Lastest release: https://github.com/onelogin/php-saml/releases/latest
* Master repo: https://github.com/onelogin/php-saml/tree/master
Copy the core of the library inside the php application. (each application has its
structure so take your time to locate the PHP SAML toolkit in the best place).
See the "Guide to add SAML support to my app" to know how.
@ -99,7 +113,7 @@ After installation has completed you will find at the `vendor/` folder a new fol
**Important** In this option, the x509 certs must be stored at `vendor/onelogin/php-saml/certs`
and settings file stored at `vendor/onelogin/php-saml`.
Your settings are at risk of being deleted when updating packages using `composer update` or similiar commands. So is **highly** recommended that instead of use settings files, you pass the settings as an array directly to the constructor (explained later in this document). If you do not use this approach your settings are at risk of being deleted when updating packages using `composer update` or similiar commands.
Your settings are at risk of being deleted when updating packages using `composer update` or similiar commands. So it is **highly** recommended that instead of using settings files, you pass the settings as an array directly to the constructor (explained later in this document). If you do not use this approach your settings are at risk of being deleted when updating packages using `composer update` or similiar commands.
Compatibility
-------------
@ -114,7 +128,7 @@ The old-demo folder contains code from an old app that uses the old version of
the toolkit (v.1). Take a look.
Sometimes the names of the classes of the old code could be a bit different
and if is your case you must change them for `OneLogin_Saml_Settings`,
and if that is your case you must change them for `OneLogin_Saml_Settings`,
`OneLogin_Saml_Response`, `OneLogin_Saml_AuthRequest` or `OneLogin_Saml_Metadata`.
We recommend that you migrate the old code to the new one to be able to use
@ -134,16 +148,10 @@ start, for example to use the static method getSelfURLNoQuery use:
Security warning
----------------
In production, the `strict` parameter **MUST** be set as `"true"`. Otherwise
In production, the `strict` parameter **MUST** be set as `"true"`. Otherwise
your environment is not secure and will be exposed to attacks.
Security Guidelines
-------------------
If you believe you have discovered a security vulnerability in this toolkit, please report it at https://www.onelogin.com/security with a description. We follow responsible disclosure guidelines, and will work with you to quickly find a resolution.
Getting started
---------------
@ -219,12 +227,12 @@ and support multiple languages.
#### Other important files ####
* `settings_example_example.php` - A template to be used in order to create a
* `settings_example.php` - A template to be used in order to create a
settings.php file which contains the basic configuration info of the toolkit.
* `advanced_settings_example.php` - A template to be used in order to create a
* `advanced_settings_example.php` - A template to be used in order to create a
advanced_settings.php file which contains extra configuration info related to
the security, the contact person, and the organization associated to the SP.
* `_toolkit_loader.php` - This file load the toolkit libraries (The SAML2 lib).
* `_toolkit_loader.php` - This file load the toolkit libraries (The SAML2 lib).
* `compatibility` - Import that file to make compatible your old code with the
new toolkit (loads the SAML library).
@ -232,26 +240,26 @@ and support multiple languages.
#### Miscellaneous ####
* `tests/` - Contains the unit test of the toolkit.
* `demo1/` - Contains an example of a simple PHP app with SAML support.
* `demo1/` - Contains an example of a simple PHP app with SAML support.
Read the `Readme.txt` inside for more info.
* `demo2/` - Contains another example.
* `demo-old/` - Contains an example that uses the code of the older version of the
the toolkit to demonstrate the backwards compatibility.
### How it works ###
#### Settings ####
First of all we need to configure the toolkit. The SP's info, the IdP's info,
and in some cases, configure advanced security issues like signatures and
and in some cases, configure advanced security issues like signatures and
encryption.
There are two ways to provide the settings information:
* Use a `settings.php` file that we should locate at the base folder of the
toolkit.
* Use an array with the setting data and provide it directly to the
* Use an array with the setting data and provide it directly to the
constructor of the class.
@ -262,7 +270,7 @@ file, rename and edit it.
<?php
$settings = array (
// If 'strict' is True, then the PHP Toolkit will reject unsigned
// If 'strict' is True, then the PHP Toolkit will reject unsigned
// or unencrypted messages if it expects them to be signed or encrypted.
// Also it will reject the messages if the SAML standard is not strictly
// followed: Destination, NameId, Conditions ... are validated too.
@ -281,10 +289,26 @@ $settings = array (
// URL Location where the <Response> from the IdP will be returned
'url' => '',
// SAML protocol binding to be used when returning the <Response>
// message. OneLogin Toolkit supports this endpoint for the
// message. OneLogin Toolkit supports this endpoint for the
// HTTP-POST binding only.
'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
),
// If you need to specify requested attributes, set a
// attributeConsumingService. nameFormat, attributeValue and
// friendlyName can be omitted
"attributeConsumingService"=> array(
"serviceName" => "SP test",
"serviceDescription" => "Test Service",
"requestedAttributes" => array(
array(
"name" => "",
"isRequired" => false,
"nameFormat" => "",
"friendlyName" => "",
"attributeValue" => array()
)
)
),
// Specifies info about where and how the <Logout Response> message MUST be
// returned to the requester, in this case our SP.
'singleLogoutService' => array (
@ -312,7 +336,7 @@ $settings = array (
'entityId' => '',
// SSO endpoint info of the IdP. (Authentication Request protocol)
'singleSignOnService' => array (
// URL Target of the IdP where the Authentication Request Message
// URL Target of the IdP where the Authentication Request Message
// will be sent.
'url' => '',
// SAML protocol binding to be used when returning the <Response>
@ -332,7 +356,7 @@ $settings = array (
// Public x509 certificate of the IdP
'x509cert' => '',
/*
* Instead of use the whole x509cert you can use a fingerprint in order to
* Instead of use the whole x509cert you can use a fingerprint in order to
* validate a SAMLResponse.
* (openssl x509 -noout -fingerprint -in "idp.crt" to generate it,
* or add for example the -sha256 , -sha384 or -sha512 parameter)
@ -340,7 +364,7 @@ $settings = array (
* If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to
* let the toolkit know which algorithm was used. Possible values: sha1, sha256, sha384 or sha512
* 'sha1' is the default value.
*
*
* Notice that if you want to validate any SAML Message sent by the HTTP-Redirect binding, you
* will need to provide the whole x509cert.
*/
@ -349,9 +373,9 @@ $settings = array (
),
);
```
In addition to the required settings data (IdP, SP), there is extra
In addition to the required settings data (IdP, SP), there is extra
information that could be defined. In the same way that a template exists
for the basic info, there is a template for that advanced info located
for the basic info, there is a template for that advanced info located
at the base folder of the toolkit and named `advanced_settings_example.php`
that you can copy and rename it as `advanced_settings.php`
@ -360,6 +384,11 @@ that you can copy and rename it as `advanced_settings.php`
$advancedSettings = array (
// Compression settings
'compress' => array (
'requests' => true,
'responses' => true
),
// Security settings
'security' => array (
@ -369,15 +398,15 @@ $advancedSettings = array (
// will be encrypted.
'nameIdEncrypted' => false,
// Indicates whether the <samlp:AuthnRequest> messages sent by this SP
// Indicates whether the <samlp:AuthnRequest> messages sent by this SP
// will be signed. [Metadata of the SP will offer this info]
'authnRequestsSigned' => false,
// Indicates whether the <samlp:logoutRequest> messages sent by this SP
// Indicates whether the <samlp:logoutRequest> messages sent by this SP
// will be signed.
'logoutRequestSigned' => false,
// Indicates whether the <samlp:logoutResponse> messages sent by this SP
// Indicates whether the <samlp:logoutResponse> messages sent by this SP
// will be signed.
'logoutResponseSigned' => false,
@ -396,11 +425,15 @@ $advancedSettings = array (
// and <samlp:LogoutResponse> elements received by this SP to be signed.
'wantMessagesSigned' => false,
// Indicates a requirement for the <saml:Assertion> elements received by
// this SP to be encrypted.
'wantAssertionsEncrypted' => false,
// Indicates a requirement for the <saml:Assertion> elements received by
// this SP to be signed. [Metadata of the SP will offer this info]
'wantAssertionsSigned' => false,
// Indicates a requirement for the NameID element on the SAMLResponse
// Indicates a requirement for the NameID element on the SAMLResponse
// received by this SP to be present.
'wantNameId' => true,
@ -426,9 +459,13 @@ $advancedSettings = array (
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'
'signatureAlgorithm' => 'http://www.w3.org/2000/09/xmldsig#rsa-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,
),
// Contact information template, it is recommended to suply a
// Contact information template, it is recommended to supply a
// technical and support contacts.
'contactPerson' => array (
'technical' => array (
@ -453,6 +490,10 @@ $advancedSettings = array (
);
```
The compression settings allow you to instruct whether or not the IdP can accept
data that has been compressed using [gzip](gzip) ('requests' and 'responses').
But if we provide a $deflate boolean parameter to the getRequest or getResponse method it will have priority over the compression settings.
In the security section, you can set the way that the SP will handle the messages
and assertions. Contact the admin of the IdP and ask him what the IdP expects,
and decide what validations will handle the SP and what requirements the SP will have
@ -462,7 +503,7 @@ Once we know what kind of data could be configured, let's talk about the way
settings are handled within the toolkit.
The settings files described (`settings.php` and `advanced_settings.php`) are loaded
by the toolkit if not other array with settings info is provided in the constructors of the toolkit. Let's see some examples.
by the toolkit if not other array with settings info is provided in the constructors of the toolkit. Let's see some examples.
```php
// Initializes toolkit with settings.php & advanced_settings files.
@ -503,7 +544,7 @@ define("TOOLKIT_PATH", '/var/www/php-saml/');
require_once(TOOLKIT_PATH . '_toolkit_loader.php');
```
After that line we will be able to use the classes (and their methods) of the
After that line we will be able to use the classes (and their methods) of the
toolkit (because the external and the Saml2 libraries files are loaded).
If you wrote the code of your SAML app for the version 1 of the PHP-SAML toolkit
@ -515,7 +556,7 @@ toolkits but maintain the old classes, methods, and workflow of the old process
to accomplish the same things.
We strongly recommend migrating your old code and use the new API of the
new toolkit due there are a lot of new features that you can't handle with the
new toolkit due there are a lot of new features that you can't handle with the
old code.
@ -538,7 +579,7 @@ The `AuthNRequest` will be sent signed or unsigned based on the security info
of the `advanced_settings.php` (`'authnRequestsSigned'`).
The IdP will then return the SAML Response to the user's client. The client is then forwarded to the Attribute Consumer Service of the SP with this information. If we do not set a 'url' param in the login method and we are using the default ACS provided by the toolkit (`endpoints/acs.php`), then the ACS endpoint will redirect the user to the file that launched the SSO request.
The IdP will then return the SAML Response to the user's client. The client is then forwarded to the Attribute Consumer Service of the SP with this information. If we do not set a 'url' param in the login method and we are using the default ACS provided by the toolkit (`endpoints/acs.php`), then the ACS endpoint will redirect the user to the file that launched the SSO request.
We can set an `'returnTo'` url to change the workflow and redirect the user to the other PHP file.
@ -548,7 +589,7 @@ $auth = new OneLogin_Saml2_Auth();
$auth->login($newTargetUrl);
```
The login method can recieve other five optional parameters:
The login method can receive other five optional parameters:
* `$parameters` - An array of parameters that will be added to the `GET` in the HTTP-Redirect.
* `$forceAuthn` - When true the `AuthNRequest` will set the `ForceAuthn='true'`
@ -569,7 +610,7 @@ exit();
#### The SP Endpoints ####
Related to the SP there are three important views: The metadata view, the ACS view and the SLS view. The toolkit
Related to the SP there are three important views: The metadata view, the ACS view and the SLS view. The toolkit
provides examples of those views in the endpoints directory.
##### SP Metadata `endpoints/metadata.php` #####
@ -725,7 +766,7 @@ Array
)
```
Each attribute name can be used as an index into `$attributes` to obtain the value. Every attribute value
Each attribute name can be used as an index into `$attributes` to obtain the value. Every attribute value
is an array - a single-valued attribute is an array of a single element.
@ -838,7 +879,7 @@ if (!OneLogin_Saml2_LogoutRequest::isValid($this->_settings, $request)) {
}
```
If you aren't using the default PHP session, or otherwise need a manual
If you aren't using the default PHP session, or otherwise need a manual
way to destroy the session, you can pass a callback method to the
`processSLO` method as the fourth parameter
@ -877,7 +918,7 @@ $auth->logout(); // Method that sent the Logout Request.
Also there are three optional parameters that can be set:
* `$name_id` - That will be used to build the LogoutRequest. If `name_id` parameter is not set and the auth object processed a
* `$name_id` - That will be used to build the LogoutRequest. If `name_id` parameter is not set and the auth object processed a
SAML Response with a `NameId`, then this `NameId` will be used.
* `$session_index` - SessionIndex that identifies the session of the user.
* `$strict` - True if we want to stay (returns the url string) False to redirect.
@ -886,7 +927,7 @@ The Logout Request will be sent signed or unsigned based on the security
info of the `advanced_settings.php` (`'logoutRequestSigned'`).
The IdP will return the Logout Response through the user's client to the
Single Logout Service of the SP.
Single Logout Service of the SP.
If we do not set a `'url'` param in the logout method and are using the
default SLS provided by the toolkit (`endpoints/sls.php`), then the SLS
endpoint will redirect the user to the file that launched the SLO request.
@ -913,7 +954,7 @@ exit();
#### Example of a view that initiates the SSO request and handles the response (is the acs target) ####
We can code a unique file that initiates the SSO process, handle the response, get the attributes, initiate
We can code a unique file that initiates the SSO process, handle the response, get the attributes, initiate
the SLO and processes the logout response.
Note: Review the `demo1` folder that contains that use case; in a later section we
@ -934,31 +975,31 @@ $auth = new OneLogin_Saml2_Auth($settingsInfo); // Initialize the SP SAML insta
if (isset($_GET['sso'])) { // SSO action. Will send an AuthNRequest to the IdP
$auth->login();
} else if (isset($_GET['sso2'])) { // Another SSO action
} else if (isset($_GET['sso2'])) { // Another SSO action
$returnTo = $spBaseUrl.'/demo1/attrs.php'; // but set a custom RelayState URL
$auth->login($returnTo);
} else if (isset($_GET['slo'])) { // SLO action. Will sent a Logout Request to IdP
$auth->logout();
} else if (isset($_GET['acs'])) { // Assertion Consumer Service
$auth->processResponse(); // Process the Response of the IdP, get the
// attributes and put then at
// attributes and put then at
// $_SESSION['samlUserdata']
$errors = $auth->getErrors(); // This method receives an array with the errors
// that could took place during the process
// that could took place during the process
if (!empty($errors)) {
print_r('<p>'.implode(', ', $errors).'</p>');
}
// This check if the response was
// This check if the response was
if (!$auth->isAuthenticated()) { // sucessfully validated and the user
echo "<p>Not authenticated</p>"; // data retrieved or not
echo "<p>Not authenticated</p>"; // data retrieved or not
exit();
}
$_SESSION['samlUserdata'] = $auth->getAttributes(); // Retrieves user data
if (isset($_POST['RelayState']) && OneLogin_Saml2_Utils::getSelfURL() != $_POST['RelayState']) {
$auth->redirectTo($_POST['RelayState']); // Redirect if there is a
$auth->redirectTo($_POST['RelayState']); // Redirect if there is a
} // relayState set
} else if (isset($_GET['sls'])) { // Single Logout Service
$auth->processSLO(); // Process the Logout Request & Logout Response
@ -987,7 +1028,7 @@ if (isset($_SESSION['samlUserdata'])) { // If there is user data we print it.
echo "<p>You don't have any attribute</p>";
}
echo '<p><a href="?slo" >Logout</a></p>'; // Print some links with possible
echo '<p><a href="?slo" >Logout</a></p>'; // Print some links with possible
} else { // actions
echo '<p><a href="?sso" >Login</a></p>';
echo '<p><a href="?sso2" >Login and access to attrs.php page</a></p>';
@ -1034,7 +1075,7 @@ A simple class used to build the Setting object used in the v1.0 of the toolkit.
##### OneLogin_Saml_XmlSec - `XmlSec.php` #####
Auxiliary class that contains methos to validate the SAML Response:
Auxiliary class that contains methods to validate the SAML Response:
`validateNumAssertions`, `validateTimestamps`, `isValid` (which
uses the other two previous methods and also validate the signature of
SAML Response).
@ -1061,7 +1102,7 @@ Main class of OneLogin PHP Toolkit
* `getAttribute` - Returns the requested SAML attribute
* `getNameId` - Returns the nameID
* `getSessionIndex` - Gets the SessionIndex from the AuthnStatement.
* `getErrors` - Returns if there were any error
* `getErrors` - Returns if there were any error
* `getSSOurl` - Gets the SSO url.
* `getSLOurl` - Gets the SLO url.
* `getLastRequestID` - The ID of the last Request SAML message generated.
@ -1098,7 +1139,7 @@ SAML 2 Authentication Response class
Assertion (encrypted or not).
* `validateTimestamps` - Verifies that the document is still valid according
Conditions Element.
* `getError` - After execute a validation process, if fails this method returns the cause
* `getError` - After executing a validation process, if it fails, this method returns the cause
##### OneLogin_Saml2_LogoutRequest - `LogoutRequest.php` #####
@ -1111,8 +1152,8 @@ SAML 2 Logout Request class
* `getNameId` - Gets the NameID of the Logout Request.
* `getIssuer` - Gets the Issuer of the Logout Request.
* `getSessionIndexes` - Gets the SessionIndexes from the Logout Request.
* `isValid` - Checks if the Logout Request recieved is valid.
* `getError` - After execute a validation process, if fails this method returns the cause
* `isValid` - Checks if the Logout Request received is valid.
* `getError` - After executing a validation process, if it fails, this method returns the cause
##### OneLogin_Saml2_LogoutResponse - `LogoutResponse.php` #####
@ -1125,7 +1166,7 @@ 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 execute a validation process, if fails this method returns the cause
* `getError` - After executing a validation process, if it fails, this method returns the cause
##### OneLogin_Saml2_Settings - `Settings.php` #####
@ -1159,7 +1200,7 @@ Configuration of the OneLogin PHP Toolkit
* `setStrict` - Activates or deactivates the strict mode.
* `isStrict` - Returns if the 'strict' mode is active.
* `isDebugActive` - Returns if the debug is active.
##### OneLogin_Saml2_Metadata - `Metadata.php` #####
A class that contains functionality related to the metadata of the SP
@ -1181,15 +1222,15 @@ Auxiliary class that contains several methods
target url).
* `isHTTPS` - Checks if https or http.
* `getSelfHost` - Returns the current host.
* `getSelfURLhost` - Returns the protocol + the current host + the port
* `getSelfURLhost` - Returns the protocol + the current host + the port
(if different than common ports).
* `getSelfURLNoQuery` - Returns the URL of the current host + current view.
* `getSelfURL` - Returns the URL of the current host + current view + query.
* `generateUniqueID` - Generates an unique string (used for example as ID
* `generateUniqueID` - Generates a unique string (used for example as ID
for assertions).
* `parseTime2SAML` - Converts a UNIX timestamp to SAML2 timestamp on the
form `yyyy-mm-ddThh:mm:ss(\.s+)?Z`.
* `parseSAML2Time` - Converts a SAML2 timestamp on the form
* `parseSAML2Time` - Converts a SAML2 timestamp on the form
`yyyy-mm-ddThh:mm:ss(\.s+)?Z` to a UNIX timestamp. The sub-second part is
ignored.
* `parseDuration` - Interprets a ISO8601 duration value relative to a given
@ -1199,17 +1240,17 @@ Auxiliary class that contains several methods
* `isSessionStarted` - Checks if the session is started or not.
* `deleteLocalSession` - Deletes the local session.
* `calculateX509Fingerprint` - Calculates the fingerprint of a x509cert.
* `formatFingerPrint` - Formates a fingerprint.
* `formatFingerPrint` - Formats a fingerprint.
* `generateNameId` - Generates a `nameID`.
* `getStatus` - Gets Status from a Response.
* `decryptElement` - Decrypts an encrypted element.
* `castKey` - Converts a `XMLSecurityKey` to the correct algorithm.
* `addSign` - Adds signature key and senders certificate to an element
* `addSign` - Adds signature key and senders certificate to an element
(Message or Assertion).
* `validateSign` - Validates a signature (Message or Assertion).
For more info, look at the source code; each method is documented and details
about what does and how to use it are provided. Make sure to also check the doc folder where
about what it does and how to use it are provided. Make sure to also check the doc folder where
HTML documentation about the classes and methods is provided for SAML and
SAML2.
@ -1232,8 +1273,8 @@ The Onelogin's PHP Toolkit allows you to provide the settings info in two ways:
* Use an array with the setting data.
In this demo we provide the data in the second way, using a setting array named
`$settingsInfo`. This array users the `settings_example.php` included as a template
to create the `settings.php` settings and store it in the `demo1/` folder.
`$settingsInfo`. This array users the `settings_example.php` included as a template
to create the `settings.php` settings and store it in the `demo1/` folder.
Configure the SP part and later review the metadata of the IdP and complete the IdP info.
If you check the code of the index.php file you will see that the `settings.php`
@ -1241,7 +1282,7 @@ file is loaded in order to get the `$settingsInfo` var to be used in order to in
the `Setting` class.
Notice that in this demo, the `setting.php` file that could be defined at the base
folder of the toolkit is ignored and the libs are loaded using the
folder of the toolkit is ignored and the libs are loaded using the
`_toolkit_loader.php` located at the base folder of the toolkit.
@ -1257,41 +1298,41 @@ Once the SP is configured, the metadata of the SP is published at the
to the same view or login and be redirected to the `attrs.php` view.
2. When you click:
2.1 in the first link, we access to (`index.php?sso`) an `AuthNRequest`
is sent to the IdP, we authenticate at the IdP and then a Response is sent
through the user's client to the SP, specifically the Assertion Consumer Service view: `index.php?acs`.
Notice that a `RelayState` parameter is set to the url that initiated the
process, the `index.php` view.
2.2 in the second link we access to (`attrs.php`) have the same process
2.2 in the second link we access to (`attrs.php`) have the same process
described at 2.1 with the diference that as `RelayState` is set the `attrs.php`.
3. The SAML Response is processed in the ACS (`index.php?acs`), if the Response
is not valid, the process stops here and a message is shown. Otherwise we
are redirected to the RelayState view. a) `index.php` or b) `attrs.php`.
4. We are logged in the app and the user attributes are showed.
4. We are logged in the app and the user attributes are showed.
At this point, we can test the single log out functionality.
5. The single log out funcionality could be tested by two ways.
5. The single log out functionality could be tested by two ways.
5.1 SLO Initiated by SP. Click on the "logout" link at the SP, after that a
Logout Request is sent to the IdP, the session at the IdP is closed and
Logout Request is sent to the IdP, the session at the IdP is closed and
replies through the client to the SP with a Logout Response (sent to the
Single Logout Service endpoint). The SLS endpoint (`index.php?sls`) of the SP
process the Logout Response and if is valid, close the user session of the
local app. Notice that the SLO Workflow starts and ends at the SP.
5.2 SLO Initiated by IdP. In this case, the action takes place on the IdP
side, the logout process is initiated at the idP, sends a Logout
side, the logout process is initiated at the idP, sends a Logout
Request to the SP (SLS endpoint, `index.php?sls`). The SLS endpoint of the SP
process the Logout Request and if is valid, close the session of the user
at the local app and send a Logout Response to the IdP (to the SLS endpoint
of the IdP). The IdP receives the Logout Response, process it and close the
session at of the IdP. Notice that the SLO Workflow starts and ends at the IdP.
Notice that all the SAML Requests and Responses are handled at a unique file,
Notice that all the SAML Requests and Responses are handled by a unique file,
the `index.php` file and how `GET` paramters are used to know the action that
must be done.
@ -1306,7 +1347,7 @@ The Onelogin's PHP Toolkit allows you to provide the settings info in two ways:
toolkit.
* Use an array with the setting data.
The first is the case of the demo2 app. The `setting.php` file and the
The first is the case of the demo2 app. The `setting.php` file and the
`setting_extended.php` file should be defined at the base folder of the toolkit.
Review the `setting_example.php` and the `advanced_settings_example.php` to
learn how to build them.
@ -1340,17 +1381,17 @@ demo1, only changes the targets.
sent to the IdP automatically, (as `RelayState` is sent the origin url).
We authenticate at the IdP and then a `Response` is sent to the SP, to the
ACS endpoint, in this case `acs.php` of the endpoints folder.
2. The SAML Response is processed in the ACS, if the `Response` is not valid,
the process stops here and a message is shown. Otherwise we are redirected
to the `RelayState` view (`sso.php` or `index.php`). The `sso.php` detects if the
user is logged and redirects to `index.php`, so we will be in the
`index.php` at the end.
3. We are logged into the app and the user attributes (if any) are shown.
3. We are logged into the app and the user attributes (if any) are shown.
At this point, we can test the single log out functionality.
4. The single log out funcionality could be tested by two ways.
4. The single log out functionality could be tested by two ways.
4.1 SLO Initiated by SP. Click on the "logout" link at the SP, after that
we are redirected to the `slo.php` view and there a Logout Request is sent
@ -1359,9 +1400,9 @@ demo1, only changes the targets.
The SLS endpoint of the SP process the Logout Response and if is
valid, close the user session of the local app. Notice that the SLO
Workflow starts and ends at the SP.
5.2 SLO Initiated by IdP. In this case, the action takes place on the IdP
side, the logout process is initiated at the idP, sends a Logout
side, the logout process is initiated at the idP, sends a Logout
Request to the SP (SLS endpoint `sls.php` of the endpoint folder).
The SLS endpoint of the SP process the Logout Request and if is valid,
close the session of the user at the local app and sends a Logout Response
@ -1379,7 +1420,7 @@ An object of the class `OneLogin_Saml_Settings` must be provided to the
constructor of the `AuthRequest`.
You will find an `example_settings.php` file at the demo-old's folder that
could be used as a template for you `settings.php` file.
could be used as a template for your `settings.php` file.
In that template, SAML settings are divided into two parts, the application
specific (`const_assertion_consumer_service_url`, `const_issuer`,

View file

@ -2,6 +2,15 @@
$advancedSettings = array (
// Compression settings
// Handle if the getRequest/getResponse methods will return the Request/Response deflated.
// But if we provide a $deflate boolean parameter to the getRequest or getResponse
// method it will have priority over the compression settings.
'compress' => array (
'requests' => true,
'responses' => true
),
// Security settings
'security' => array (
@ -38,6 +47,10 @@ $advancedSettings = array (
// <samlp:LogoutResponse> elements received by this SP to be signed.
'wantMessagesSigned' => false,
// Indicates a requirement for the <saml:Assertion> elements received by
// this SP to be encrypted.
'wantAssertionsEncrypted' => false,
// Indicates a requirement for the <saml:Assertion> elements received by
// this SP to be signed. [The Metadata of the SP will offer this info]
'wantAssertionsSigned' => false,
@ -71,6 +84,10 @@ $advancedSettings = array (
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'
// 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'
'signatureAlgorithm' => 'http://www.w3.org/2000/09/xmldsig#rsa-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,
),
// Contact information template, it is recommended to suply a technical and support contacts

View file

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

View file

@ -102,7 +102,10 @@ class OneLogin_Saml2_Auth
*/
public function setStrict($value)
{
assert('is_bool($value)');
if (! (is_bool($value))) {
throw new Exception('Invalid value passed to setStrict()');
}
$this->_settings->setStrict($value);
}
@ -196,7 +199,7 @@ class OneLogin_Saml2_Auth
$security = $this->_settings->getSecurityData();
if (isset($security['logoutResponseSigned']) && $security['logoutResponseSigned']) {
$signature = $this->buildResponseSignature($logoutResponse, $parameters['RelayState'], $security['signatureAlgorithm']);
$signature = $this->buildResponseSignature($logoutResponse, isset($parameters['RelayState'])? $parameters['RelayState']: null, $security['signatureAlgorithm']);
$parameters['SigAlg'] = $security['signatureAlgorithm'];
$parameters['Signature'] = $signature;
}
@ -472,9 +475,20 @@ class OneLogin_Saml2_Auth
$objKey = new XMLSecurityKey($signAlgorithm, array('type' => 'private'));
$objKey->loadKey($key, false);
$msg = 'SAMLRequest='.urlencode($samlRequest);
$msg .= '&RelayState='.urlencode($relayState);
$msg .= '&SigAlg=' . urlencode($signAlgorithm);
$security = $this->_settings->getSecurityData();
if ($security['lowercaseUrlencoding']) {
$msg = 'SAMLRequest='.rawurlencode($samlRequest);
if (isset($relayState)) {
$msg .= '&RelayState='.rawurlencode($relayState);
}
$msg .= '&SigAlg=' . rawurlencode($signAlgorithm);
} else {
$msg = 'SAMLRequest='.urlencode($samlRequest);
if (isset($relayState)) {
$msg .= '&RelayState='.urlencode($relayState);
}
$msg .= '&SigAlg=' . urlencode($signAlgorithm);
}
$signature = $objKey->signData($msg);
return base64_encode($signature);
}
@ -505,9 +519,20 @@ class OneLogin_Saml2_Auth
$objKey = new XMLSecurityKey($signAlgorithm, array('type' => 'private'));
$objKey->loadKey($key, false);
$msg = 'SAMLResponse='.urlencode($samlResponse);
$msg .= '&RelayState='.urlencode($relayState);
$msg .= '&SigAlg=' . urlencode($signAlgorithm);
$security = $this->_settings->getSecurityData();
if ($security['lowercaseUrlencoding']) {
$msg = 'SAMLResponse='.rawurlencode($samlResponse);
if (isset($relayState)) {
$msg .= '&RelayState='.rawurlencode($relayState);
}
$msg .= '&SigAlg=' . rawurlencode($signAlgorithm);
} else {
$msg = 'SAMLResponse='.urlencode($samlResponse);
if (isset($relayState)) {
$msg .= '&RelayState='.urlencode($relayState);
}
$msg .= '&SigAlg=' . urlencode($signAlgorithm);
}
$signature = $objKey->signData($msg);
return base64_encode($signature);
}

View file

@ -137,12 +137,22 @@ 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()
public function getRequest($deflate = null)
{
$deflatedRequest = gzdeflate($this->_authnRequest);
$base64Request = base64_encode($deflatedRequest);
$subject = $this->_authnRequest;
if (is_null($deflate)) {
$deflate = $this->_settings->shouldCompressRequests();
}
if ($deflate) {
$subject = gzdeflate($this->_authnRequest);
}
$base64Request = base64_encode($subject);
return $base64Request;
}

View file

@ -21,7 +21,9 @@ 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;
/**
* Constructor
*

View file

@ -55,7 +55,7 @@ class OneLogin_Saml2_LogoutRequest
$nameIdValue = OneLogin_Saml2_Utils::generateUniqueID();
$issueInstant = OneLogin_Saml2_Utils::parseTime2SAML(time());
$cert = null;
if (isset($security['nameIdEncrypted']) && $security['nameIdEncrypted']) {
$cert = $idpData['x509cert'];
@ -110,12 +110,23 @@ LOGOUTREQUEST;
/**
* Returns the Logout Request defated, base64encoded, unsigned
*
* @param bool|null $deflate Whether or not we should 'gzdeflate' the request body before we return it.
*
* @return string Deflated base64 encoded Logout Request
*/
public function getRequest()
public function getRequest($deflate = null)
{
$deflatedRequest = gzdeflate($this->_logoutRequest);
return base64_encode($deflatedRequest);
$subject = $this->_logoutRequest;
if (is_null($deflate)) {
$deflate = $this->_settings->shouldCompressRequests();
}
if ($deflate) {
$subject = gzdeflate($this->_logoutRequest);
}
return base64_encode($subject);
}
/**
@ -143,7 +154,7 @@ LOGOUTREQUEST;
*
* @param string|DOMDocument $request Logout Request Message
* @param string|null $key The SP key
*
*
* @return array Name ID Data (Value, Format, NameQualifier, SPNameQualifier)
*
* @throws Exception
@ -235,11 +246,11 @@ LOGOUTREQUEST;
/**
* Gets the SessionIndexes from the Logout Request.
* Notice: Our Constructor only support 1 SessionIndex but this parser
* extracts an array of all the SessionIndex found on a
* extracts an array of all the SessionIndex found on a
* Logout Request, that could be many.
*
* @param string|DOMDocument $request Logout Request Message
*
*
* @return array The SessionIndex value
*/
public static function getSessionIndexes($request)
@ -283,7 +294,7 @@ LOGOUTREQUEST;
throw new Exception("Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd");
}
}
$currentURL = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
// Check NotOnOrAfter
@ -375,7 +386,7 @@ LOGOUTREQUEST;
/* After execute a validation process, if fails this method returns the cause
*
* @return string Cause
* @return string Cause
*/
public function getError()
{

View file

@ -71,13 +71,13 @@ class OneLogin_Saml2_LogoutResponse
/**
* Gets the Status of the Logout Response.
*
*
* @return string The Status
*/
public function getStatus()
{
$entries = $this->_query('/samlp:LogoutResponse/samlp:Status/samlp:StatusCode');
if ($entries->length == 0) {
if ($entries->length != 1) {
return null;
}
$status = $entries->item(0)->getAttribute('Value');
@ -213,7 +213,7 @@ class OneLogin_Saml2_LogoutResponse
/**
* Generates a Logout Response object.
*
* @param string $inResponseTo InResponseTo value for the Logout Response.
* @param string $inResponseTo InResponseTo value for the Logout Response.
*/
public function build($inResponseTo)
{
@ -244,18 +244,28 @@ 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()
public function getResponse($deflate = null)
{
$deflatedResponse = gzdeflate($this->_logoutResponse);
return base64_encode($deflatedResponse);
$subject = $this->_logoutResponse;
if (is_null($deflate)) {
$deflate = $this->_settings->shouldCompressResponses();
}
if ($deflate) {
$subject = gzdeflate($this->_logoutResponse);
}
return base64_encode($subject);
}
/* After execute a validation process, if fails this method returns the cause.
*
* @return string Cause
* @return string Cause
*/
public function getError()
{

View file

@ -1,5 +1,5 @@
<?php
/**
* Metadata lib of OneLogin PHP Toolkit
*
@ -58,6 +58,7 @@ SLS_TEMPLATE;
}
$strOrganization = '';
if (!empty($organization)) {
$organizationInfoNames = array();
$organizationInfoDisplaynames = array();
@ -96,6 +97,58 @@ CONTACT;
$strContacts = "\n".implode("\n", $contactsInfo);
}
$strAttributeConsumingService = '';
if (isset($sp['attributeConsumingService'])) {
$attrCsDesc = '';
if (isset($sp['attributeConsumingService']['serviceDescription'])) {
$attrCsDesc = sprintf(
' <md:ServiceDescription xml:lang="en">%s</md:ServiceDescription>' . PHP_EOL,
$sp['attributeConsumingService']['serviceDescription']
);
}
if (!isset($sp['attributeConsumingService']['serviceName'])) {
$sp['attributeConsumingService']['serviceName'] = 'Service';
}
$requestedAttributeData = array();
foreach ($sp['attributeConsumingService']['requestedAttributes'] as $attribute) {
$requestedAttributeStr = sprintf(' <md:RequestedAttribute Name="%s"', $attribute['name']);
if (isset($attribute['nameFormat'])) {
$requestedAttributeStr .= sprintf(' NameFormat="%s"', $attribute['nameFormat']);
}
if (isset($attribute['friendlyName'])) {
$requestedAttributeStr .= sprintf(' FriendlyName="%s"', $attribute['friendlyName']);
}
if (isset($attribute['isRequired'])) {
$requestedAttributeStr .= sprintf(' isRequired="%s"', $attribute['isRequired'] === true ? 'true' : 'false');
}
$reqAttrAuxStr = " />";
if (isset($attribute['attributeValue']) && !empty($attribute['attributeValue'])) {
$reqAttrAuxStr = '>';
if (is_string($attribute['attributeValue'])) {
$attribute['attributeValue'] = array($attribute['attributeValue']);
}
foreach ($attribute['attributeValue'] as $attrValue) {
$reqAttrAuxStr .=<<<ATTRIBUTEVALUE
<saml:AttributeValue xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">{$attrValue}</saml:AttributeValue>
ATTRIBUTEVALUE;
}
$reqAttrAuxStr .= "\n </md:RequestedAttribute>";
}
$requestedAttributeData[] = $requestedAttributeStr . $reqAttrAuxStr;
}
$requestedAttributeStr = implode(PHP_EOL, $requestedAttributeData);
$strAttributeConsumingService = <<<METADATA_TEMPLATE
<md:AttributeConsumingService index="1">
<md:ServiceName xml:lang="en">{$sp['attributeConsumingService']['serviceName']}</md:ServiceName>
{$attrCsDesc}{$requestedAttributeStr}
</md:AttributeConsumingService>
METADATA_TEMPLATE;
}
$metadata = <<<METADATA_TEMPLATE
<?xml version="1.0"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
@ -107,6 +160,7 @@ CONTACT;
<md:AssertionConsumerService Binding="{$sp['assertionConsumerService']['binding']}"
Location="{$sp['assertionConsumerService']['url']}"
index="1" />
{$strAttributeConsumingService}
</md:SPSSODescriptor>{$strOrganization}{$strContacts}
</md:EntityDescriptor>
METADATA_TEMPLATE;
@ -159,7 +213,7 @@ METADATA_TEMPLATE;
$keyInfo = $xml->createElementNS(OneLogin_Saml2_Constants::NS_DS, 'ds:KeyInfo');
$keyInfo->appendChild($keyData);
$keyDescriptor = $xml->createElementNS(OneLogin_Saml2_Constants::NS_MD, "md:KeyDescriptor");
$SPSSODescriptor = $xml->getElementsByTagName('SPSSODescriptor')->item(0);

View file

@ -69,7 +69,7 @@ class OneLogin_Saml2_Response
if ($encryptedAssertionNodes->length !== 0) {
$this->decryptedDocument = clone $this->document;
$this->encrypted = true;
$this->_decryptAssertion($this->decryptedDocument);
$this->decryptedDocument = $this->_decryptAssertion($this->decryptedDocument);
}
}
@ -109,6 +109,12 @@ class OneLogin_Saml2_Response
$signedElements = $this->processSignedElements();
$responseTag = '{'.OneLogin_Saml2_Constants::NS_SAMLP.'}Response';
$assertionTag = '{'.OneLogin_Saml2_Constants::NS_SAML.'}Assertion';
$hasSignedResponse = in_array($responseTag, $signedElements);
$hasSignedAssertion = in_array($assertionTag, $signedElements);
if ($this->_settings->isStrict()) {
$security = $this->_settings->getSecurityData();
@ -149,17 +155,27 @@ class OneLogin_Saml2_Response
if ($security['wantNameIdEncrypted']) {
$encryptedIdNodes = $this->_queryAssertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData');
if ($encryptedIdNodes->length == 0) {
if ($encryptedIdNodes->length != 1) {
throw new Exception("The NameID of the Response is not encrypted and the SP requires it");
}
}
// Validate Conditions element exists
if (!$this->checkOneCondition()) {
throw new Exception("The Assertion must include a Conditions element");
}
// Validate Asserion timestamps
$validTimestamps = $this->validateTimestamps();
if (!$validTimestamps) {
throw new Exception('Timing issues (please check your clock settings)');
}
// Validate AuthnStatement element exists and is unique
if (!$this->checkOneAuthnStatement()) {
throw new Exception("The Assertion must include an AuthnStatement element");
}
// EncryptedAttributes are not supported
$encryptedAttributeNodes = $this->_queryAssertion('/saml:AttributeStatement/saml:EncryptedAttribute');
if ($encryptedAttributeNodes->length > 0) {
@ -169,11 +185,13 @@ class OneLogin_Saml2_Response
// Check destination
if ($this->document->documentElement->hasAttribute('Destination')) {
$destination = trim($this->document->documentElement->getAttribute('Destination'));
if (!empty($destination)) {
if (empty($destination)) {
throw new Exception("The response has an empty Destination value");
} else {
if (strpos($destination, $currentURL) !== 0) {
$currentURLrouted = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
$currentURLNoRouted = OneLogin_Saml2_Utils::getSelfURLNoQuery();
if (strpos($destination, $currentURLrouted) !== 0) {
if (strpos($destination, $currentURLNoRouted) !== 0) {
throw new Exception("The response was received at $currentURL instead of $destination");
}
}
@ -247,42 +265,40 @@ class OneLogin_Saml2_Response
throw new Exception("A valid SubjectConfirmation was not found on this Response");
}
if ($security['wantAssertionsSigned'] && !in_array('Assertion', $signedElements)) {
if ($security['wantAssertionsSigned'] && !$hasSignedAssertion) {
throw new Exception("The Assertion of the Response is not signed and the SP requires it");
}
if ($security['wantMessagesSigned'] && !in_array('Response', $signedElements)) {
if ($security['wantMessagesSigned'] && !$hasSignedResponse) {
throw new Exception("The Message of the Response is not signed and the SP requires it");
}
}
if (!empty($signedElements)) {
// Detect case not supported
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.');
}
}
if (empty($signedElements) || (!$hasSignedResponse && !$hasSignedAssertion)) {
throw new Exception('No Signature found. SAML Response rejected');
} else {
$cert = $idpData['x509cert'];
$fingerprint = $idpData['certFingerprint'];
$fingerprintalg = $idpData['certFingerprintAlgorithm'];
# If find a Signature on the Response, validates it checking the original response
if (in_array('Response', $signedElements)) {
$documentToValidate = $this->document;
} else {
# Otherwise validates the assertion (decrypted assertion if was encrypted)
$documentToValidate = $this->decryptedDocument;
if ($this->encrypted) {
$documentToValidate = $this->decryptedDocument;
$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.');
}
} else {
$documentToValidate = $this->document;
}
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");
}
if (!OneLogin_Saml2_Utils::validateSign($documentToValidate, $cert, $fingerprint, $fingerprintalg)) {
throw new Exception('Signature validation failed. SAML Response rejected');
# 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");
}
} else {
throw new Exception('No Signature found. SAML Response rejected');
}
return true;
} catch (Exception $e) {
@ -317,6 +333,36 @@ class OneLogin_Saml2_Response
}
}
/**
* Checks that the samlp:Response/saml:Assertion/saml:Conditions element exists and is unique.
*
* @return boolean true if the Conditions element exists and is unique
*/
public function checkOneCondition()
{
$entries = $this->_queryAssertion("/saml:Conditions");
if ($entries->length == 1) {
return true;
} else {
return false;
}
}
/**
* Checks that the samlp:Response/saml:Assertion/saml:AuthnStatement element exists and is unique.
*
* @return boolean true if the AuthnStatement element exists and is unique
*/
public function checkOneAuthnStatement()
{
$entries = $this->_queryAssertion("/saml:AuthnStatement");
if ($entries->length == 1) {
return true;
} else {
return false;
}
}
/**
* Gets the audiences.
*
@ -346,14 +392,18 @@ class OneLogin_Saml2_Response
{
$issuers = array();
$responseIssuer = $this->_query('/samlp:Response/saml:Issuer');
$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.");
}
$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.");
}
return array_unique($issuers);
@ -392,9 +442,20 @@ class OneLogin_Saml2_Response
throw new Exception("Not NameID found in the assertion of the Response");
}
} else {
if ($this->_settings->isStrict() && empty($nameId->nodeValue)) {
throw new Exception("An empty NameID value found");
}
$nameIdData['Value'] = $nameId->nodeValue;
foreach (array('Format', 'SPNameQualifier', 'NameQualifier') as $attr) {
if ($nameId->hasAttribute($attr)) {
if ($this->_settings->isStrict() && $attr == 'SPNameQualifier') {
$spData = $this->_settings->getSPData();
$spEntityId = $spData['entityId'];
if ($spEntityId != $nameId->getAttribute($attr)) {
throw new Exception("The SPNameQualifier value mistmatch the SP entityID value.");
}
}
$nameIdData[$attr] = $nameId->getAttribute($attr);
}
}
@ -482,6 +543,10 @@ class OneLogin_Saml2_Response
foreach ($entries as $entry) {
$attributeName = $entry->attributes->getNamedItem('Name')->nodeValue;
if (in_array($attributeName, array_keys($attributes))) {
throw new Exception("Found an Attribute element with duplicated Name");
}
$attributeValues = array();
foreach ($entry->childNodes as $childNode) {
$tagName = ($childNode->prefix ? $childNode->prefix.':' : '') . 'AttributeValue';
@ -504,7 +569,15 @@ class OneLogin_Saml2_Response
{
$encryptedAssertionNodes = $this->document->getElementsByTagName('EncryptedAssertion');
$assertionNodes = $this->document->getElementsByTagName('Assertion');
return ($assertionNodes->length + $encryptedAssertionNodes->length == 1);
$valid = $assertionNodes->length + $encryptedAssertionNodes->length == 1;
if ($this->encrypted) {
$assertionNodes = $this->decryptedDocument->getElementsByTagName('Assertion');
$valid = $valid && $assertionNodes->length == 1;
}
return $valid;
}
/**
@ -526,9 +599,13 @@ class OneLogin_Saml2_Response
$signNodes = $this->document->getElementsByTagName('Signature');
}
foreach ($signNodes as $signNode) {
$signedElement = $signNode->parentNode->localName;
$responseTag = '{'.OneLogin_Saml2_Constants::NS_SAMLP.'}Response';
$assertionTag = '{'.OneLogin_Saml2_Constants::NS_SAML.'}Assertion';
if ($signedElement != 'Response' && $signedElement != 'Assertion') {
$signedElement = '{'.$signNode->parentNode->namespaceURI.'}'.$signNode->parentNode->localName;
if ($signedElement != $responseTag && $signedElement != $assertionTag) {
throw new Exception('Invalid Signature Element '.$signedElement.' SAML Response rejected');
}
@ -544,7 +621,7 @@ class OneLogin_Saml2_Response
$verifiedIds[] = $idValue;
$ref = $signNode->getElementsByTagName('Reference');
if (!empty($ref)) {
if ($ref->length == 1) {
$ref = $ref->item(0);
$sei = $ref->getAttribute('URI');
if (!empty($sei)) {
@ -559,6 +636,8 @@ class OneLogin_Saml2_Response
}
$verifiedSeis[] = $sei;
}
} else {
throw new Exception('Unexpected number of Reference nodes found for signature. SAML Response rejected.');
}
$signedElements[] = $signedElement;
}
@ -609,13 +688,34 @@ class OneLogin_Saml2_Response
if (count($signedElements) > 2) {
return false;
}
$responseTag = '{'.OneLogin_Saml2_Constants::NS_SAMLP.'}Response';
$assertionTag = '{'.OneLogin_Saml2_Constants::NS_SAML.'}Assertion';
$ocurrence = array_count_values($signedElements);
if ((in_array('Response', $signedElements) && $ocurrence['Response'] > 1) ||
(in_array('Assertion', $signedElements) && $ocurrence['Assertion'] > 1) ||
!in_array('Response', $signedElements) && !in_array('Assertion', $signedElements)
if ((in_array($responseTag, $signedElements) && $ocurrence[$responseTag] > 1) ||
(in_array($assertionTag, $signedElements) && $ocurrence[$assertionTag] > 1) ||
!in_array($responseTag, $signedElements) && !in_array($assertionTag, $signedElements)
) {
return false;
}
// Check that the signed elements found here, are the ones that will be verified
// by OneLogin_Saml2_Utils->validateSign()
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.");
}
}
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.");
}
}
return true;
}
@ -647,13 +747,13 @@ class OneLogin_Saml2_Response
if (!$assertionReferenceNode) {
// is the response signed as a whole?
$signatureQuery = '/samlp:Response/ds:Signature/ds:SignedInfo/ds:Reference';
$assertionReferenceNode = $xpath->query($signatureQuery)->item(0);
if ($assertionReferenceNode) {
$uri = $assertionReferenceNode->attributes->getNamedItem('URI')->nodeValue;
$responseReferenceNode = $xpath->query($signatureQuery)->item(0);
if ($responseReferenceNode) {
$uri = $responseReferenceNode->attributes->getNamedItem('URI')->nodeValue;
if (empty($uri)) {
$id = $assertionReferenceNode->parentNode->parentNode->parentNode->attributes->getNamedItem('ID')->nodeValue;
$id = $responseReferenceNode->parentNode->parentNode->parentNode->attributes->getNamedItem('ID')->nodeValue;
} else {
$id = substr($assertionReferenceNode->attributes->getNamedItem('URI')->nodeValue, 1);
$id = substr($responseReferenceNode->attributes->getNamedItem('URI')->nodeValue, 1);
}
$nameQuery = "/samlp:Response[@ID='$id']/saml:Assertion" . $assertionXpath;
} else {
@ -681,7 +781,11 @@ class OneLogin_Saml2_Response
*/
private function _query($query)
{
return OneLogin_Saml2_Utils::query($this->document, $query);
if ($this->encrypted) {
return OneLogin_Saml2_Utils::query($this->decryptedDocument, $query);
} else {
return OneLogin_Saml2_Utils::query($this->document, $query);
}
}
/**
@ -734,9 +838,17 @@ class OneLogin_Saml2_Response
if ($decrypted instanceof DOMDocument) {
return $decrypted;
} else {
$encryptedAssertion = $decrypted->parentNode;
$container = $encryptedAssertion->parentNode;
# Fix possible issue with saml namespace
if (!$decrypted->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml') &&
!$decrypted->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns') &&
!$container->hasAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:saml')) {
$decrypted->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns', OneLogin_Saml2_Constants::NS_SAML);
}
$container->replaceChild($decrypted, $encryptedAssertion);
return $decrypted->ownerDocument;

View file

@ -1,5 +1,5 @@
<?php
/**
* Configuration of the OneLogin PHP Toolkit
*
@ -15,7 +15,7 @@ class OneLogin_Saml2_Settings
private $_paths = array();
/**
* Strict. If active, PHP Toolkit will reject unsigned or unencrypted messages
* Strict. If active, PHP Toolkit will reject unsigned or unencrypted messages
* if it expects them signed or encrypted. If not, the messages will be accepted
* and some security issues will be also relaxed.
*
@ -44,6 +44,14 @@ class OneLogin_Saml2_Settings
*/
private $_idp = array();
/**
* Compression settings that determine
* whether gzip compression should be used.
*
* @var array
*/
private $_compress = array();
/**
* Security Info related to the SP.
*
@ -209,7 +217,7 @@ class OneLogin_Saml2_Settings
* Loads settings info from a settings Array
*
* @param array $settings SAML Toolkit Settings
*
*
* @return bool True if the settings info is valid
*/
private function _loadSettingsFromArray($settings)
@ -232,6 +240,10 @@ class OneLogin_Saml2_Settings
$this->_debug = $settings['debug'];
}
if (isset($settings['compress'])) {
$this->_compress = $settings['compress'];
}
if (isset($settings['security'])) {
$this->_security = $settings['security'];
}
@ -297,6 +309,14 @@ class OneLogin_Saml2_Settings
$this->_sp['singleLogoutService']['binding'] = OneLogin_Saml2_Constants::BINDING_HTTP_REDIRECT;
}
if (!isset($this->_compress['requests'])) {
$this->_compress['requests'] = true;
}
if (!isset($this->_compress['responses'])) {
$this->_compress['responses'] = true;
}
// Related to nameID
if (!isset($this->_sp['NameIDFormat'])) {
$this->_sp['NameIDFormat'] = OneLogin_Saml2_Constants::NAMEID_UNSPECIFIED;
@ -353,6 +373,10 @@ class OneLogin_Saml2_Settings
$this->_security['signatureAlgorithm'] = XMLSecurityKey::RSA_SHA1;
}
if (!isset($this->_security['lowercaseUrlencoding'])) {
$this->_security['lowercaseUrlencoding'] = false;
}
// Certificates / Private key /Fingerprint
if (!isset($this->_idp['x509cert'])) {
$this->_idp['x509cert'] = '';
@ -393,11 +417,45 @@ class OneLogin_Saml2_Settings
}
$spErrors = $this->checkSPSettings($settings);
$errors = array_merge($spErrors, $errors);
$compressErrors = $this->checkCompressionSettings($settings);
$errors = array_merge($compressErrors, $errors);
}
return $errors;
}
/**
* Checks the compression settings info.
*
* @param array $settings Array with settings data
*
* @return array $errors Errors found on the settings data
*/
public function checkCompressionSettings($settings)
{
$errors = array();
if (isset($settings['compress'])) {
if (!is_array($settings['compress'])) {
$errors[] = "invalid_syntax";
} else if (
isset($settings['compress']['requests'])
&& $settings['compress']['requests'] !== true
&& $settings['compress']['requests'] !== false
) {
$errors[] = "'compress'=>'requests' values must be true or false.";
} else if (
isset($settings['compress']['responses'])
&& $settings['compress']['responses'] !== true
&& $settings['compress']['responses'] !== false
) {
$errors[] = "'compress'=>'responses' values must be true or false.";
}
}
return $errors;
}
/**
* Checks the IdP settings info.
*
@ -665,6 +723,26 @@ class OneLogin_Saml2_Settings
return $this->_organization;
}
/**
* Should SAML requests be compressed?
*
* @return bool Yes/No as True/False
*/
public function shouldCompressRequests()
{
return $this->_compress['requests'];
}
/**
* Should SAML responses be compressed?
*
* @return bool Yes/No as True/False
*/
public function shouldCompressResponses()
{
return $this->_compress['responses'];
}
/**
* Gets the SP metadata. The XML representation.
*
@ -694,14 +772,14 @@ class OneLogin_Saml2_Settings
if (!$keyMetadata) {
throw new OneLogin_Saml2_Error(
'Private key not found.',
'SP Private key not found.',
OneLogin_Saml2_Error::PRIVATE_KEY_FILE_NOT_FOUND
);
}
if (!$certMetadata) {
throw new OneLogin_Saml2_Error(
'Public cert file not found.',
'SP Public cert not found.',
OneLogin_Saml2_Error::PUBLIC_CERT_FILE_NOT_FOUND
);
}
@ -719,19 +797,19 @@ class OneLogin_Saml2_Settings
$keyMetadataFile = $this->_paths['cert'].$keyFileName;
$certMetadataFile = $this->_paths['cert'].$certFileName;
if (!file_exists($keyMetadataFile)) {
throw new OneLogin_Saml2_Error(
'Private key file not found: %s',
'SP Private key file not found: %s',
OneLogin_Saml2_Error::PRIVATE_KEY_FILE_NOT_FOUND,
array($keyMetadataFile)
);
}
if (!file_exists($certMetadataFile)) {
throw new OneLogin_Saml2_Error(
'Public cert file not found: %s',
'SP Public cert file not found: %s',
OneLogin_Saml2_Error::PUBLIC_CERT_FILE_NOT_FOUND,
array($certMetadataFile)
);
@ -835,7 +913,9 @@ class OneLogin_Saml2_Settings
*/
public function setStrict($value)
{
assert('is_bool($value)');
if (! (is_bool($value))) {
throw new Exception('Invalid value passed to setStrict()');
}
$this->_strict = $value;
}

View file

@ -8,14 +8,22 @@
class OneLogin_Saml2_Utils
{
const RESPONSE_SIGNATURE_XPATH = "/samlp:Response/ds:Signature";
const ASSERTION_SIGNATURE_XPATH = "/samlp:Response/saml:Assertion/ds:Signature";
/**
* Translates any string. Accepts args
*
* @param string $msg Message to be translated
* @param array|null $args Arguments
*
* @return string $translatedMsg Translated text
*/
* @var bool Control if the `Forwarded-For-*` headers are used
*/
private static $_proxyVars = false;
/**
* Translates any string. Accepts args
*
* @param string $msg Message to be translated
* @param array|null $args Arguments
*
* @return string $translatedMsg Translated text
*/
public static function t($msg, $args = array())
{
assert('is_string($msg)');
@ -272,6 +280,22 @@ class OneLogin_Saml2_Utils
exit();
}
/**
* @param $proxyVars bool Whether to use `X-Forwarded-*` headers to determine port/domain/protocol
*/
public static function setProxyVars($proxyVars)
{
self::$_proxyVars = (bool)$proxyVars;
}
/**
* return bool
*/
public static function getProxyVars()
{
return self::$_proxyVars;
}
/**
* Returns the protocol + the current host + the port (if different than
* common ports).
@ -290,11 +314,7 @@ class OneLogin_Saml2_Utils
$protocol = 'http';
}
if (isset($_SERVER["HTTP_X_FORWARDED_PORT"])) {
$portnumber = $_SERVER["HTTP_X_FORWARDED_PORT"];
} else if (isset($_SERVER["SERVER_PORT"])) {
$portnumber = $_SERVER["SERVER_PORT"];
}
$portnumber = self::getSelfPort();
if (isset($portnumber) && ($portnumber != '80') && ($portnumber != '443')) {
$port = ':' . $portnumber;
@ -304,13 +324,10 @@ class OneLogin_Saml2_Utils
}
/**
* Returns the current host.
*
* @return string $currentHost The current host
* @return string The raw host name
*/
public static function getSelfHost()
protected static function getRawHost()
{
if (array_key_exists('HTTP_HOST', $_SERVER)) {
$currentHost = $_SERVER['HTTP_HOST'];
} elseif (array_key_exists('SERVER_NAME', $_SERVER)) {
@ -322,15 +339,48 @@ class OneLogin_Saml2_Utils
$currentHost = php_uname("n");
}
}
return $currentHost;
}
if (strstr($currentHost, ":")) {
$currentHostData = explode(":", $currentHost);
$possiblePort = array_pop($currentHostData);
if (is_numeric($possiblePort)) {
$currentHost = implode(':', $currentHostData);
/**
* Returns the current host.
*
* @return string $currentHost The current host
*/
public static function getSelfHost()
{
$currentHost = self::getRawHost();
// strip the port
if (false !== strpos($currentHost, ':')) {
list($currentHost, $port) = explode(':', $currentHost, 2);
}
return $currentHost;
}
/**
* @return null|string The port number used for the request
*/
public static function getSelfPort()
{
$portnumber = null;
if (self::getProxyVars() && isset($_SERVER["HTTP_X_FORWARDED_PORT"])) {
$portnumber = $_SERVER["HTTP_X_FORWARDED_PORT"];
} else if (isset($_SERVER["SERVER_PORT"])) {
$portnumber = $_SERVER["SERVER_PORT"];
} else {
$currentHost = self::getRawHost();
// strip the port
if (false !== strpos($currentHost, ':')) {
list($currentHost, $port) = explode(':', $currentHost, 2);
if (is_numeric($port)) {
$portnumber = $port;
}
}
}
return $currentHost;
return $portnumber;
}
/**
@ -341,8 +391,8 @@ class OneLogin_Saml2_Utils
public static function isHTTPS()
{
$isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|| (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443)
|| (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https');
|| (self::getSelfPort() == 443)
|| (self::getProxyVars() && isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https');
return $isHttps;
}
@ -792,26 +842,25 @@ class OneLogin_Saml2_Utils
$status = array();
$statusEntry = self::query($dom, '/samlp:Response/samlp:Status');
if ($statusEntry->length == 0) {
throw new Exception('Missing Status on response');
if ($statusEntry->length != 1) {
throw new Exception('Missing valid Status on response');
}
$codeEntry = self::query($dom, '/samlp:Response/samlp:Status/samlp:StatusCode', $statusEntry->item(0));
if ($codeEntry->length == 0) {
throw new Exception('Missing Status Code on response');
if ($codeEntry->length != 1) {
throw new Exception('Missing valid Status Code on response');
}
$code = $codeEntry->item(0)->getAttribute('Value');
$status['code'] = $code;
$status['msg'] = '';
$messageEntry = self::query($dom, '/samlp:Response/samlp:Status/samlp:StatusMessage', $statusEntry->item(0));
if ($messageEntry->length == 0) {
$subCodeEntry = self::query($dom, '/samlp:Response/samlp:Status/samlp:StatusCode/samlp:StatusCode', $statusEntry->item(0));
if ($subCodeEntry->length > 0) {
if ($subCodeEntry->length == 1) {
$status['msg'] = $subCodeEntry->item(0)->getAttribute('Value');
} else {
$status['msg'] = '';
}
} else {
} else if ($messageEntry->length == 1) {
$msg = $messageEntry->item(0)->textContent;
$status['msg'] = $msg;
}
@ -1021,12 +1070,13 @@ class OneLogin_Saml2_Utils
* @param string|null $cert The pubic cert
* @param string|null $fingerprint The fingerprint of the public cert
* @param string|null $fingerprintalg The algorithm used to get the fingerprint
* @param string|null $xpath The xpath of the signed element
*
* @return bool
*
* @throws Exception
*/
public static function validateSign($xml, $cert = null, $fingerprint = null, $fingerprintalg = 'sha1')
public static function validateSign($xml, $cert = null, $fingerprint = null, $fingerprintalg = 'sha1', $xpath=null)
{
if ($xml instanceof DOMDocument) {
$dom = clone $xml;
@ -1040,7 +1090,14 @@ class OneLogin_Saml2_Utils
$objXMLSecDSig = new XMLSecurityDSig();
$objXMLSecDSig->idKeys = array('ID');
$objDSig = $objXMLSecDSig->locateSignature($dom);
if ($xpath) {
$nodeset = OneLogin_Saml2_Utils::query($dom, $xpath);
$objDSig = $nodeset->item(0);
$objXMLSecDSig->sigNode = $objDSig;
} else {
$objDSig = $objXMLSecDSig->locateSignature($dom);
}
if (!$objDSig) {
throw new Exception('Cannot locate Signature Node');
}

View file

@ -1,6 +1,6 @@
{
"php-saml": {
"version": "2.9.0",
"released": "27/06/2016"
"version": "2.10.1",
"released": "26/10/2016"
}
}

View file

@ -24,6 +24,22 @@ $settings = array (
// HTTP-Redirect binding only
'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
),
// If you need to specify requested attributes, set a
// attributeConsumingService. nameFormat, attributeValue and
// friendlyName can be omitted. Otherwise remove this section.
"attributeConsumingService"=> array(
"ServiceName" => "SP test",
"serviceDescription" => "Test Service",
"requestedAttributes" => array(
array(
"name" => "",
"isRequired" => false,
"nameFormat" => "",
"friendlyName" => "",
"attributeValue" => ""
)
)
),
// Specifies info about where and how the <Logout Response> message MUST be
// returned to the requester, in this case our SP.
'singleLogoutService' => array (