protected function _sendHTTPPost($message, $remoteEntity) { $name = $message['__']['paramname']; $extra = ""; if ($message['__']['ProtocolBinding'] == 'JSON-POST') { if ($relayState = $message['__']['RelayState']) { $relayState = "&RelayState={$relayState}"; } $name = 'j' . $name; $encodedMessage = json_encode($message); $signatureHTMLValue = htmlspecialchars(base64_encode(sha1($remoteEntity['sharedkey'] . sha1("{$name}={$message}{$relayState}")))); $extra .= '<input type="hidden" name="Signature" value="' . $signatureHTMLValue . '">'; } else { // Determine if we should sign the message $wantRequestsSigned = isset($remoteEntity['AuthnRequestsSigned']) && $remoteEntity['AuthnRequestsSigned'] || $this->_server->getCurrentEntitySetting('WantsAuthnRequestsSigned'); if ($name == 'SAMLRequest' && $wantRequestsSigned) { $this->_server->getSessionLog()->debug("HTTP-Redirect: (Re-)Signing"); $message = $this->_server->sign($message); } else { if ($name == 'SAMLResponse' && isset($remoteEntity['WantsAssertionsSigned']) && $remoteEntity['WantsAssertionsSigned']) { $this->_server->getSessionLog()->debug("HTTP-Redirect: (Re-)Signing Assertion"); $message['saml:Assertion']['__t'] = 'saml:Assertion'; $message['saml:Assertion']['_xmlns:saml'] = "urn:oasis:names:tc:SAML:2.0:assertion"; $message['saml:Assertion']['ds:Signature'] = '__placeholder__'; uksort($message['saml:Assertion'], array(__CLASS__, '_usortAssertion')); $message['saml:Assertion'] = $this->_server->sign($message['saml:Assertion']); #$enc = docrypt(certs::$server_crt, $message['saml:Assertion'], 'saml:EncryptedAssertion'); } else { if ($name == 'SAMLResponse' && isset($remoteEntity['WantsResponsesSigned']) && $remoteEntity['WantsResponsesSigned']) { $this->_server->getSessionLog()->debug("HTTP-Redirect: (Re-)Signing"); uksort($message['saml:Assertion'], array(__CLASS__, '_usortAssertion')); $message = $this->_server->sign($message); } } } $encodedMessage = Corto_XmlToArray::array2xml($message); $schemaUrl = 'http://docs.oasis-open.org/security/saml/v2.0/saml-schema-protocol-2.0.xsd'; if ($this->_server->getConfig('debug') && ini_get('allow_url_fopen') && file_exists($schemaUrl)) { $dom = new DOMDocument(); $dom->loadXML($encodedMessage); if (!$dom->schemaValidate($schemaUrl)) { //echo '<pre>'.htmlentities(Corto_XmlToArray::formatXml($encodedMessage)).'</pre>'; //throw new Exception('Message XML doesnt validate against XSD at Oasis-open.org?!'); } } } $extra .= isset($message['__']['RelayState']) ? '<input type="hidden" name="RelayState" value="' . htmlspecialchars($message['__']['RelayState']) . '">' : ''; $extra .= isset($message['__']['return']) ? '<input type="hidden" name="return" value="' . htmlspecialchars($message['__']['return']) . '">' : ''; $encodedMessage = htmlspecialchars(base64_encode($encodedMessage)); $action = $message['_Destination'] . (isset($message['_Recipient']) ? $message['_Recipient'] : ''); $this->_server->getSessionLog()->debug("HTTP-Post: Sending Message: " . var_export($message, true)); $output = $this->_server->renderTemplate('form', array('action' => $action, 'message' => $encodedMessage, 'xtra' => $extra, 'name' => $name, 'trace' => $this->_server->getConfig('debug', false) ? htmlentities(Corto_XmlToArray::formatXml(Corto_XmlToArray::array2xml($message))) : '')); $this->_server->sendOutput($output); }
public function idPsMetadataService() { $entitiesDescriptor = array(Corto_XmlToArray::TAG_NAME_PFX => 'md:EntitiesDescriptor', '_xmlns:md' => 'urn:oasis:names:tc:SAML:2.0:metadata', '_xmlns:mdui' => 'urn:oasis:names:tc:SAML:metadata:ui', '_ID' => $this->_server->getNewId(), 'ds:Signature' => '__placeholder__', 'md:EntityDescriptor' => array()); // Fetch SP Entity Descriptor for the SP Entity ID that is fetched from the request $request = EngineBlock_ApplicationSingleton::getInstance()->getHttpRequest(); $spEntityId = $request->getQueryParameter('sp-entity-id'); if ($spEntityId) { $spEntityDescriptor = $this->_getSpEntityDescriptor($spEntityId); $spEntity = $this->_server->getRemoteEntity($spEntityId); if ($spEntityDescriptor) { $entitiesDescriptor['md:EntityDescriptor'][] = $spEntityDescriptor; } } foreach ($this->_server->getRemoteEntities() as $entityID => $entity) { if (!isset($entity['SingleSignOnService'])) { continue; } $entityDescriptor = array('_validUntil' => $this->_server->timeStamp($this->_server->getCurrentEntitySetting('idpMetadataValidUntilSeconds', 86400)), '_entityID' => $entityID, 'md:IDPSSODescriptor' => array('_protocolSupportEnumeration' => "urn:oasis:names:tc:SAML:2.0:protocol")); if (isset($entity['DisplayName'])) { if (!isset($entityDescriptor['md:IDPSSODescriptor']['md:Extensions'])) { $entityDescriptor['md:IDPSSODescriptor']['md:Extensions'] = array(); } if (!isset($entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'])) { $entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'] = array(0 => array()); } foreach ($entity['DisplayName'] as $lang => $name) { if (trim($name) === '') { continue; } if (!isset($entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'][0]['mdui:DisplayName'])) { $entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'][0]['mdui:DisplayName'] = array(); } $entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'][0]['mdui:DisplayName'][] = array('_xml:lang' => $lang, '__v' => $name); } } if (isset($entity['Description'])) { if (!isset($entityDescriptor['md:IDPSSODescriptor']['md:Extensions'])) { $entityDescriptor['md:IDPSSODescriptor']['md:Extensions'] = array(); } if (!isset($entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'])) { $entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'] = array(0 => array()); } foreach ($entity['Description'] as $lang => $name) { if (trim($name) === '') { continue; } if (!isset($entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'][0]['mdui:Description'])) { $entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'][0]['mdui:Description'] = array(); } $entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'][0]['mdui:Description'][] = array('_xml:lang' => $lang, '__v' => $name); } } $hasLogoHeight = isset($entity['Logo']['Height']) && $entity['Logo']['Height']; $hasLogoWidth = isset($entity['Logo']['Width']) && $entity['Logo']['Width']; if (isset($entity['Logo']) && $hasLogoHeight && $hasLogoWidth) { if (!isset($entityDescriptor['md:IDPSSODescriptor']['md:Extensions'])) { $entityDescriptor['md:IDPSSODescriptor']['md:Extensions'] = array(); } if (!isset($entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'])) { $entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'] = array(0 => array()); } $entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'][0]['mdui:Logo'] = array(array('_height' => $entity['Logo']['Height'], '_width' => $entity['Logo']['Width'], '__v' => $entity['Logo']['URL'])); } if (isset($entity['GeoLocation']) && !empty($entity['GeoLocation'])) { if (!isset($entityDescriptor['md:IDPSSODescriptor']['md:Extensions'])) { $entityDescriptor['md:IDPSSODescriptor']['md:Extensions'] = array(); } if (!isset($entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:DiscoHints'])) { $entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:DiscoHints'] = array(0 => array()); } $entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:DiscoHints'][0]['mdui:GeolocationHint'] = array(array('__v' => $entity['GeoLocation'])); } if (isset($entity['Keywords'])) { if (!isset($entityDescriptor['md:IDPSSODescriptor']['md:Extensions'])) { $entityDescriptor['md:IDPSSODescriptor']['md:Extensions'] = array(); } if (!isset($entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'])) { $entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'] = array(0 => array()); } $uiInfo =& $entityDescriptor['md:IDPSSODescriptor']['md:Extensions']['mdui:UIInfo'][0]; foreach ($entity['Keywords'] as $lang => $name) { if (trim($name) === '') { continue; } if (!isset($uiInfo['mdui:Keywords'])) { $uiInfo['mdui:Keywords'] = array(); } $uiInfo['mdui:Keywords'][] = array(array('_xml:lang' => $lang, '__v' => $name)); } } // Check if an alternative Public & Private key have been set for a SP // If yes, use these in the metadata of Engineblock if (isset($spEntity) && $spEntity['AlternatePrivateKey'] && $spEntity['AlternatePublicKey']) { $publicCertificate = $spEntity['AlternatePublicKey']; } else { $certificates = $this->_server->getCurrentEntitySetting('certificates', array()); $publicCertificate = $certificates['public']; } if (isset($publicCertificate)) { $entityDescriptor['md:IDPSSODescriptor']['md:KeyDescriptor'] = array(array('_xmlns:ds' => 'http://www.w3.org/2000/09/xmldsig#', '_use' => 'signing', 'ds:KeyInfo' => array('ds:X509Data' => array('ds:X509Certificate' => array('__v' => $this->_server->getCertDataFromPem($publicCertificate))))), array('_xmlns:ds' => 'http://www.w3.org/2000/09/xmldsig#', '_use' => 'encryption', 'ds:KeyInfo' => array('ds:X509Data' => array('ds:X509Certificate' => array('__v' => $this->_server->getCertDataFromPem($publicCertificate)))))); } $entityDescriptor['md:IDPSSODescriptor']['md:NameIDFormat'] = array('__v' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'); $entityDescriptor['md:IDPSSODescriptor']['md:SingleSignOnService'] = array('_Binding' => self::DEFAULT_REQUEST_BINDING, '_Location' => $this->_server->getCurrentEntityUrl('singleSignOnService', $entityID)); $entitiesDescriptor['md:EntityDescriptor'][] = $entityDescriptor; } $alternatePublicKey = isset($spEntity['AlternatePublicKey']) ? $spEntity['AlternatePublicKey'] : null; $alternatePrivateKey = isset($spEntity['AlternatePublicKey']) ? $spEntity['AlternatePublicKey'] : null; $entitiesDescriptor = $this->_server->sign($entitiesDescriptor, $alternatePublicKey, $alternatePrivateKey); $xml = Corto_XmlToArray::array2xml($entitiesDescriptor); $schemaUrl = 'http://docs.oasis-open.org/security/saml/v2.0/saml-schema-metadata-2.0.xsd'; if ($this->_server->getConfig('debug', false) && ini_get('allow_url_fopen') && file_exists($schemaUrl)) { $dom = new DOMDocument(); $dom->loadXML($xml); if (!$dom->schemaValidate($schemaUrl)) { echo '<pre>' . htmlentities(Corto_XmlToArray::formatXml($xml)) . '</pre>'; throw new Exception('Metadata XML doesnt validate against XSD at Oasis-open.org?!'); } } $this->_server->sendHeader('Content-Type', 'application/xml'); //$this->_server->sendHeader('Content-Type', 'application/samlmetadata+xml'); $this->_server->sendOutput($xml); }
/** * Validates xml against oasis SAML 2 spec * * @param string $xml * @return void * @throws Exception in case validating itself fails or if xml does not validate */ protected function _validateXml($xml) { $inDebugModus = $this->_server->getConfig('debug', false); if ($inDebugModus) { if (!ini_get('allow_url_fopen')) { throw new Exception('Failed validating XML, url_fopen is not allowed'); } // Load schema $schemaUrl = 'http://docs.oasis-open.org/security/saml/v2.0/saml-schema-metadata-2.0.xsd'; $schemaXml = @file_get_contents($schemaUrl); if ($schemaXml === false) { throw new Exception('Failed validating XML, schema url could not be opened: "' . $schemaUrl . '"'); } $schemaXml = $this->_absolutizeSchemaLocations($schemaXml, $schemaUrl); $dom = new DOMDocument(); $dom->loadXML($xml); if (!@$dom->schemaValidateSource($schemaXml)) { $errorInfo = error_get_last(); $errorMessage = $errorInfo['message']; // @todo improve parsing message by creating custom exceptions for which know that structure of messages $parsedErrorMessage = preg_replace('/\\{[^}]*\\}/', '', $errorMessage); echo '<pre>' . htmlentities(Corto_XmlToArray::formatXml($xml)) . '</pre>'; throw new Exception('Metadata XML doesnt validate against XSD at Oasis-open.org: ' . $parsedErrorMessage); } } }