From 2adf62656bfea03dd3415b4ea0942e422d07109d Mon Sep 17 00:00:00 2001 From: Olav Morken <olav.morken@uninett.no> Date: Mon, 27 Oct 2008 09:03:09 +0000 Subject: [PATCH] SAML20/HTTPRedirect: Make it possible to enable signing in both sp-hosted and idp-remote metadata git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@943 44740490-163a-0410-bde0-09ae8108e29a --- docs/source/simplesamlphp-idp.xml | 105 +++++++++++--- docs/source/simplesamlphp-sp.xml | 133 ++++++++++-------- .../Bindings/SAML20/HTTPRedirect.php | 85 ++++++++--- metadata-templates/saml20-idp-hosted.php | 9 -- metadata-templates/saml20-sp-hosted.php | 10 -- metadata-templates/saml20-sp-remote.php | 4 +- www/admin/metadata.php | 8 +- 7 files changed, 237 insertions(+), 117 deletions(-) diff --git a/docs/source/simplesamlphp-idp.xml b/docs/source/simplesamlphp-idp.xml index ca9575d19..b01f57f0a 100644 --- a/docs/source/simplesamlphp-idp.xml +++ b/docs/source/simplesamlphp-idp.xml @@ -726,28 +726,51 @@ openssl x509 -req -days 60 -in server2.csr -signkey server2.key -out server2.crt </section> <section> - <title>Fields for signing authentication requests</title> + <title>Fields for signing and validating messages</title> - <para>By default, simpleSAMLphp will not sign the HTTP-REDIRECT - LogoutRequest. To activate signing, set the - <literal>request.signing</literal> parameter to - <literal>true</literal>. The signing will use the same - privatekey/certificate as used for signing the AuthnResponse.</para> + <para>simpleSAMLphp only signs authentication responses by default. + Signing of logout requests and logout responses can be enabled by + setting the <literal>redirect.sign</literal> option. Validation of + received messages can be enabled by the + <literal>redirect.validate</literal> option.</para> + + <para>These options set the default for this IdP, but options for each + SP can be set in <literal>saml20-sp-remote</literal>.</para> <glosslist> <glossentry> - <glossterm>request.signing</glossterm> + <glossterm>redirect.sign</glossterm> <glossdef> - <para>Boolean, default <literal>false</literal>.</para> + <para>Boolean, default <literal>false</literal>. To turn on + signing of logout requests and logout responses, set this flag + to true.</para> + + <para>This option can be overridden by the + <literal>redirect.sign</literal> option in + <literal>saml20-sp-remote</literal>.</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>redirect.validate</glossterm> + + <glossdef> + <para>Boolean, default <literal>false</literal>. To turn on + validation of received authentication requests, logout requests + and logout responses, set this flag to true.</para> + + <para>This option can be overridden by the + <literal>redirect.validate</literal> option in + <literal>saml20-sp-remote</literal>.</para> </glossdef> </glossentry> </glosslist> <example> - <title>Example of configured signed requests</title> + <title>Example of configuration for signed messages</title> - <programlisting> 'request.signing' => true,</programlisting> + <programlisting> 'redirect.sign' => true,</programlisting> </example> </section> </section> @@ -959,21 +982,12 @@ openssl x509 -req -days 60 -in server2.csr -signkey server2.key -out server2.crt </glossdef> </glossentry> - <glossentry> - <glossterm>request.signing</glossterm> - - <glossdef> - <para>Boolean, default <literal>false</literal>. Defines whether - this IdP should require signed requests from this SP.</para> - </glossdef> - </glossentry> - <glossentry> <glossterm>certificate</glossterm> <glossdef> <para>Name of certificate file for verifying the signature when - <literal>request.signing</literal> is set to + <literal>redirect.validate</literal> is set to <literal>true</literal>.</para> </glossdef> </glossentry> @@ -1051,6 +1065,55 @@ openssl x509 -req -days 60 -in server2.csr -signkey server2.key -out server2.crt </glossentry> </glosslist> </section> + + <section> + <title>Fields for signing and validating messages</title> + + <para>simpleSAMLphp only signs authentication responses by default. + Signing of logout requests and logout responses can be enabled by + setting the <literal>redirect.sign</literal> option. Validation of + received messages can be enabled by the + <literal>redirect.validate</literal> option.</para> + + <para>These options overrides the options set in + <literal>saml20-idp-hosted</literal>.</para> + + <glosslist> + <glossentry> + <glossterm>redirect.sign</glossterm> + + <glossdef> + <para>Boolean, default <literal>false</literal>. To turn on + signing of logout requests and logout responses, set this flag + to true.</para> + + <para>This option overrides the <literal>redirect.sign</literal> + option in <literal>saml20-idp-hosted</literal>.</para> + </glossdef> + </glossentry> + + <glossentry> + <glossterm>redirect.validate</glossterm> + + <glossdef> + <para>Boolean, default <literal>false</literal>. To turn on + validation of received authentication requests, logout requests + and logout responses, set this flag to true.</para> + + <para>This option overrides the + <literal>redirect.validate</literal> option in + <literal>saml20-idp-hosted</literal>.</para> + </glossdef> + </glossentry> + </glosslist> + + <example> + <title>Example of configuration for validating messages</title> + + <programlisting>'redirect.validate' => true, +'certificate' => 'server.crt'</programlisting> + </example> + </section> </section> </section> @@ -1189,7 +1252,7 @@ openssl x509 -req -days 60 -in server2.csr -signkey server2.key -out server2.crt </itemizedlist> <para>In hosted IdP metadata there is a config parameter auth that will - tell simpleSAML which authentication plugin that can be used.</para> + tell simpleSAML which authentication plugin that can beu sed.</para> <tip> <para>The authentication API is pretty basic. The easiest way to diff --git a/docs/source/simplesamlphp-sp.xml b/docs/source/simplesamlphp-sp.xml index d412cac15..2f6455edb 100644 --- a/docs/source/simplesamlphp-sp.xml +++ b/docs/source/simplesamlphp-sp.xml @@ -293,61 +293,84 @@ used.</para> </glossdef> </glossentry> - </glosslist> - </section> - <section> - <title>Fields for signing authentication requests</title> + <glossentry> + <glossterm>privatekey</glossterm> - <para>simpleSAMLphp supports signing the HTTP-REDIRECT authentication - request, but by default it will not sign it. Note that if you want to - sign the authentication requests, you will need a keypair/certificate - at the SP.</para> + <glossdef> + <para>File name of private key to be used for signing + messages.</para> + </glossdef> + </glossentry> - <glosslist> <glossentry> - <glossterm>request.signing</glossterm> + <glossterm>certificate</glossterm> <glossdef> - <para>Boolean, default <literal>false</literal>. To turn on - signing of authentication requests, set this flag to - true.</para> + <para>File name of certificate corresponding to the private key. + This certificate will be included in generated metadata.</para> </glossdef> </glossentry> <glossentry> - <glossterm>privatekey</glossterm> + <glossterm>privatekey_pass</glossterm> <glossdef> - <para>File name of private key to be used for singing.</para> + <para>Optional field with the passphrase for the private + key.</para> </glossdef> </glossentry> + </glosslist> + </section> + + <section> + <title>Fields for signing and validating messages</title> + <para>simpleSAMLphp only signs authentication responses by default. + Signing of authentication requests, logout requests and logout + responses can be enabled by setting the + <literal>redirect.sign</literal> option. Validation of received + messages can be enabled by the <literal>redirect.validate</literal> + option. Note that if you want to sign messages, you will need a + keypair/certificate at the SP.</para> + + <para>These options set the default for this SP, but options for each + IdP can be set in <literal>saml20-idp-remote</literal>.</para> + + <glosslist> <glossentry> - <glossterm>certificate</glossterm> + <glossterm>redirect.sign</glossterm> <glossdef> - <para>File name of certificate corresponding to the private key. - Use of certificates is not yet implemented in simpleSAMLphp, but - is reserved for future use; the certificate will be used to - generate SAML 2.0 Metadata for export to the IdP.</para> + <para>Boolean, default <literal>false</literal>. To turn on + signing of authentication requests, logout requests and logout + responses, set this flag to true.</para> + + <para>This option can be overridden by the + <literal>redirect.sign</literal> option in + <literal>saml20-idp-remote</literal>.</para> </glossdef> </glossentry> <glossentry> - <glossterm>privatekey_pass</glossterm> + <glossterm>redirect.validate</glossterm> <glossdef> - <para>Optional field with the passphrase for the private - key.</para> + <para>Boolean, default <literal>false</literal>. To turn on + validation of received logout requests and logout responses, set + this flag to true.</para> + + <para>This option can be overridden by the + <literal>redirect.validate</literal> option in + <literal>saml20-idp-remote</literal>.</para> </glossdef> </glossentry> </glosslist> <example> - <title>Example of configured signed requests</title> + <title>Example of configured signed messages</title> - <programlisting> 'request.signing' => true, + <programlisting> 'redirect.sign' => true, 'privatekey' => 'server.pem',</programlisting> </example> </section> @@ -397,13 +420,13 @@ 'base64attributes' => true, /* - * When request.signing is true the certificate of the IdP will be used + * When redirect.validate is true the certificate of the IdP will be used * to verify all messages received with the HTTPRedirect binding. * * The certificate from the IdP must be installed in the cert directory * before verification can be done. */ - 'request.signing' => false, + 'redirect.validate' => false, 'certificate' => "idp.example.org.crt", /* @@ -571,6 +594,9 @@ <filename>certs</filename> directory. Used for decrypting assertions and as an alternative to certFingerprint for validating signatures.</para> + + <para>This option is also required if validating signed logout + requests/responses from this IdP.</para> </glossdef> </glossentry> @@ -624,55 +650,52 @@ </section> <section> - <title>Fields for requiring signed LogoutRequests</title> + <title>Fields for signing and validating messages</title> - <para>simpleSAMLphp supports signing the HTTP-REDIRECT authentication - request, but by default it will not sign it. Note that if you want to - sign the authentication requests, you must supply a - keypair/certificate to the SP.</para> + <para>simpleSAMLphp only signs authentication responses by default. + Signing of authentication requests, logout requests and logout + responses can be enabled by setting the + <literal>redirect.sign</literal> option. Validation of received + messages can be enabled by the <literal>redirect.validate</literal> + option. Note that if you want to sign messages, you will need a + keypair/certificate at the SP.</para> + + <para>These options overrides the options set in + <literal>saml20-sp-hosted</literal>.</para> <glosslist> <glossentry> - <glossterm>request.signing</glossterm> + <glossterm>redirect.sign</glossterm> <glossdef> <para>Boolean, default <literal>false</literal>. To turn on - signing authentication requests, set this flag to true.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>privatekey</glossterm> + signing of authentication requests, logout requests and logout + responses, set this flag to true.</para> - <glossdef> - <para>File name of the private key to be used for - singing.</para> + <para>This option overrides the <literal>redirect.sign</literal> + option in <literal>saml20-sp-hosted</literal>.</para> </glossdef> </glossentry> <glossentry> - <glossterm>certificate</glossterm> + <glossterm>redirect.validate</glossterm> <glossdef> - <para>File name of certificate corresponding to the private - key.</para> - </glossdef> - </glossentry> - - <glossentry> - <glossterm>privatekey_pass</glossterm> + <para>Boolean, default <literal>false</literal>. To turn on + validation of received logout requests and logout responses, set + this flag to true.</para> - <glossdef> - <para>Optional field with the passphrase for the private - key.</para> + <para>This option overrides the + <literal>redirect.validate</literal> option in + <literal>saml20-sp-hosted</literal>.</para> </glossdef> </glossentry> </glosslist> <example> - <title>Example of configured signed LogoutRequests</title> + <title>Example of configuration for validating messages</title> - <programlisting>'request.signing' => true, + <programlisting>'redirect.validate' => true, 'certificate' => 'server.crt'</programlisting> </example> </section> diff --git a/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php b/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php index e2f096093..526ee3022 100644 --- a/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php +++ b/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php @@ -18,18 +18,38 @@ class SimpleSAML_Bindings_SAML20_HTTPRedirect { } - public function signQuery($query, $md) { + /** + * Sign a HTTP-Redirect query string. + * + * @param string $query The query string. + * @param array $md The metadata of the sender. + * @param array $targetmd The metadata of the recipient. + * @return string The signed query. + */ + public function signQuery($query, $md, $targetmd) { + assert('is_string($query)'); + assert('is_array($md)'); + assert('is_array($targetmd)'); /* Check if signing of HTTP-Redirect messages is enabled. */ - - if (!array_key_exists('request.signing', $md) || !$md['request.signing']){ - return $query; + if (array_key_exists('redirect.sign', $targetmd)) { + $sign = (bool)$targetmd['redirect.sign']; + } elseif (array_key_exists('redirect.sign', $md)) { + $sign = (bool)$md['redirect.sign']; + } elseif (array_key_exists('request.signing', $md)) { + SimpleSAML_Logger::warning('Found deprecated \'request.signing\' metadata' . + ' option for entity ' . var_export($md['entityid'], TRUE) . '.' . + ' Please replace with \'redirect.sign\' instead.'); + $sign = (bool)$md['request.signing']; + } else { + $sign = FALSE; } - if (!array_key_exists('privatekey', $md)) { - throw new Exception('If you set request.signing to be true in the metadata, you also have to add the privatekey parameter.'); + if (!$sign) { + /* Signing of queries disabled. */ + return $query; } - + /* Load the private key. */ $privatekey = SimpleSAML_Utilities::loadPrivateKey($md, TRUE); @@ -63,18 +83,51 @@ class SimpleSAML_Bindings_SAML20_HTTPRedirect { return $query; } - - public function validateQuery($issuer,$mode = 'SP',$request = 'SAMLRequest') { - $metadataset = 'saml20-idp-remote'; + + /** + * Validate query string. + * + * This function validates the signature on the query string of the current request. + * + * @param string $issuer The issuer of this query string. + * @param string $mode Whether we are running as an SP or an IdP. + * @param string $request The query parameter which contains the request/response we should validate. + * @return bool FALSE if the query string wasn't validated, TRUE if it validate. An exception will be + * thrown if the validation fails. + */ + public function validateQuery($issuer, $mode = 'SP', $request = 'SAMLRequest') { + assert('is_string($issuer)'); + assert('$mode === "SP" || $mode === "IdP"'); + assert('$request === "SAMLRequest" || $request === "SAMLResponse"'); + if ($mode == 'IdP') { - $metadataset = 'saml20-sp-remote'; + $issuerSet = 'saml20-sp-remote'; + $recipientSet = 'saml20-idp-hosted'; + } else { + $issuerSet = 'saml20-idp-remote'; + $recipientSet = 'saml20-sp-hosted'; } - SimpleSAML_Logger::debug('Library - HTTPRedirect validateQuery(): Looking up metadata issuer:' . $issuer . ' in set '. $metadataset); - $md = $this->metadata->getMetaData($issuer, $metadataset); + SimpleSAML_Logger::debug('Library - HTTPRedirect validateQuery(): Looking up metadata issuer:' . $issuer . ' in set '. $issuerSet); + $md = $this->metadata->getMetaData($issuer, $issuerSet); + + $recipientMetadata = $this->metadata->getMetaDataCurrent($recipientSet); // check whether to validate or not - if (!array_key_exists('request.signing', $md) || !$md['request.signing']){ + if (array_key_exists('redirect.validate', $md)) { + $validate = (bool)$md['redirect.validate']; + } elseif (array_key_exists('redirect.validate', $recipientMetadata)) { + $validate = (bool)$recipientMetadata['redirect.validate']; + } elseif (array_key_exists('request.signing', $md)) { + SimpleSAML_Logger::warning('Found deprecated \'request.signing\' metadata' . + ' option for entity ' . var_export($issuer, TRUE) . '.' . + ' Please replace with \'redirect.validate\' instead.'); + $validate = (bool)$md['request.signing']; + } else { + $validate = FALSE; + } + + if (!$validate) { return false; } @@ -87,7 +140,7 @@ class SimpleSAML_Bindings_SAML20_HTTPRedirect { // building query string $query = $request.'='.urlencode($_GET[$request]); - if($_GET['RelayState']) { + if(array_key_exists('RelayState', $_GET)) { $relaystate = $_GET['RelayState']; $query .= "&RelayState=" . urlencode($relaystate); } @@ -161,7 +214,7 @@ class SimpleSAML_Bindings_SAML20_HTTPRedirect { $metadataset = 'saml20-idp-hosted'; } $localmd = $this->metadata->getMetaData($localentityid, $metadataset); - $request = $this->signQuery($request, $localmd); + $request = $this->signQuery($request, $localmd, $md); $redirectURL = $idpTargetUrl . "?" . $request; return $redirectURL; diff --git a/metadata-templates/saml20-idp-hosted.php b/metadata-templates/saml20-idp-hosted.php index 73ec7b00f..869e323e9 100644 --- a/metadata-templates/saml20-idp-hosted.php +++ b/metadata-templates/saml20-idp-hosted.php @@ -14,15 +14,6 @@ * Optional Parameters: * - 'userid.attribute' * - * - * Request signing (optional paramters) - * When request.signing is true the privatekey and certificate of the SP - * will be used to sign/verify all messages received/sent with the HTTPRedirect binding. - * The certificate and privatekey from above will be used for signing and - * verification purposes. - * - * - request.signing - * */ diff --git a/metadata-templates/saml20-sp-hosted.php b/metadata-templates/saml20-sp-hosted.php index 17ac7a7a2..ace8affa4 100644 --- a/metadata-templates/saml20-sp-hosted.php +++ b/metadata-templates/saml20-sp-hosted.php @@ -10,16 +10,6 @@ * Optional fields: * - NameIDFormat * - ForceAuthn - * - * Authentication request signing - * When request.signing is true the privatekey and certificate of the SP - * will be used to sign/verify all messages received/sent with the HTTPRedirect binding. - * Certificate and privatekey must be placed in the cert directory. - * All these attributes are optional: - * - * - 'request.signing' => true, - * - 'privatekey' => 'server.pem', - * - 'certificate' => 'server.crt', */ $metadata = array( diff --git a/metadata-templates/saml20-sp-remote.php b/metadata-templates/saml20-sp-remote.php index c9f6b88ae..76f0039a9 100644 --- a/metadata-templates/saml20-sp-remote.php +++ b/metadata-templates/saml20-sp-remote.php @@ -22,12 +22,12 @@ * - 'userid.attribute' * * Request signing - * When request.signing is true the certificate of the sp + * When redirect.sign is true the certificate of the sp * will be used to verify all messages received with the HTTPRedirect binding. * The certificate from the SP must be installed in the cert directory * before verification can be done. * - * 'request.signing' => false, + * 'redirect.sign' => false, * 'certificate' => "saml2sp.example.org.crt" * */ diff --git a/www/admin/metadata.php b/www/admin/metadata.php index bcc445b41..faf8aadd0 100644 --- a/www/admin/metadata.php +++ b/www/admin/metadata.php @@ -29,7 +29,7 @@ try { foreach ($metalist AS $entityid => $mentry) { $results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry, array('entityid', 'host'), - array('request.signing','certificate','privatekey', 'privatekey_pass', 'NameIDFormat', 'ForceAuthn', 'AuthnContextClassRef', 'SPNameQualifier', 'attributemap', 'attributealter', 'attributes', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.privatekey_pass', 'metadata.sign.certificate', 'idpdisco.url') + array('redirect.sign','redirect.validate','certificate','privatekey', 'privatekey_pass', 'NameIDFormat', 'ForceAuthn', 'AuthnContextClassRef', 'SPNameQualifier', 'attributemap', 'attributealter', 'attributes', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.privatekey_pass', 'metadata.sign.certificate', 'idpdisco.url') ); } $et->data['metadata.saml20-sp-hosted'] = $results; @@ -39,7 +39,7 @@ try { foreach ($metalist AS $entityid => $mentry) { $results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry, array('entityid', 'SingleSignOnService', 'SingleLogoutService', 'certFingerprint'), - array('name', 'description', 'base64attributes', 'certificate', 'hint.cidr', 'saml2.relaxvalidation', 'SingleLogoutServiceResponse', 'request.signing', 'attributemap', 'attributealter', 'sharedkey', 'assertion.encryption', 'icon') + array('name', 'description', 'base64attributes', 'certificate', 'hint.cidr', 'saml2.relaxvalidation', 'SingleLogoutServiceResponse', 'redirect.sign', 'redirect.validate', 'attributemap', 'attributealter', 'sharedkey', 'assertion.encryption', 'icon') ); $index = array_search('certFingerprint', $results[$entityid]['required.notfound']); if ($index !== FALSE) { @@ -58,7 +58,7 @@ try { foreach ($metalist AS $entityid => $mentry) { $results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry, array('entityid', 'host', 'privatekey', 'certificate', 'auth'), - array('requireconsent','request.signing', 'privatekey_pass', 'authority', 'attributemap', 'attributealter', 'userid.attribute', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.privatekey_pass', 'metadata.sign.certificate', 'AttributeNameFormat', 'name') + array('requireconsent', 'redirect.sign', 'redirect.validate', 'privatekey_pass', 'authority', 'attributemap', 'attributealter', 'userid.attribute', 'metadata.sign.enable', 'metadata.sign.privatekey', 'metadata.sign.privatekey_pass', 'metadata.sign.certificate', 'AttributeNameFormat', 'name') ); } $et->data['metadata.saml20-idp-hosted'] = $results; @@ -68,7 +68,7 @@ try { foreach ($metalist AS $entityid => $mentry) { $results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry, array('entityid', 'AssertionConsumerService'), - array('SingleLogoutService', 'NameIDFormat', 'SPNameQualifier', 'base64attributes', 'simplesaml.nameidattribute', 'attributemap', 'attributealter', 'simplesaml.attributes', 'attributes', 'name', 'description','request.signing','certificate', 'ForceAuthn', 'sharedkey', 'assertion.encryption', 'userid.attribute', 'signresponse', 'AttributeNameFormat') + array('SingleLogoutService', 'NameIDFormat', 'SPNameQualifier', 'base64attributes', 'simplesaml.nameidattribute', 'attributemap', 'attributealter', 'simplesaml.attributes', 'attributes', 'name', 'description', 'redirect.sign', 'redirect.validate', 'certificate', 'ForceAuthn', 'sharedkey', 'assertion.encryption', 'userid.attribute', 'signresponse', 'AttributeNameFormat') ); } $et->data['metadata.saml20-sp-remote'] = $results; -- GitLab