Skip to content
Snippets Groups Projects
Commit 28fd8c14 authored by Olav Morken's avatar Olav Morken
Browse files

Applied patch from Hans Zandbelt (with some modifications). This patch enables...

Applied patch from Hans Zandbelt (with some modifications). This patch enables signing of authentication requests if there is an privatekey-element in the SP's metadata.


git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@172 44740490-163a-0410-bde0-09ae8108e29a
parent 0c9bab36
No related branches found
No related tags found
No related merge requests found
......@@ -283,6 +283,15 @@ $config = array (
*/
'auth.auto.delay_login' => 0,
/*
* This option enables signing of all messages sent with the
* HTTP-Redirect binding. The default value is false. To enable, set
* this option to true, and add a 'privatekey' element to the entity
* (IdP or SP) which is sending the message.
*/
'binding.httpredirect.sign' => true,
);
......
......@@ -29,8 +29,76 @@ class SimpleSAML_Bindings_SAML20_HTTPRedirect {
$this->configuration = $configuration;
$this->metadata = $metadatastore;
}
public function getRedirectURL($request, $remoteentityid, $relayState = null, $endpoint = 'SingleSignOnService', $direction = 'SAMLRequest', $mode = 'SP') {
public function signQuery($query, $md) {
/* Check if signing of HTTP-Redirect messages is enabled. */
if($this->configuration->getValue('binding.httpredirect.sign', false) !== true) {
return $query;
}
/* Don't attempt to sign the query if no private key is set in the metadata. */
if(!array_key_exists('privatekey', $md) || $md['privatekey'] === NULL) {
return $query;
}
/* Load the private key. */
$privatekey = $this->configuration->getBaseDir() . 'cert/' . $md['privatekey'];
if (!file_exists($privatekey)) {
throw new Exception('Could not find private key file [' . $privatekey . '] which is needed to sign the request.');
}
$keydata = file_get_contents($privatekey);
if($keydata === FALSE) {
throw new Exception('Unable to load private key file: ' . $privatekey);
}
$keyid = openssl_pkey_get_private($keydata);
if($keyid === FALSE) {
throw new Exception('OpenSSL was unable to parse the private key from the following file: ' . $privatekey);
}
/* Make sure that the loaded key is a RSA key. */
$keydetails = openssl_pkey_get_details($keyid);
if($keydetails === FALSE) {
throw new Exception('Unable to get key details of already loaded key.');
}
if($keydetails['type'] !== OPENSSL_KEYTYPE_RSA) {
throw new Exception('Private key used to sign the query string isn\'t a RSA key. Key was loaded from the following file: ' . $privatekey);
}
/* Sign the query string. According to the specification, the string which should be
* signed is the concatenation of the following query parameters (in order):
* - SAMLRequest/SAMLResponse
* - RelayState (if present)
* - SigAlg
*
* We assume that the query string now contains only the two first parameters.
*/
/* Append the signature algorithm. We always use RSA-SHA1. */
$algURI = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
$query = $query . "&" . "SigAlg=" . urlencode($algURI);
/* Sign the query string. The default hash algorithm of openssl_sign is (fortunately) SHA1. */
if(!openssl_sign($query, $signature, $keyid)) {
throw new Exception('OpenSSL was unable to sign the query string.');
}
/* Free the key we used. */
openssl_pkey_free($keyid);
/* Return the signed query string. */
$query = $query . "&" . "Signature=" . urlencode(base64_encode($signature));
return $query;
}
public function getRedirectURL($request, $localentityid, $remoteentityid, $relayState = null, $endpoint = 'SingleSignOnService', $direction = 'SAMLRequest', $mode = 'SP') {
if (!in_array($mode, array('SP', 'IdP'))) {
throw new Exception('mode parameter of sendMessage() must be either SP or IdP');
......@@ -51,21 +119,28 @@ class SimpleSAML_Bindings_SAML20_HTTPRedirect {
if (!isset($idpTargetUrl) or $idpTargetUrl == '') {
throw new Exception('Could not find endpoint [' .$endpoint . '] in metadata for [' . $remoteentityid . '] (looking in ' . $metadataset . ')');
}
$encodedRequest = urlencode( base64_encode( gzdeflate( $request ) ));
$redirectURL = $idpTargetUrl . "?" . $direction . "=" . $encodedRequest;
$request = urlencode( base64_encode( gzdeflate( $request ) ));
$request = $direction . "=" . $request;
if (isset($relayState)) {
$redirectURL .= "&RelayState=" . urlencode($relayState);
$request .= "&RelayState=" . urlencode($relayState);
}
$metadataset = 'saml20-sp-hosted';
if ($mode == 'IdP') {
$metadataset = 'saml20-idp-hosted';
}
$localmd = $this->metadata->getMetaData($localentityid, $metadataset);
$request = $this->signQuery($request, $localmd);
$redirectURL = $idpTargetUrl . "?" . $request;
return $redirectURL;
}
public function sendMessage($request, $remoteentityid, $relayState = null, $endpoint = 'SingleSignOnService', $direction = 'SAMLRequest', $mode = 'SP') {
public function sendMessage($request, $localentityid, $remoteentityid, $relayState = null, $endpoint = 'SingleSignOnService', $direction = 'SAMLRequest', $mode = 'SP') {
$redirectURL = $this->getRedirectURL($request, $remoteentityid, $relayState, $endpoint, $direction, $mode);
$redirectURL = $this->getRedirectURL($request, $localentityid, $remoteentityid, $relayState, $endpoint, $direction, $mode);
if ($this->configuration->getValue('debug')) {
......
......@@ -132,7 +132,7 @@ class SimpleSAML_XML_SAML20_AuthnRequest {
}
public function generate($spentityid) {
public function generate($spentityid, $destination) {
$md = $this->metadata->getMetaData($spentityid);
$id = self::generateID();
......@@ -154,6 +154,7 @@ class SimpleSAML_XML_SAML20_AuthnRequest {
"IssueInstant=\"" . $issueInstant . "\" " .
"ForceAuthn=\"false\" " .
"IsPassive=\"false\" " .
"Destination=\"" . $destination . "\" " .
"ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" " .
"AssertionConsumerServiceURL=\"" . $assertionConsumerServiceURL . "\">\n" .
"<saml:Issuer " .
......
......@@ -141,6 +141,7 @@ class SimpleSAML_XML_SAML20_LogoutRequest {
"xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" " .
"ID=\"" . $id . "\" " .
"Version=\"2.0\" " .
"Destination=\"" . $destination . "\" " .
"IssueInstant=\"" . $issueInstant . "\"> " .
"<saml:Issuer " .
"xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">" .
......
......@@ -15,7 +15,22 @@ $metadata = array(
'host' => 'sp.example.org',
'spNameQualifier' => 'sp.example.org',
'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
'ForceAuthn' => 'false'
'ForceAuthn' => 'false',
/*
* This option configures the name of a file which contains a
* RSA key for this service provider. The file must be located
* in the cert-directory of the SimpleSAMLPHP installation.
*
* This key will be used to sign all outgoing authentication-
* requests, logoutrequests and logoutresponses (everything
* that uses the HTTP-Redirect binding).
*
* To enable signing, set this option to a private key file
* and enable the 'binding.httpredirect.sign' global option.
*/
'privatekey' => 'server.pem',
)
);
......
......@@ -52,7 +52,7 @@ if (isset($_GET['SAMLRequest'])) {
/* Send the response using the HTTP-Redirect binding. */
$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config,
$metadata);
$binding->sendMessage($responseText, $spentityid, $relayState,
$binding->sendMessage($responseText, $idpentityid, $spentityid, $relayState,
'SingleLogoutService', 'SAMLResponse', 'IdP');
exit;
}
......@@ -114,7 +114,7 @@ if ($spentityid) {
}
//$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP'
$httpredirect->sendMessage($req, $spentityid, $relayState, 'SingleLogoutService', 'SAMLRequest', 'IdP');
$httpredirect->sendMessage($req, $idpentityid, $spentityid, $relayState, 'SingleLogoutService', 'SAMLRequest', 'IdP');
exit();
......@@ -165,7 +165,7 @@ try {
}
//$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP'
$httpredirect->sendMessage($logoutResponseXML, $logoutrequest->getIssuer(), $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP');
$httpredirect->sendMessage($logoutResponseXML, $idpentityid, $logoutrequest->getIssuer(), $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP');
} catch(Exception $exception) {
......
......@@ -77,7 +77,7 @@ if (isset($_GET['SAMLRequest'])) {
'SP me (' . $responder . ') is sending logout response to IdP (' . $requester . ')');
// Send the Logout response using HTTP POST binding.
$httpredirect->sendMessage($logoutResponseXML, $requester, $logoutrequest->getRelayState(), 'SingleLogoutServiceResponse', 'SAMLResponse');
$httpredirect->sendMessage($logoutResponseXML, $responser, $requester, $logoutrequest->getRelayState(), 'SingleLogoutServiceResponse', 'SAMLResponse');
} elseif(isset($_GET['SAMLResponse'])) {
......
......@@ -46,7 +46,7 @@ if (isset($session) ) {
'SP (' . $spentityid . ') is sending logout request to IdP (' . $idpentityid . ')');
//$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP'
$httpredirect->sendMessage($req, $idpentityid, $relayState, 'SingleLogoutService', 'SAMLRequest', 'SP');
$httpredirect->sendMessage($req, $spentityid, $idpentityid, $relayState, 'SingleLogoutService', 'SAMLRequest', 'SP');
} catch(Exception $exception) {
......
......@@ -64,7 +64,9 @@ if (!isset($session) || !$session->isValid() ) {
try {
$sr = new SimpleSAML_XML_SAML20_AuthnRequest($config, $metadata);
$req = $sr->generate($spentityid);
$md = $metadata->getMetaData($idpentityid, 'saml20-idp-remote');
$req = $sr->generate($spentityid, $md['SingleSignOnService']);
$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
......@@ -76,7 +78,7 @@ if (!isset($session) || !$session->isValid() ) {
$logger->log(LOG_NOTICE, $session->getTrackID(), 'SAML2.0', 'SP.initSSO', 'AuthnRequest', $idpentityid,
'SP (' . $spentityid . ') is sending authenticatino request to IdP (' . $idpentityid . ')');
$httpredirect->sendMessage($req, $idpentityid, $relayState);
$httpredirect->sendMessage($req, $spentityid, $idpentityid, $relayState);
} catch(Exception $exception) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment