Skip to content

Commit d63e3ec

Browse files
author
Hans Zandbelt
committed
basic ADFS PRP IDP support
git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@1513 44740490-163a-0410-bde0-09ae8108e29a
1 parent 0829dd5 commit d63e3ec

4 files changed

Lines changed: 289 additions & 0 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
$config = array(
4+
5+
'key' => 'server.pem',
6+
'cert' => 'server.crt',
7+
'auth' => 'auth/login.php',
8+
9+
);
10+
11+
?>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
$config = array(
4+
5+
'urn:federation:localhost:adfs' => array(
6+
7+
'prp' => 'https://localhost/adfs/ls/',
8+
'simplesaml.nameidattribute' => 'uid',
9+
'authproc' => array(
10+
50 => array(
11+
'class' => 'core:AttributeLimit',
12+
'cn', 'mail', 'uid'
13+
),
14+
),
15+
),
16+
17+
);
18+
19+
?>

modules/adfs/default-disable

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This file indicates that the default state of this module
2+
is disabled. To enable, create a file named enable in the
3+
same directory as this file.

modules/adfs/www/idp/prp.php

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
<?php
2+
/**
3+
* ADFS PRP IDP protocol support for simpleSAMLphp.
4+
*
5+
* @author Hans Zandbelt, SURFnet BV. <hans.zandbelt@surfnet.nl>
6+
* @package simpleSAMLphp
7+
* @version $Id$
8+
*/
9+
10+
$config = SimpleSAML_Configuration::getInstance();
11+
$adfsconfig = SimpleSAML_Configuration::getConfig('adfs-idp-hosted.php');
12+
$session = SimpleSAML_Session::getInstance();
13+
14+
SimpleSAML_Logger::info('ADFS - IdP.SSOService: Accessing ADFS IdP endpoint SSOService');
15+
16+
try {
17+
if (array_key_exists('entityId', $config)) {
18+
$idpentityid = $config['entityId'];
19+
} else {
20+
$idpentityid = 'urn:federation:' . SimpleSAML_Utilities::getSelfHost() . ':idp';
21+
}
22+
} catch (Exception $exception) {
23+
SimpleSAML_Utilities::fatalError($session->getTrackID(), 'METADATA', $exception);
24+
}
25+
26+
SimpleSAML_Logger::info('ADFS - IdP.SSOService: Accessing ADFS IdP endpoint SSOService');
27+
28+
function ADFS_GenerateResponse($issuer, $target, $nameid, $attributes) {
29+
# $nameid = 'hans@surfnet.nl';
30+
$issueInstant = SimpleSAML_Utilities::generateTimestamp();
31+
$notBefore = SimpleSAML_Utilities::generateTimestamp(time() - 30);
32+
$assertionExpire = SimpleSAML_Utilities::generateTimestamp(time() + 60 * 5);
33+
$assertionID = SimpleSAML_Utilities::generateID();
34+
$nameidFormat = 'http://schemas.xmlsoap.org/claims/UPN';
35+
$result =
36+
'<wst:RequestSecurityTokenResponse xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
37+
<wst:RequestedSecurityToken>
38+
<saml:Assertion Issuer="' . $issuer . '" IssueInstant="' . $issueInstant . '" AssertionID="' . $assertionID . '" MinorVersion="1" MajorVersion="1" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">
39+
<saml:Conditions NotOnOrAfter="' . $assertionExpire . '" NotBefore="' . $notBefore . '">
40+
<saml:AudienceRestrictionCondition>
41+
<saml:Audience>' . $target .'</saml:Audience>
42+
</saml:AudienceRestrictionCondition>
43+
</saml:Conditions>
44+
<saml:AuthenticationStatement AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:unspecified" AuthenticationInstant="' . $issueInstant . '">
45+
<saml:Subject>
46+
<saml:NameIdentifier Format="' . $nameidFormat . '">' . $nameid . '</saml:NameIdentifier>
47+
</saml:Subject>
48+
</saml:AuthenticationStatement>
49+
<saml:AttributeStatement>
50+
<saml:Subject>
51+
<saml:NameIdentifier Format="' . $nameidFormat . '">' . $nameid . '</saml:NameIdentifier>
52+
</saml:Subject>';
53+
foreach ($attributes as $name => $values) {
54+
$result .= '<saml:Attribute AttributeNamespace="http://schemas.xmlsoap.org/claims" AttributeName="' . $name .'">';
55+
foreach ($values as $value) {
56+
$result .= '<saml:AttributeValue>' . $value . '</saml:AttributeValue>';
57+
}
58+
$result .= '</saml:Attribute>';
59+
}
60+
$result .= '
61+
</saml:AttributeStatement>
62+
</saml:Assertion>
63+
</wst:RequestedSecurityToken>
64+
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"><wsa:EndpointReference xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
65+
<wsa:Address>' . $target . '</wsa:Address>
66+
</wsa:EndpointReference></wsp:AppliesTo>
67+
</wst:RequestSecurityTokenResponse>';
68+
return $result;
69+
}
70+
71+
function ADFS_SignResponse($response, $key, $cert) {
72+
$objXMLSecDSig = new XMLSecurityDSig();
73+
$objXMLSecDSig->idKeys = array('AssertionID');
74+
$objXMLSecDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
75+
$responsedom = new DOMDocument();
76+
$responsedom->loadXML(str_replace ("\r", "", $response));
77+
$firstassertionroot = $responsedom->getElementsByTagName('Assertion')->item(0);
78+
$objXMLSecDSig->addReferenceList(array($firstassertionroot), XMLSecurityDSig::SHA1,
79+
array('http://www.w3.org/2000/09/xmldsig#enveloped-signature', XMLSecurityDSig::EXC_C14N),
80+
array('id_name' => 'AssertionID'));
81+
$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'private'));
82+
$objKey->loadKey($key, TRUE);
83+
$objXMLSecDSig->sign($objKey);
84+
if ($cert) {
85+
$public_cert = file_get_contents($cert);
86+
$objXMLSecDSig->add509Cert($public_cert, TRUE);
87+
}
88+
$newSig = $responsedom->importNode($objXMLSecDSig->sigNode, TRUE);
89+
$firstassertionroot->appendChild($newSig);
90+
return $responsedom->saveXML();
91+
}
92+
93+
function ADFS_PostResponse($url, $wresult, $wctx) {
94+
print '
95+
<body onload="document.forms[0].submit()"><form method="post" action="' . $url . '">
96+
<input type="hidden" name="wa" value="wsignin1.0">
97+
<input type="hidden" name="wresult" value="' . htmlspecialchars($wresult) . '">
98+
<input type="hidden" name="wctx" value="' . htmlspecialchars($wctx) . '">
99+
<noscript><input type="submit" value="Continue"></noscript>
100+
</form></body>';
101+
exit;
102+
}
103+
104+
if (isset($_GET['wa'])) {
105+
106+
if ($_GET['wa'] == 'wsignin1.0') {
107+
try {
108+
// accomodate for disfunctional $_GET "windows" slash decoding in PHP
109+
$wctx = $_GET['wctx'];
110+
foreach (split('&', $_SERVER['REQUEST_URI']) as $e) {
111+
$a = split('=', $e);
112+
if ($a[0] == 'wctx') $wctx = urldecode($a[1]);
113+
}
114+
$requestid = $wctx;
115+
$issuer = $_GET['wtrealm'];
116+
$requestcache = array(
117+
'RequestID' => $requestid,
118+
'Issuer' => $issuer,
119+
'RelayState' => $requestid
120+
);
121+
122+
$spentityid = $requestcache['Issuer'];
123+
124+
SimpleSAML_Logger::info('ADFS - IdP.SSOService: Incoming Authentication request: '.$issuer.' id '.$requestid);
125+
126+
} catch(Exception $exception) {
127+
SimpleSAML_Utilities::fatalError($session->getTrackID(), 'PROCESSAUTHNREQUEST', $exception);
128+
}
129+
}
130+
131+
} elseif(isset($_GET['RequestID'])) {
132+
133+
try {
134+
135+
SimpleSAML_Logger::info('ADFS - IdP.SSOService: Got incoming authentication ID');
136+
137+
$authId = $_GET['RequestID'];
138+
$requestcache = $session->getAuthnRequest('adfs', $authId);
139+
if (!$requestcache) {
140+
throw new Exception('Could not retrieve cached RequestID = ' . $authId);
141+
}
142+
143+
} catch(Exception $exception) {
144+
SimpleSAML_Utilities::fatalError($session->getTrackID(), 'CACHEAUTHNREQUEST', $exception);
145+
}
146+
147+
} elseif(isset($_REQUEST[SimpleSAML_Auth_ProcessingChain::AUTHPARAM])) {
148+
149+
$authProcId = $_REQUEST[SimpleSAML_Auth_ProcessingChain::AUTHPARAM];
150+
$authProcState = SimpleSAML_Auth_ProcessingChain::fetchProcessedState($authProcId);
151+
$requestcache = $authProcState['core:adfs-idp:requestcache'];
152+
153+
} else {
154+
SimpleSAML_Utilities::fatalError($session->getTrackID(), 'SSOSERVICEPARAMS');
155+
}
156+
157+
if(SimpleSAML_Auth_Source::getById($adfsconfig->getValue('auth')) !== NULL) {
158+
$authSource = TRUE;
159+
$authority = $adfsconfig->getValue('auth');
160+
} else {
161+
$authSource = FALSE;
162+
$authority = $adfsconfig->getValue('authority');
163+
}
164+
165+
if (!$session->isValid($authority) ) {
166+
167+
SimpleSAML_Logger::info('ADFS - IdP.SSOService: Will go to authentication module ' . $adfsconfig->getValue('auth'));
168+
169+
$authId = SimpleSAML_Utilities::generateID();
170+
$session->setAuthnRequest('adfs', $authId, $requestcache);
171+
172+
$redirectTo = SimpleSAML_Utilities::selfURLNoQuery() . '?RequestID=' . urlencode($authId);
173+
174+
if($authSource) {
175+
176+
SimpleSAML_Auth_Default::initLogin($adfsconfig->getValue('auth'), $redirectTo, NULL, NULL);
177+
} else {
178+
$authurl = '/' . $config->getBaseURL() . $adfsconfig->getValue('auth');
179+
180+
SimpleSAML_Utilities::redirect($authurl, array(
181+
'RelayState' => $redirectTo,
182+
'AuthId' => $authId,
183+
'protocol' => 'adfs',
184+
));
185+
}
186+
187+
} else {
188+
189+
try {
190+
191+
$spentityid = $requestcache['Issuer'];
192+
$spmetadata = SimpleSAML_Configuration::getConfig('adfs-sp-remote.php');
193+
$spmetadata = SimpleSAML_Configuration::loadFromArray($spmetadata->getValue($spentityid));
194+
195+
$sp_name = $spmetadata->getValue('name', $spentityid);
196+
197+
SimpleSAML_Logger::info('ADFS - IdP.SSOService: Sending back AuthnResponse to ' . $spentityid);
198+
199+
$attributes = $session->getAttributes();
200+
201+
if (!isset($authProcState)) {
202+
203+
$idpap = $adfsconfig->getValue('authproc');
204+
if ($idpap) $idpap = array('authproc' => $idpap); else $idpap = array();
205+
206+
$spap = $spmetadata->getValue('authproc');
207+
if ($spap) $spap = array('authproc' => $spap); else $spap = array();
208+
209+
$pc = new SimpleSAML_Auth_ProcessingChain($idpap, $spap, 'idp');
210+
211+
$authProcState = array(
212+
'core:adfs-idp:requestcache' => $requestcache,
213+
'ReturnURL' => SimpleSAML_Utilities::selfURLNoQuery(),
214+
'Attributes' => $attributes,
215+
'Destination' => $spap,
216+
'Source' => $idpap,
217+
'isPassive' => false,
218+
);
219+
220+
$previousSSOTime = $session->getData('adfs-idp-ssotime', $spentityid);
221+
if ($previousSSOTime !== NULL) {
222+
$authProcState['PreviousSSOTimestamp'] = $previousSSOTime;
223+
}
224+
225+
try {
226+
$pc->processState($authProcState);
227+
} catch (SimpleSAML_Error_NoPassive $e) {
228+
SimpleSAML_Utilities::fatalError($session->getTrackID(), 'GENERATEAUTHNRESPONSE', $exception);
229+
}
230+
231+
$requestcache['AuthProcState'] = $authProcState;
232+
}
233+
234+
$attributes = $authProcState['Attributes'];
235+
236+
$session->setData('adfs-idp-ssotime', $spentityid, time(),
237+
SimpleSAML_Session::DATA_TIMEOUT_LOGOUT);
238+
239+
$requestID = NULL; $relayState = NULL;
240+
if (array_key_exists('RequestID', $requestcache)) $requestID = $requestcache['RequestID'];
241+
if (array_key_exists('RelayState', $requestcache)) $relayState = $requestcache['RelayState'];
242+
243+
$nameid = $session->getNameID();
244+
245+
$response = ADFS_GenerateResponse($idpentityid, $spentityid, $nameid['value'], $attributes);
246+
$wresult = ADFS_SignResponse($response, $config->getPathValue('certdir') . $adfsconfig->getValue('key'), $config->getPathValue('certdir') . $adfsconfig->getValue('cert'));
247+
248+
ADFS_PostResponse($spmetadata->getValue('prp'), $wresult, $relayState);
249+
250+
} catch(Exception $exception) {
251+
SimpleSAML_Utilities::fatalError($session->getTrackID(), 'GENERATEAUTHNRESPONSE', $exception);
252+
}
253+
254+
}
255+
256+
?>

0 commit comments

Comments
 (0)