ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
ADFS.php
Go to the documentation of this file.
1 <?php
2 
5 
7 {
8  public static function receiveAuthnRequest(SimpleSAML_IdP $idp)
9  {
10  try {
11  parse_str($_SERVER['QUERY_STRING'], $query);
12 
13  $requestid = $query['wctx'];
14  $issuer = $query['wtrealm'];
15 
17  $spMetadata = $metadata->getMetaDataConfig($issuer, 'adfs-sp-remote');
18 
19  SimpleSAML\Logger::info('ADFS - IdP.prp: Incoming Authentication request: '.$issuer.' id '.$requestid);
20  } catch (Exception $exception) {
21  throw new SimpleSAML_Error_Error('PROCESSAUTHNREQUEST', $exception);
22  }
23 
24  $state = array(
25  'Responder' => array('sspmod_adfs_IdP_ADFS', 'sendResponse'),
26  'SPMetadata' => $spMetadata->toArray(),
27  'ForceAuthn' => false,
28  'isPassive' => false,
29  'adfs:wctx' => $requestid,
30  'adfs:wreply' => false
31  );
32 
33  if (isset($query['wreply']) && !empty($query['wreply'])) {
34  $state['adfs:wreply'] = SimpleSAML\Utils\HTTP::checkURLAllowed($query['wreply']);
35  }
36 
38  }
39 
41  {
43  $notBefore = SimpleSAML\Utils\Time::generateTimestamp(time() - 30);
45  $assertionID = SimpleSAML\Utils\Random::generateID();
46  $nameidFormat = 'http://schemas.xmlsoap.org/claims/UPN';
47  $nameid = htmlspecialchars($nameid);
48 
49  $result = <<<MSG
50 <wst:RequestSecurityTokenResponse xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
51  <wst:RequestedSecurityToken>
52  <saml:Assertion Issuer="$issuer" IssueInstant="$issueInstant" AssertionID="$assertionID" MinorVersion="1" MajorVersion="1" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">
53  <saml:Conditions NotOnOrAfter="$assertionExpire" NotBefore="$notBefore">
54  <saml:AudienceRestrictionCondition>
55  <saml:Audience>$target</saml:Audience>
56  </saml:AudienceRestrictionCondition>
57  </saml:Conditions>
58  <saml:AuthenticationStatement AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:unspecified" AuthenticationInstant="$issueInstant">
59  <saml:Subject>
60  <saml:NameIdentifier Format="$nameidFormat">$nameid</saml:NameIdentifier>
61  </saml:Subject>
62  </saml:AuthenticationStatement>
63  <saml:AttributeStatement>
64  <saml:Subject>
65  <saml:NameIdentifier Format="$nameidFormat">$nameid</saml:NameIdentifier>
66  </saml:Subject>
67 MSG;
68 
69  foreach ($attributes as $name => $values) {
70  if ((!is_array($values)) || (count($values) == 0)) {
71  continue;
72  }
73 
74  list($namespace, $name) = SimpleSAML\Utils\Attributes::getAttributeNamespace($name, 'http://schemas.xmlsoap.org/claims');
75  foreach ($values as $value) {
76  if ((!isset($value)) || ($value === '')) {
77  continue;
78  }
79  $value = htmlspecialchars($value);
80 
81  $result .= <<<MSG
82  <saml:Attribute AttributeNamespace="$namespace" AttributeName="$name">
83  <saml:AttributeValue>$value</saml:AttributeValue>
84  </saml:Attribute>
85 MSG;
86 
87  }
88  }
89 
90  $result .= <<<MSG
91  </saml:AttributeStatement>
92  </saml:Assertion>
93  </wst:RequestedSecurityToken>
94  <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
95  <wsa:EndpointReference xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
96  <wsa:Address>$target</wsa:Address>
97  </wsa:EndpointReference>
98  </wsp:AppliesTo>
99 </wst:RequestSecurityTokenResponse>
100 MSG;
101 
102  return $result;
103  }
104 
105  private static function signResponse($response, $key, $cert, $algo)
106  {
107  $objXMLSecDSig = new XMLSecurityDSig();
108  $objXMLSecDSig->idKeys = array('AssertionID');
109  $objXMLSecDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
110  $responsedom = \SAML2\DOMDocumentFactory::fromString(str_replace("\r", "", $response));
111  $firstassertionroot = $responsedom->getElementsByTagName('Assertion')->item(0);
112  $objXMLSecDSig->addReferenceList(
113  array($firstassertionroot), XMLSecurityDSig::SHA256,
114  array('http://www.w3.org/2000/09/xmldsig#enveloped-signature', XMLSecurityDSig::EXC_C14N),
115  array('id_name' => 'AssertionID')
116  );
117 
118  $objKey = new XMLSecurityKey($algo, array('type' => 'private'));
119  $objKey->loadKey($key, true);
120  $objXMLSecDSig->sign($objKey);
121  if ($cert) {
122  $public_cert = file_get_contents($cert);
123  $objXMLSecDSig->add509Cert($public_cert, true);
124  }
125  $newSig = $responsedom->importNode($objXMLSecDSig->sigNode, true);
126  $firstassertionroot->appendChild($newSig);
127  return $responsedom->saveXML();
128  }
129 
130  private static function postResponse($url, $wresult, $wctx)
131  {
132  $wresult = htmlspecialchars($wresult);
133  $wctx = htmlspecialchars($wctx);
134 
135  $post = <<<MSG
136  <body onload="document.forms[0].submit()">
137  <form method="post" action="$url">
138  <input type="hidden" name="wa" value="wsignin1.0">
139  <input type="hidden" name="wresult" value="$wresult">
140  <input type="hidden" name="wctx" value="$wctx">
141  <noscript>
142  <input type="submit" value="Continue">
143  </noscript>
144  </form>
145  </body>
146 MSG;
147 
148  echo $post;
149  exit;
150  }
151 
152  public static function sendResponse(array $state)
153  {
154  $spMetadata = $state["SPMetadata"];
155  $spEntityId = $spMetadata['entityid'];
156  $spMetadata = SimpleSAML_Configuration::loadFromArray($spMetadata, '$metadata['.var_export($spEntityId, true).']');
157 
158  $attributes = $state['Attributes'];
159 
160  $nameidattribute = $spMetadata->getValue('simplesaml.nameidattribute');
161  if (!empty($nameidattribute)) {
162  if (!array_key_exists($nameidattribute, $attributes)) {
163  throw new Exception('simplesaml.nameidattribute does not exist in resulting attribute set');
164  }
165  $nameid = $attributes[$nameidattribute][0];
166  } else {
168  }
169 
170  $idp = SimpleSAML_IdP::getByState($state);
171  $idpMetadata = $idp->getConfig();
172  $idpEntityId = $idpMetadata->getString('entityid');
173 
174  $idp->addAssociation(array(
175  'id' => 'adfs:'.$spEntityId,
176  'Handler' => 'sspmod_adfs_IdP_ADFS',
177  'adfs:entityID' => $spEntityId,
178  ));
179 
180  $assertionLifetime = $spMetadata->getInteger('assertion.lifetime', null);
181  if ($assertionLifetime === null) {
182  $assertionLifetime = $idpMetadata->getInteger('assertion.lifetime', 300);
183  }
184 
186 
187  $privateKeyFile = \SimpleSAML\Utils\Config::getCertPath($idpMetadata->getString('privatekey'));
188  $certificateFile = \SimpleSAML\Utils\Config::getCertPath($idpMetadata->getString('certificate'));
189 
190  $algo = $spMetadata->getString('signature.algorithm', null);
191  if ($algo === null) {
192  $algo = $idpMetadata->getString('signature.algorithm', XMLSecurityKey::RSA_SHA256);
193  }
194  $wresult = sspmod_adfs_IdP_ADFS::signResponse($response, $privateKeyFile, $certificateFile, $algo);
195 
196  $wctx = $state['adfs:wctx'];
197  $wreply = $state['adfs:wreply'] ? : $spMetadata->getValue('prp');
198  sspmod_adfs_IdP_ADFS::postResponse($wreply, $wresult, $wctx);
199  }
200 
201  public static function sendLogoutResponse(SimpleSAML_IdP $idp, array $state)
202  {
203  // NB:: we don't know from which SP the logout request came from
204  $idpMetadata = $idp->getConfig();
205  \SimpleSAML\Utils\HTTP::redirectTrustedURL($idpMetadata->getValue('redirect-after-logout', \SimpleSAML\Utils\HTTP::getBaseURL()));
206  }
207 
208  public static function receiveLogoutMessage(SimpleSAML_IdP $idp)
209  {
210  // if a redirect is to occur based on wreply, we will redirect to url as
211  // this implies an override to normal sp notification
212  if (isset($_GET['wreply']) && !empty($_GET['wreply'])) {
213  $idp->doLogoutRedirect(\SimpleSAML\Utils\HTTP::checkURLAllowed($_GET['wreply']));
214  assert(false);
215  }
216 
217  $state = array(
218  'Responder' => array('sspmod_adfs_IdP_ADFS', 'sendLogoutResponse'),
219  );
220  $assocId = null;
221  // TODO: verify that this is really no problem for:
222  // a) SSP, because there's no caller SP.
223  // b) ADFS SP because caller will be called back..
225  }
226 
227  // accepts an association array, and returns a URL that can be accessed to terminate the association
228  public static function getLogoutURL(SimpleSAML_IdP $idp, array $association, $relayState)
229  {
231  $spMetadata = $metadata->getMetaDataConfig($association['adfs:entityID'], 'adfs-sp-remote');
232  $returnTo = SimpleSAML\Module::getModuleURL('adfs/idp/prp.php?assocId='.urlencode($association["id"]).'&relayState='.urlencode($relayState));
233  return $spMetadata->getValue('prp').'?wa=wsignoutcleanup1.0&wreply='.urlencode($returnTo);
234  }
235 }
handleLogoutRequest(array &$state, $assocId)
Process a logout request.
Definition: IdP.php:472
if($err=$client->getError()) $namespace
static receiveAuthnRequest(SimpleSAML_IdP $idp)
Definition: ADFS.php:8
static generateID()
Generate a random identifier, ID_LENGTH bytes long.
Definition: Random.php:26
doLogoutRedirect($url)
Log out, then redirect to a URL.
Definition: IdP.php:529
static getAttributeNamespace($name, $defaultns)
Extract an attribute&#39;s namespace, or revert to default.
Definition: Attributes.php:122
handleAuthenticationRequest(array &$state)
Process authentication requests.
Definition: IdP.php:377
static getMetadataHandler()
This function retrieves the current instance of the metadata handler.
if(!isset($_REQUEST['ReturnTo'])) $returnTo
Definition: authpage.php:16
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
static getByState(array &$state)
Retrieve the IdP "owning" the state.
Definition: IdP.php:145
$assertionLifetime
static checkURLAllowed($url, array $trustedSites=null)
Check if a URL is valid and is in our list of allowed URLs.
Definition: HTTP.php:321
$result
$idpEntityId
Definition: prp.php:12
$_GET["client_id"]
static signResponse($response, $key, $cert, $algo)
Definition: ADFS.php:105
$spEntityId
static getLogoutURL(SimpleSAML_IdP $idp, array $association, $relayState)
Definition: ADFS.php:228
static redirectTrustedURL($url, $parameters=array())
This function redirects to the specified URL without performing any security checks.
Definition: HTTP.php:959
static sendLogoutResponse(SimpleSAML_IdP $idp, array $state)
Definition: ADFS.php:201
$spMetadata
$metadata['__DYNAMIC:1__']
$algo
Definition: pwgen.php:34
static generateTimestamp($instant=null)
This function generates a timestamp on the form used by the SAML protocols.
Definition: Time.php:31
static getModuleURL($resource, array $parameters=array())
Get absolute URL to a specified module resource.
Definition: Module.php:220
if(!array_key_exists('stateid', $_REQUEST)) $state
Handle linkback() response from LinkedIn.
Definition: linkback.php:10
Attribute-related utility methods.
static info($string)
Definition: Logger.php:199
$relayState
getBaseURL($t, $type='get', $key=null, $value=null)
Definition: showstats.php:145
$values
$nameid
Definition: status.php:36
getConfig()
Retrieve the configuration for this IdP.
Definition: IdP.php:158
input
Definition: langcheck.php:166
$post
Definition: post.php:34
static postResponse($url, $wresult, $wctx)
Definition: ADFS.php:130
$query
if(array_key_exists('yes', $_REQUEST)) $attributes
Definition: getconsent.php:85
catch(Exception $e) if(!($request instanceof \SAML2\ArtifactResolve)) $issuer
exit
Definition: backend.php:16
if(!isset($associations[$assocId])) $association
static generateResponse($issuer, $target, $nameid, $attributes, $assertionLifetime)
Definition: ADFS.php:40
$idp
Definition: prp.php:13
static getCertPath($path)
Resolves a path that may be relative to the cert-directory.
Definition: Config.php:22
$sc SubjectConfirmationData NotOnOrAfter
$idpMetadata
if(!isset($_REQUEST['association'])) $assocId
$url
$response
$target
Definition: test.php:19
$key
Definition: croninfo.php:18
static loadFromArray($config, $location='[ARRAY]', $instance=null)
Loads a configuration from the given array.
static sendResponse(array $state)
Definition: ADFS.php:152
static receiveLogoutMessage(SimpleSAML_IdP $idp)
Definition: ADFS.php:208