/** * Notify about an event. * * @param string $event The event. * @param array $data Event data. Optional. */ public static function log($event, array $data = array()) { assert('is_string($event)'); assert('!isset($data["op"])'); assert('!isset($data["time"])'); assert('!isset($data["_id"])'); if (!self::$initialized) { self::initOutputs(); self::$initialized = TRUE; } if (empty(self::$outputs)) { /* Not enabled. */ return; } $data['op'] = $event; $data['time'] = microtime(TRUE); /* The ID generation is designed to cluster IDs related in time close together. */ $int_t = (int) $data['time']; $hd = openssl_random_pseudo_bytes(16); $data['_id'] = sprintf('%016x%s', $int_t, bin2hex($hd)); foreach (self::$outputs as $out) { $out->emit($data); } }
<?php if (!isset($_REQUEST['id'])) { throw new SimpleSAML_Error_BadRequest('Missing required parameter: id'); } if (isset($_REQUEST['type'])) { $type = (string) $_REQUEST['type']; if (!in_array($type, array('init', 'js', 'nojs', 'embed'), TRUE)) { throw new SimpleSAML_Error_BadRequest('Invalid value for type.'); } } else { $type = 'init'; } if ($type !== 'embed' && $type !== 'async') { SimpleSAML_Logger::stats('slo-iframe ' . $type); SimpleSAML_Stats::log('core:idp:logout-iframe:page', array('type' => $type)); } $state = SimpleSAML_Auth_State::loadState($_REQUEST['id'], 'core:Logout-IFrame'); $idp = SimpleSAML_IdP::getByState($state); if ($type !== 'init') { /* Update association state. */ $associations = $idp->getAssociations(); foreach ($state['core:Logout-IFrame:Associations'] as $assocId => &$sp) { $spId = sha1($assocId); /* Move SPs from 'onhold' to 'inprogress'. */ if ($sp['core:Logout-IFrame:State'] === 'onhold') { $sp['core:Logout-IFrame:State'] = 'inprogress'; } /* Check for update through request. */ if (isset($_REQUEST[$spId])) { $s = $_REQUEST[$spId];
/** * Receive a logout message. * * @param SimpleSAML_IdP $idp The IdP we are receiving it for. */ public static function receiveLogoutMessage(SimpleSAML_IdP $idp) { $binding = SAML2_Binding::getCurrentBinding(); $message = $binding->receive(); $spEntityId = $message->getIssuer(); if ($spEntityId === NULL) { /* Without an issuer we have no way to respond to the message. */ throw new SimpleSAML_Error_BadRequest('Received message on logout endpoint without issuer.'); } $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); $idpMetadata = $idp->getConfig(); $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote'); sspmod_saml_Message::validateMessage($spMetadata, $idpMetadata, $message); if ($message instanceof SAML2_LogoutResponse) { SimpleSAML_Logger::info('Received SAML 2.0 LogoutResponse from: ' . var_export($spEntityId, TRUE)); $statsData = array('spEntityID' => $spEntityId, 'idpEntityID' => $idpMetadata->getString('entityid')); if (!$message->isSuccess()) { $statsData['error'] = $message->getStatus(); } SimpleSAML_Stats::log('saml:idp:LogoutResponse:recv', $statsData); $relayState = $message->getRelayState(); if (!$message->isSuccess()) { $logoutError = sspmod_saml_Message::getResponseError($message); SimpleSAML_Logger::warning('Unsuccessful logout. Status was: ' . $logoutError); } else { $logoutError = NULL; } $assocId = 'saml:' . $spEntityId; $idp->handleLogoutResponse($assocId, $relayState, $logoutError); } elseif ($message instanceof SAML2_LogoutRequest) { SimpleSAML_Logger::info('Received SAML 2.0 LogoutRequest from: ' . var_export($spEntityId, TRUE)); SimpleSAML_Stats::log('saml:idp:LogoutRequest:recv', array('spEntityID' => $spEntityId, 'idpEntityID' => $idpMetadata->getString('entityid'))); $spStatsId = $spMetadata->getString('core:statistics-id', $spEntityId); SimpleSAML_Logger::stats('saml20-idp-SLO spinit ' . $spStatsId . ' ' . $idpMetadata->getString('entityid')); $state = array('Responder' => array('sspmod_saml_IdP_SAML2', 'sendLogoutResponse'), 'saml:SPEntityId' => $spEntityId, 'saml:RelayState' => $message->getRelayState(), 'saml:RequestId' => $message->getId()); $assocId = 'saml:' . $spEntityId; $idp->handleLogoutRequest($state, $assocId); } else { throw new SimpleSAML_Error_BadRequest('Unknown message received on logout endpoint: ' . get_class($message)); } }
/** * Retrieve a logout URL for a given logout association. * * @param SimpleSAML_IdP $idp The IdP we are sending a logout request from. * @param array $association The association that should be terminated. * @param string|NULL $relayState An id that should be carried across the logout. */ public static function getLogoutURL(SimpleSAML_IdP $idp, array $association, $relayState) { assert('is_string($relayState) || is_null($relayState)'); SimpleSAML_Logger::info('Sending SAML 2.0 LogoutRequest to: ' . var_export($association['saml:entityID'], TRUE)); $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); $idpMetadata = $idp->getConfig(); $spMetadata = $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote'); $lr = sspmod_saml_Message::buildLogoutRequest($idpMetadata, $spMetadata); $lr->setRelayState($relayState); $lr->setSessionIndex($association['saml:SessionIndex']); $lr->setNameId($association['saml:NameID']); $assertionLifetime = $spMetadata->getInteger('assertion.lifetime', NULL); if ($assertionLifetime === NULL) { $assertionLifetime = $idpMetadata->getInteger('assertion.lifetime', 300); } $lr->setNotOnOrAfter(time() + $assertionLifetime); $encryptNameId = $spMetadata->getBoolean('nameid.encryption', NULL); if ($encryptNameId === NULL) { $encryptNameId = $idpMetadata->getBoolean('nameid.encryption', FALSE); } if ($encryptNameId) { $lr->encryptNameId(sspmod_saml_Message::getEncryptionKey($spMetadata)); } SimpleSAML_Stats::log('saml:idp:LogoutRequest:sent', array('spEntityID' => $association['saml:entityID'], 'idpEntityID' => $idpMetadata->getString('entityid'))); $binding = new SAML2_HTTPRedirect(); return $binding->getRedirectURL($lr); }
/** * Process a authentication response * * This function saves the state, and redirects the user to the page where * the user can authorize the release of the attributes. * If storage is used and the consent has already been given the user is * passed on. * * @param array &$state The state of the response. * * @return void */ public function process(&$state) { assert('is_array($state)'); assert('array_key_exists("UserID", $state)'); assert('array_key_exists("Destination", $state)'); assert('array_key_exists("entityid", $state["Destination"])'); assert('array_key_exists("metadata-set", $state["Destination"])'); assert('array_key_exists("entityid", $state["Source"])'); assert('array_key_exists("metadata-set", $state["Source"])'); $spEntityId = $state['Destination']['entityid']; $idpEntityId = $state['Source']['entityid']; $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); /** * If the consent module is active on a bridge $state['saml:sp:IdP'] * will contain an entry id for the remote IdP. If not, then the * consent module is active on a local IdP and nothing needs to be * done. */ if (isset($state['saml:sp:IdP'])) { $idpEntityId = $state['saml:sp:IdP']; $idpmeta = $metadata->getMetaData($idpEntityId, 'saml20-idp-remote'); $state['Source'] = $idpmeta; } $statsData = array('spEntityID' => $spEntityId); // Do not use consent if disabled if (isset($state['Source']['consent.disable']) && self::checkDisable($state['Source']['consent.disable'], $spEntityId)) { SimpleSAML_Logger::debug('Consent: Consent disabled for entity ' . $spEntityId . ' with IdP ' . $idpEntityId); SimpleSAML_Stats::log('consent:disabled', $statsData); return; } if (isset($state['Destination']['consent.disable']) && self::checkDisable($state['Destination']['consent.disable'], $idpEntityId)) { SimpleSAML_Logger::debug('Consent: Consent disabled for entity ' . $spEntityId . ' with IdP ' . $idpEntityId); SimpleSAML_Stats::log('consent:disabled', $statsData); return; } if ($this->_store !== null) { $source = $state['Source']['metadata-set'] . '|' . $idpEntityId; $destination = $state['Destination']['metadata-set'] . '|' . $spEntityId; $attributes = $state['Attributes']; // Remove attributes that do not require consent foreach ($attributes as $attrkey => $attrval) { if (in_array($attrkey, $this->_noconsentattributes)) { unset($attributes[$attrkey]); } } SimpleSAML_Logger::debug('Consent: userid: ' . $state['UserID']); SimpleSAML_Logger::debug('Consent: source: ' . $source); SimpleSAML_Logger::debug('Consent: destination: ' . $destination); $userId = self::getHashedUserID($state['UserID'], $source); $targetedId = self::getTargetedID($state['UserID'], $source, $destination); $attributeSet = self::getAttributeHash($attributes, $this->_includeValues); SimpleSAML_Logger::debug('Consent: hasConsent() [' . $userId . '|' . $targetedId . '|' . $attributeSet . ']'); try { if ($this->_store->hasConsent($userId, $targetedId, $attributeSet)) { // Consent already given SimpleSAML_Logger::stats('Consent: Consent found'); SimpleSAML_Stats::log('consent:found', $statsData); return; } SimpleSAML_Logger::stats('Consent: Consent notfound'); SimpleSAML_Stats::log('consent:notfound', $statsData); $state['consent:store'] = $this->_store; $state['consent:store.userId'] = $userId; $state['consent:store.destination'] = $targetedId; $state['consent:store.attributeSet'] = $attributeSet; } catch (Exception $e) { SimpleSAML_Logger::error('Consent: Error reading from storage: ' . $e->getMessage()); SimpleSAML_Logger::stats('Consent: Failed'); SimpleSAML_Stats::log('consent:failed', $statsData); } } else { SimpleSAML_Logger::stats('Consent: No storage'); SimpleSAML_Stats::log('consent:nostorage', $statsData); } $state['consent:focus'] = $this->_focus; $state['consent:checked'] = $this->_checked; $state['consent:hiddenAttributes'] = $this->_hiddenAttributes; $state['consent:noconsentattributes'] = $this->_noconsentattributes; $state['consent:showNoConsentAboutService'] = $this->_showNoConsentAboutService; // User interaction nessesary. Throw exception on isPassive request if (isset($state['isPassive']) && $state['isPassive'] == true) { SimpleSAML_Stats::log('consent:nopassive', $statsData); throw new SimpleSAML_Error_NoPassive('Unable to give consent on passive request.'); } // Save state and redirect $id = SimpleSAML_Auth_State::saveState($state, 'consent:request'); $url = SimpleSAML_Module::getModuleURL('consent/getconsent.php'); SimpleSAML_Utilities::redirectTrustedURL($url, array('StateId' => $id)); }
} else { $spentityid = 'UNKNOWN'; } } // The user has pressed the yes-button if (array_key_exists('yes', $_REQUEST)) { if (array_key_exists('saveconsent', $_REQUEST)) { SimpleSAML_Logger::stats('consentResponse remember'); } else { SimpleSAML_Logger::stats('consentResponse rememberNot'); } $statsInfo = array('remember' => array_key_exists('saveconsent', $_REQUEST)); if (isset($state['Destination']['entityid'])) { $statsInfo['spEntityID'] = $state['Destination']['entityid']; } SimpleSAML_Stats::log('consent:accept', $statsInfo); if (array_key_exists('consent:store', $state) && array_key_exists('saveconsent', $_REQUEST) && $_REQUEST['saveconsent'] === '1') { /* Save consent. */ $store = $state['consent:store']; $userId = $state['consent:store.userId']; $targetedId = $state['consent:store.destination']; $attributeSet = $state['consent:store.attributeSet']; SimpleSAML_Logger::debug('Consent - saveConsent() : [' . $userId . '|' . $targetedId . '|' . $attributeSet . ']'); try { $store->saveConsent($userId, $targetedId, $attributeSet); } catch (Exception $e) { SimpleSAML_Logger::error('Consent: Error writing to storage: ' . $e->getMessage()); } } SimpleSAML_Auth_ProcessingChain::resumeProcessing($state); }
/* Find the status of all SPs. */ foreach ($SPs as $assocId => &$sp) { $spId = 'logout-iframe-' . sha1($assocId); if (isset($_REQUEST[$spId])) { $spStatus = $_REQUEST[$spId]; if ($spStatus === 'completed' || $spStatus === 'failed') { $sp['core:Logout-IFrame:State'] = $spStatus; } } if (!isset($associations[$assocId])) { $sp['core:Logout-IFrame:State'] = 'completed'; } } /* Terminate the associations. */ foreach ($SPs as $assocId => $sp) { if ($sp['core:Logout-IFrame:State'] === 'completed') { $idp->terminateAssociation($assocId); } else { SimpleSAML_Logger::warning('Unable to terminate association with ' . var_export($assocId, TRUE) . '.'); if (isset($sp['saml:entityID'])) { $spId = $sp['saml:entityID']; } else { $spId = $assocId; } SimpleSAML_Logger::stats('slo-iframe-fail ' . $spId); SimpleSAML_Stats::log('core:idp:logout-iframe:spfail', array('sp' => $spId)); $state['core:Failed'] = TRUE; } } /* We are done. */ $idp->finishLogout($state);
*/ if (!array_key_exists('StateId', $_REQUEST)) { throw new SimpleSAML_Error_BadRequest('Missing required StateId query parameter.'); } $id = $_REQUEST['StateId']; // sanitize the input $sid = SimpleSAML_Utilities::parseStateID($id); if (!is_null($sid['url'])) { SimpleSAML_Utilities::checkURLAllowed($sid['url']); } $state = SimpleSAML_Auth_State::loadState($id, 'consent:request'); $resumeFrom = SimpleSAML_Module::getModuleURL('consent/getconsent.php', array('StateId' => $id)); $logoutLink = SimpleSAML_Module::getModuleURL('consent/logout.php', array('StateId' => $id)); $aboutService = null; if (!isset($state['consent:showNoConsentAboutService']) || $state['consent:showNoConsentAboutService']) { if (isset($state['Destination']['url.about'])) { $aboutService = $state['Destination']['url.about']; } } $statsInfo = array(); if (isset($state['Destination']['entityid'])) { $statsInfo['spEntityID'] = $state['Destination']['entityid']; } SimpleSAML_Stats::log('consent:reject', $statsInfo); $globalConfig = SimpleSAML_Configuration::getInstance(); $t = new SimpleSAML_XHTML_Template($globalConfig, 'consent:noconsent.php'); $t->data['dstMetadata'] = $state['Destination']; $t->data['resumeFrom'] = $resumeFrom; $t->data['aboutService'] = $aboutService; $t->data['logoutLink'] = $logoutLink; $t->show();
/** * Receive an authentication request. * * @param SimpleSAML_IdP $idp The IdP we are receiving it for. */ public static function receiveAuthnRequest(SimpleSAML_IdP $idp) { if (isset($_REQUEST['cookieTime'])) { $cookieTime = (int) $_REQUEST['cookieTime']; if ($cookieTime + 5 > time()) { /* * Less than five seconds has passed since we were * here the last time. Cookies are probably disabled. */ \SimpleSAML\Utils\HTTP::checkSessionCookie(\SimpleSAML\Utils\HTTP::getSelfURL()); } } if (!isset($_REQUEST['providerId'])) { throw new SimpleSAML_Error_BadRequest('Missing providerId parameter.'); } $spEntityId = (string) $_REQUEST['providerId']; if (!isset($_REQUEST['shire'])) { throw new SimpleSAML_Error_BadRequest('Missing shire parameter.'); } $shire = (string) $_REQUEST['shire']; if (isset($_REQUEST['target'])) { $target = $_REQUEST['target']; } else { $target = NULL; } SimpleSAML\Logger::info('Shib1.3 - IdP.SSOService: Got incoming Shib authnRequest from ' . var_export($spEntityId, TRUE) . '.'); $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'shib13-sp-remote'); $found = FALSE; foreach ($spMetadata->getEndpoints('AssertionConsumerService') as $ep) { if ($ep['Binding'] !== 'urn:oasis:names:tc:SAML:1.0:profiles:browser-post') { continue; } if ($ep['Location'] !== $shire) { continue; } $found = TRUE; break; } if (!$found) { throw new Exception('Invalid AssertionConsumerService for SP ' . var_export($spEntityId, TRUE) . ': ' . var_export($shire, TRUE)); } SimpleSAML_Stats::log('saml:idp:AuthnRequest', array('spEntityID' => $spEntityId, 'protocol' => 'saml1')); $sessionLostURL = \SimpleSAML\Utils\HTTP::addURLParameters(\SimpleSAML\Utils\HTTP::getSelfURL(), array('cookieTime' => time())); $state = array('Responder' => array('sspmod_saml_IdP_SAML1', 'sendResponse'), 'SPMetadata' => $spMetadata->toArray(), SimpleSAML_Auth_State::RESTART => $sessionLostURL, 'saml:shire' => $shire, 'saml:target' => $target, 'saml:AuthnRequestReceivedAt' => microtime(TRUE)); $idp->handleAuthenticationRequest($state); }
} $associations = $idp->getAssociations(); if (!isset($associations[$assocId])) { throw new SimpleSAML_Error_BadRequest('Invalid association id.'); } $association = $associations[$assocId]; $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); $idpMetadata = $idp->getConfig(); $spMetadata = $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote'); $lr = sspmod_saml_Message::buildLogoutRequest($idpMetadata, $spMetadata); $lr->setSessionIndex($association['saml:SessionIndex']); $lr->setNameId($association['saml:NameID']); $assertionLifetime = $spMetadata->getInteger('assertion.lifetime', NULL); if ($assertionLifetime === NULL) { $assertionLifetime = $idpMetadata->getInteger('assertion.lifetime', 300); } $lr->setNotOnOrAfter(time() + $assertionLifetime); $encryptNameId = $spMetadata->getBoolean('nameid.encryption', NULL); if ($encryptNameId === NULL) { $encryptNameId = $idpMetadata->getBoolean('nameid.encryption', FALSE); } if ($encryptNameId) { $lr->encryptNameId(sspmod_saml_Message::getEncryptionKey($spMetadata)); } SimpleSAML_Stats::log('saml:idp:LogoutRequest:sent', array('spEntityID' => $association['saml:entityID'], 'idpEntityID' => $idpMetadata->getString('entityid'))); $bindings = array(\SAML2\Constants::BINDING_HTTP_POST); $dst = $spMetadata->getDefaultEndpoint('SingleLogoutService', $bindings); $binding = \SAML2\Binding::getBinding($dst['Binding']); $lr->setDestination($dst['Location']); $lr->setRelayState($relayState); $binding->send($lr);