public static function receiveAuthnRequest(SimpleSAML_IdP $idp) { try { // accomodate for disfunctional $_GET "windows" slash decoding in PHP $wctx = $_GET['wctx']; foreach (explode('&', $_SERVER['REQUEST_URI']) as $e) { $a = explode('=', $e); if ($a[0] == 'wctx') { $wctx = urldecode($a[1]); } } $requestid = $wctx; $issuer = $_GET['wtrealm']; $requestcache = array('RequestID' => $requestid, 'Issuer' => $issuer, 'RelayState' => $requestid); $spEntityId = $requestcache['Issuer']; $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'adfs-sp-remote'); SimpleSAML\Logger::info('ADFS - IdP.prp: Incoming Authentication request: ' . $issuer . ' id ' . $requestid); } catch (Exception $exception) { throw new SimpleSAML_Error_Error('PROCESSAUTHNREQUEST', $exception); } $sessionLostURL = NULL; // TODO? $forceAuthn = FALSE; $isPassive = FALSE; $state = array('Responder' => array('sspmod_adfs_IdP_ADFS', 'sendResponse'), 'SPMetadata' => $spMetadata->toArray(), 'ForceAuthn' => $forceAuthn, 'isPassive' => $isPassive, 'adfs:wctx' => $wctx); $idp->handleAuthenticationRequest($state); }
/** * Hook to run a cron job. * * @param array &$croninfo Output */ function sanitycheck_hook_cron(&$croninfo) { assert('is_array($croninfo)'); assert('array_key_exists("summary", $croninfo)'); assert('array_key_exists("tag", $croninfo)'); SimpleSAML\Logger::info('cron [sanitycheck]: Running cron in cron tag [' . $croninfo['tag'] . '] '); try { $sconfig = SimpleSAML_Configuration::getOptionalConfig('config-sanitycheck.php'); $cronTag = $sconfig->getString('cron_tag', null); if ($cronTag === null || $cronTag !== $croninfo['tag']) { return; } $info = array(); $errors = array(); $hookinfo = array('info' => &$info, 'errors' => &$errors); SimpleSAML\Module::callHooks('sanitycheck', $hookinfo); if (count($errors) > 0) { foreach ($errors as $err) { $croninfo['summary'][] = 'Sanitycheck error: ' . $err; } } } catch (Exception $e) { $croninfo['summary'][] = 'Error executing sanity check: ' . $e->getMessage(); } }
function driveProcessingChain($idp_metadata, $source, $sp_metadata, $sp_entityid, $attributes, $userid, $hashAttributes = false) { /* * Create a new processing chain */ $pc = new SimpleSAML_Auth_ProcessingChain($idp_metadata, $sp_metadata, 'idp'); /* * Construct the state. * REMEMBER: Do not set Return URL if you are calling processStatePassive */ $authProcState = array('Attributes' => $attributes, 'Destination' => $sp_metadata, 'Source' => $idp_metadata, 'isPassive' => true); /* * Call processStatePAssive. * We are not interested in any user interaction, only modifications to the attributes */ $pc->processStatePassive($authProcState); $attributes = $authProcState['Attributes']; /* * Generate identifiers and hashes */ $destination = $sp_metadata['metadata-set'] . '|' . $sp_entityid; $targeted_id = sspmod_consent_Auth_Process_Consent::getTargetedID($userid, $source, $destination); $attribute_hash = sspmod_consent_Auth_Process_Consent::getAttributeHash($attributes, $hashAttributes); SimpleSAML\Logger::info('consentAdmin: user: '******'consentAdmin: target: ' . $targeted_id); SimpleSAML\Logger::info('consentAdmin: attribute: ' . $attribute_hash); // Return values return array($targeted_id, $attribute_hash, $attributes); }
/** * Filter out YubiKey 'otp' attribute and replace it with * a 'yubiPrefix' attribute that leaves out the dynamic part. * * @param array &$state The state we should update. */ public function process(&$state) { assert('is_array($state)'); assert('array_key_exists("Attributes", $state)'); $attributes = $state['Attributes']; SimpleSAML\Logger::debug('OTP2YubiPrefix: enter with attributes: ' . implode(',', array_keys($attributes))); $otps = $attributes['otp']; $otp = $otps['0']; $token_size = 32; $identity = substr($otp, 0, strlen($otp) - $token_size); $attributes['yubiPrefix'] = array($identity); SimpleSAML\Logger::info('OTP2YubiPrefix: otp: ' . $otp . ' identity: ' . $identity . ' (otp keys: ' . implode(',', array_keys($otps)) . ')'); unset($attributes['otp']); SimpleSAML\Logger::debug('OTP2YubiPrefix: leaving with attributes: ' . implode(',', array_keys($attributes))); }
/** * 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); }
/** * Continue the logout operation. * * This function will never return. * * @param string $assocId The association that is terminated. * @param string|null $relayState The RelayState from the start of the logout. * @param SimpleSAML_Error_Exception|null $error The error that occurred during session termination (if any). * * @throws SimpleSAML_Error_Exception If the RelayState was lost during logout. */ public function onResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = null) { assert('is_string($assocId)'); assert('is_string($relayState) || is_null($relayState)'); if ($relayState === null) { throw new SimpleSAML_Error_Exception('RelayState lost during logout.'); } $state = SimpleSAML_Auth_State::loadState($relayState, 'core:LogoutTraditional'); if ($error === null) { SimpleSAML\Logger::info('Logged out of ' . var_export($assocId, true) . '.'); $this->idp->terminateAssociation($assocId); } else { SimpleSAML\Logger::warning('Error received from ' . var_export($assocId, true) . ' during logout:'); $error->logWarning(); $state['core:Failed'] = true; } self::logoutNextSP($state); }
/** * Hook to run a cron job. * * @param array &$croninfo Output */ function riak_hook_cron(&$croninfo) { assert('is_array($croninfo)'); assert('array_key_exists("summary", $croninfo)'); assert('array_key_exists("tag", $croninfo)'); if ($croninfo['tag'] !== 'hourly') { return; } try { $store = new sspmod_riak_Store_Store(); $result = $store->bucket->indexSearch('expires', 'int', 1, time() - 30); foreach ($result as $link) { $link->getBinary()->delete(); } SimpleSAML\Logger::info(sprintf("deleted %s riak key%s", sizeof($result), sizeof($result) == 1 ? '' : 's')); } catch (Exception $e) { $message = 'riak threw exception: ' . $e->getMessage(); SimpleSAML\Logger::warning($message); $croninfo['summary'][] = $message; } }
/** * Check for consent. * * This function checks whether a given user has authorized the release of the attributes identified by * $attributeSet from $source to $destination. * * @param string $userId The hash identifying the user at an IdP. * @param string $destinationId A string which identifies the destination. * @param string $attributeSet A hash which identifies the attributes. * * @return bool True if the user has given consent earlier, false if not (or on error). */ public function hasConsent($userId, $destinationId, $attributeSet) { assert('is_string($userId)'); assert('is_string($destinationId)'); assert('is_string($attributeSet)'); $cookieName = self::_getCookieName($userId, $destinationId); $data = $userId . ':' . $attributeSet . ':' . $destinationId; SimpleSAML\Logger::debug('Consent cookie - Get [' . $data . ']'); if (!array_key_exists($cookieName, $_COOKIE)) { SimpleSAML\Logger::debug('Consent cookie - no cookie with name \'' . $cookieName . '\'.'); return false; } if (!is_string($_COOKIE[$cookieName])) { SimpleSAML\Logger::warning('Value of consent cookie wasn\'t a string. Was: ' . var_export($_COOKIE[$cookieName], true)); return false; } $data = self::_sign($data); if ($_COOKIE[$cookieName] !== $data) { SimpleSAML\Logger::info('Attribute set changed from the last time consent was given.'); return false; } SimpleSAML\Logger::debug('Consent cookie - found cookie with correct name and value.'); return true; }
/** * Start a SAML 2 logout operation. * * @param array $state The logout state. */ public function startSLO2(&$state) { assert('is_array($state)'); assert('array_key_exists("saml:logout:IdP", $state)'); assert('array_key_exists("saml:logout:NameID", $state)'); assert('array_key_exists("saml:logout:SessionIndex", $state)'); $id = SimpleSAML_Auth_State::saveState($state, 'saml:slosent'); $idp = $state['saml:logout:IdP']; $nameId = $state['saml:logout:NameID']; $sessionIndex = $state['saml:logout:SessionIndex']; $idpMetadata = $this->getIdPMetadata($idp); $endpoint = $idpMetadata->getEndpointPrioritizedByBinding('SingleLogoutService', array(\SAML2\Constants::BINDING_HTTP_REDIRECT, \SAML2\Constants::BINDING_HTTP_POST), FALSE); if ($endpoint === FALSE) { SimpleSAML\Logger::info('No logout endpoint for IdP ' . var_export($idp, TRUE) . '.'); return; } $lr = sspmod_saml_Message::buildLogoutRequest($this->metadata, $idpMetadata); $lr->setNameId($nameId); $lr->setSessionIndex($sessionIndex); $lr->setRelayState($id); $lr->setDestination($endpoint['Location']); $encryptNameId = $idpMetadata->getBoolean('nameid.encryption', NULL); if ($encryptNameId === NULL) { $encryptNameId = $this->metadata->getBoolean('nameid.encryption', FALSE); } if ($encryptNameId) { $lr->encryptNameId(sspmod_saml_Message::getEncryptionKey($idpMetadata)); } $b = \SAML2\Binding::getBinding($endpoint['Binding']); $b->send($lr); assert('FALSE'); }
/** * 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'); $bindings = array(SAML2_Const::BINDING_HTTP_REDIRECT, SAML2_Const::BINDING_HTTP_POST); $dst = $spMetadata->getEndpointPrioritizedByBinding('SingleLogoutService', $bindings); if ($dst['Binding'] === SAML2_Const::BINDING_HTTP_POST) { $params = array('association' => $association['id'], 'idp' => $idp->getId()); if ($relayState !== NULL) { $params['RelayState'] = $relayState; } return SimpleSAML\Module::getModuleURL('core/idp/logout-iframe-post.php', $params); } $lr = self::buildLogoutRequest($idpMetadata, $spMetadata, $association, $relayState); $lr->setDestination($dst['Location']); $binding = new SAML2_HTTPRedirect(); return $binding->getRedirectURL($lr); }
/** * Attempt to log in using the given username and password. * * @param string $username The username the user wrote. * @param string $password The password the user wrote. * @return array Associative array with the user's attributes. */ protected function login($username, $password) { assert('is_string($username)'); assert('is_string($password)'); $radius = radius_auth_open(); /* Try to add all radius servers, trigger a failure if no one works. */ $success = false; foreach ($this->servers as $server) { if (!isset($server['port'])) { $server['port'] = 1812; } if (!radius_add_server($radius, $server['hostname'], $server['port'], $server['secret'], $this->timeout, $this->retries)) { SimpleSAML\Logger::info("Could not add radius server: " . radius_strerror($radius)); continue; } $success = true; } if (!$success) { throw new Exception('Error adding radius servers, no servers available'); } if (!radius_create_request($radius, RADIUS_ACCESS_REQUEST)) { throw new Exception('Error creating radius request: ' . radius_strerror($radius)); } if ($this->realm === null) { radius_put_attr($radius, RADIUS_USER_NAME, $username); } else { radius_put_attr($radius, RADIUS_USER_NAME, $username . '@' . $this->realm); } radius_put_attr($radius, RADIUS_USER_PASSWORD, $password); if ($this->nasIdentifier !== null) { radius_put_attr($radius, RADIUS_NAS_IDENTIFIER, $this->nasIdentifier); } $res = radius_send_request($radius); if ($res != RADIUS_ACCESS_ACCEPT) { switch ($res) { case RADIUS_ACCESS_REJECT: /* Invalid username or password. */ throw new SimpleSAML_Error_Error('WRONGUSERPASS'); case RADIUS_ACCESS_CHALLENGE: throw new Exception('Radius authentication error: Challenge requested, but not supported.'); default: throw new Exception('Error during radius authentication: ' . radius_strerror($radius)); } } /* If we get this far, we have a valid login. */ $attributes = array(); if ($this->usernameAttribute !== null) { $attributes[$this->usernameAttribute] = array($username); } if ($this->vendor === null) { /* * We aren't interested in any vendor-specific attributes. We are * therefore done now. */ return $attributes; } /* get AAI attribute sets. Contributed by Stefan Winter, (c) RESTENA */ while ($resa = radius_get_attr($radius)) { if (!is_array($resa)) { throw new Exception('Error getting radius attributes: ' . radius_strerror($radius)); } /* Use the received user name */ if ($resa['attr'] == RADIUS_USER_NAME) { $attributes[$this->usernameAttribute] = array($resa['data']); continue; } if ($resa['attr'] !== RADIUS_VENDOR_SPECIFIC) { continue; } $resv = radius_get_vendor_attr($resa['data']); if (!is_array($resv)) { throw new Exception('Error getting vendor specific attribute: ' . radius_strerror($radius)); } $vendor = $resv['vendor']; $attrv = $resv['attr']; $datav = $resv['data']; if ($vendor != $this->vendor || $attrv != $this->vendorType) { continue; } $attrib_name = strtok($datav, '='); $attrib_value = strtok('='); /* if the attribute name is already in result set, add another value */ if (array_key_exists($attrib_name, $attributes)) { $attributes[$attrib_name][] = $attrib_value; } else { $attributes[$attrib_name] = array($attrib_value); } } /* end of contribution */ return $attributes; }
/** * Add attributes from an LDAP server. * * @param array &$request The current request */ public function process(&$request) { assert('is_array($request)'); assert('array_key_exists("Attributes", $request)'); $attributes =& $request['Attributes']; // perform a merge on the ldap_search_filter // loop over the attributes and build the search and replace arrays foreach ($attributes as $attr => $val) { $arrSearch[] = '%' . $attr . '%'; if (strlen($val[0]) > 0) { $arrReplace[] = SimpleSAML_Auth_LDAP::escape_filter_value($val[0]); } else { $arrReplace[] = ''; } } // merge the attributes into the ldap_search_filter $filter = str_replace($arrSearch, $arrReplace, $this->search_filter); if (strpos($filter, '%') !== false) { SimpleSAML\Logger::info('AttributeAddFromLDAP: There are non-existing attributes in the search filter. (' . $this->search_filter . ')'); return; } if (!in_array($this->attr_policy, array('merge', 'replace', 'add'))) { SimpleSAML\Logger::warning("AttributeAddFromLDAP: 'attribute.policy' must be one of 'merge'," . "'replace' or 'add'."); return; } // search for matching entries try { $entries = $this->getLdap()->searchformultiple($this->base_dn, $filter, array_values($this->search_attributes), true, false); } catch (Exception $e) { return; // silent fail, error is still logged by LDAP search } // handle [multiple] values foreach ($entries as $entry) { foreach ($this->search_attributes as $target => $name) { if (is_numeric($target)) { $target = $name; } if (isset($attributes[$target]) && $this->attr_policy === 'replace') { unset($attributes[$target]); } $name = strtolower($name); if (isset($entry[$name])) { unset($entry[$name]['count']); if (isset($attributes[$target])) { foreach (array_values($entry[$name]) as $value) { if ($this->attr_policy === 'merge') { if (!in_array($value, $attributes[$target])) { $attributes[$target][] = $value; } } else { $attributes[$target][] = $value; } } } else { $attributes[$target] = array_values($entry[$name]); } } } } }
/** * Attempt to log in using the given username and password. * * Will throw a SimpleSAML_Error_Error('WRONGUSERPASS') if the username or password is wrong. * If there is a configuration problem, an Exception will be thrown. * * @param string $username The username the user wrote. * @param string $password The password the user wrote. * @param arrray $sasl_args Array of SASL options for LDAP bind. * @return array Associative array with the users attributes. */ public function login($username, $password, array $sasl_args = NULL) { assert('is_string($username)'); assert('is_string($password)'); if (empty($password)) { SimpleSAML\Logger::info($this->location . ': Login with empty password disallowed.'); throw new SimpleSAML_Error_Error('WRONGUSERPASS'); } $ldap = new SimpleSAML_Auth_LDAP($this->hostname, $this->enableTLS, $this->debug, $this->timeout, $this->port, $this->referrals); if (!$this->searchEnable) { $ldapusername = addcslashes($username, ',+"\\<>;*'); $dn = str_replace('%username%', $ldapusername, $this->dnPattern); } else { if ($this->searchUsername !== NULL) { if (!$ldap->bind($this->searchUsername, $this->searchPassword)) { throw new Exception('Error authenticating using search username & password.'); } } $dn = $ldap->searchfordn($this->searchBase, $this->searchAttributes, $username, TRUE, $this->searchFilter); if ($dn === NULL) { /* User not found with search. */ SimpleSAML\Logger::info($this->location . ': Unable to find users DN. username=\'' . $username . '\''); throw new SimpleSAML_Error_Error('WRONGUSERPASS'); } } if (!$ldap->bind($dn, $password, $sasl_args)) { SimpleSAML\Logger::info($this->location . ': ' . $username . ' failed to authenticate. DN=' . $dn); throw new SimpleSAML_Error_Error('WRONGUSERPASS'); } /* In case of SASL bind, authenticated and authorized DN may differ */ if (isset($sasl_args)) { $dn = $ldap->whoami($this->searchBase, $this->searchAttributes); } /* Are privs needed to get the attributes? */ if ($this->privRead) { /* Yes, rebind with privs */ if (!$ldap->bind($this->privUsername, $this->privPassword)) { throw new Exception('Error authenticating using privileged DN & password.'); } } return $ldap->getAttributes($dn, $this->attributes); }
/** * Print the exception to the log with log level info. * * This function will write this exception to the log, including a full backtrace. */ public function logInfo() { SimpleSAML\Logger::info($this->getClass() . ': ' . $this->getMessage()); $this->logBacktrace(\SimpleSAML\Logger::INFO); }
/** * Validate certificate and login * * This function try to validate the certificate. * On success, the user is logged in without going through * o login page. * On failure, The authX509:X509error.php template is * loaded. * * @param array &$state Information about the current authentication. */ public function authenticate(&$state) { assert('is_array($state)'); $ldapcf = $this->ldapcf; if (!isset($_SERVER['SSL_CLIENT_CERT']) || $_SERVER['SSL_CLIENT_CERT'] == '') { $state['authX509.error'] = "NOCERT"; $this->authFailed($state); assert('false'); // NOTREACHED return; } $client_cert = $_SERVER['SSL_CLIENT_CERT']; $client_cert_data = openssl_x509_parse($client_cert); if ($client_cert_data == false) { SimpleSAML\Logger::error('authX509: invalid cert'); $state['authX509.error'] = "INVALIDCERT"; $this->authFailed($state); assert('false'); // NOTREACHED return; } $dn = null; foreach ($this->x509attributes as $x509_attr => $ldap_attr) { /* value is scalar */ if (array_key_exists($x509_attr, $client_cert_data['subject'])) { $value = $client_cert_data['subject'][$x509_attr]; SimpleSAML\Logger::info('authX509: cert ' . $x509_attr . ' = ' . $value); $dn = $ldapcf->searchfordn($ldap_attr, $value, true); if ($dn !== null) { break; } } } if ($dn === null) { SimpleSAML\Logger::error('authX509: cert has ' . 'no matching user in LDAP'); $state['authX509.error'] = "UNKNOWNCERT"; $this->authFailed($state); assert('false'); /* NOTREACHED */ return; } if ($this->ldapusercert === null) { // do not check for certificate match $attributes = $ldapcf->getAttributes($dn); assert('is_array($attributes)'); $state['Attributes'] = $attributes; $this->authSuccesful($state); assert('false'); /* NOTREACHED */ return; } $ldap_certs = $ldapcf->getAttributes($dn, $this->ldapusercert); if ($ldap_certs === false) { SimpleSAML\Logger::error('authX509: no certificate ' . 'found in LDAP for dn=' . $dn); $state['authX509.error'] = "UNKNOWNCERT"; $this->authFailed($state); assert('false'); /* NOTREACHED */ return; } $merged_ldapcerts = array(); foreach ($this->ldapusercert as $attr) { $merged_ldapcerts = array_merge($merged_ldapcerts, $ldap_certs[$attr]); } $ldap_certs = $merged_ldapcerts; foreach ($ldap_certs as $ldap_cert) { $pem = $this->der2pem($ldap_cert); $ldap_cert_data = openssl_x509_parse($pem); if ($ldap_cert_data == false) { SimpleSAML\Logger::error('authX509: cert in ' . 'LDAP in invalid for ' . 'dn = ' . $dn); continue; } if ($ldap_cert_data === $client_cert_data) { $attributes = $ldapcf->getAttributes($dn); assert('is_array($attributes)'); $state['Attributes'] = $attributes; $this->authSuccesful($state); assert('false'); /* NOTREACHED */ return; } } SimpleSAML\Logger::error('authX509: no matching cert in ' . 'LDAP for dn = ' . $dn); $state['authX509.error'] = "UNKNOWNCERT"; $this->authFailed($state); assert('false'); /* NOTREACHED */ return; }
<?php /** * The SSOService is part of the SAML 2.0 IdP code, and it receives incoming Authentication Requests * from a SAML 2.0 SP, parses, and process it, and then authenticates the user and sends the user back * to the SP with an Authentication Response. * * @author Andreas Åkre Solberg, UNINETT AS. <*****@*****.**> * @package SimpleSAMLphp */ require_once '../../_include.php'; SimpleSAML\Logger::info('SAML2.0 - IdP.SSOService: Accessing SAML 2.0 IdP endpoint SSOService'); $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); $idpEntityId = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); $idp = SimpleSAML_IdP::getById('saml2:' . $idpEntityId); try { sspmod_saml_IdP_SAML2::receiveAuthnRequest($idp); } catch (Exception $e) { if ($e->getMessage() === "Unable to find the current binding.") { throw new SimpleSAML_Error_Error('SSOPARAMS', $e, 400); } else { throw $e; // do not ignore other exceptions! } } assert('FALSE');
/** * Process this filter * * Logic is largely the same as (and lifted from) sqlauth:sql * @param mixed &$request * @throws SimpleSAML_Error_Exception */ public function process(&$request) { assert('is_array($request)'); assert('array_key_exists("Attributes", $request)'); assert('array_key_exists("entityid", $request["Destination"])'); $attributes =& $request['Attributes']; if (!array_key_exists($this->attribute, $attributes)) { SimpleSAML\Logger::info('AttributeFromSQL: attribute \'' . $this->attribute . '\' not set, declining'); return; } $db = $this->connect(); try { $sth = $db->prepare('SELECT attribute,value FROM ' . $this->table . ' WHERE uid=? AND (sp=\'%\' OR sp=?);'); } catch (PDOException $e) { throw new SimpleSAML_Error_Exception('AttributeFromSQL: prepare() failed: ' . $e->getMessage()); } try { $res = $sth->execute(array($attributes[$this->attribute][0], $request["Destination"]["entityid"])); } catch (PDOException $e) { throw new SimpleSAML_Error_Exception('AttributeFromSQL: execute(' . $attributes[$this->attribute][0] . ', ' . $request["Destination"]["entityid"] . ') failed: ' . $e->getMessage()); } try { $data = $sth->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { throw new SimpleSAML_Error_Exception('AttributeFromSQL: fetchAll() failed: ' . $e->getMessage()); } if (count($data) === 0) { SimpleSAML\Logger::info('AttributeFromSQL: no additional attributes for ' . $this->attribute . '=\'' . $attributes[$this->attribute][0] . '\''); return; } /* Extract attributes from the SQL datasource, and then merge them into * the existing attribute set. If $replace is set, overwrite any existing * attribute of the same name; otherwise add it as a multi-valued attribute */ foreach ($data as $row) { if (empty($row['attribute']) || $row['value'] === null) { SimpleSAML\Logger::debug('AttributeFromSQL: skipping invalid attribute/value tuple: ' . var_export($row, true)); continue; } $name = (string) $row['attribute']; $value = (string) $row['value']; /* Limit the attribute set returned */ if ($this->limit !== null && !in_array($name, $this->limit, true)) { SimpleSAML\Logger::notice('AttributeFromSQL: skipping unwanted attribute ' . $name . ' [limited to: ' . var_export($this->limit, true) . ']'); continue; } if (!array_key_exists($name, $attributes) || $this->replace === true) { $attributes[$name] = array(); } if (in_array($value, $attributes[$name], true)) { /* Value already exists in attribute. */ SimpleSAML\Logger::debug('AttributeFromSQL: skipping duplicate attribute/value tuple ' . $name . '=\'' . $value . '\''); continue; } $attributes[$name][] = $value; } }
/** * Hook to run a cron job. * * @param array &$croninfo Output */ function metarefresh_hook_cron(&$croninfo) { assert('is_array($croninfo)'); assert('array_key_exists("summary", $croninfo)'); assert('array_key_exists("tag", $croninfo)'); SimpleSAML\Logger::info('cron [metarefresh]: Running cron in cron tag [' . $croninfo['tag'] . '] '); try { $config = SimpleSAML_Configuration::getInstance(); $mconfig = SimpleSAML_Configuration::getOptionalConfig('config-metarefresh.php'); $sets = $mconfig->getConfigList('sets', array()); $stateFile = $config->getPathValue('datadir', 'data/') . 'metarefresh-state.php'; foreach ($sets as $setkey => $set) { // Only process sets where cron matches the current cron tag $cronTags = $set->getArray('cron'); if (!in_array($croninfo['tag'], $cronTags)) { continue; } SimpleSAML\Logger::info('cron [metarefresh]: Executing set [' . $setkey . ']'); $expireAfter = $set->getInteger('expireAfter', NULL); if ($expireAfter !== NULL) { $expire = time() + $expireAfter; } else { $expire = NULL; } $outputDir = $set->getString('outputDir'); $outputDir = $config->resolvePath($outputDir); $outputFormat = $set->getValueValidate('outputFormat', array('flatfile', 'serialize'), 'flatfile'); $oldMetadataSrc = SimpleSAML_Metadata_MetaDataStorageSource::getSource(array('type' => $outputFormat, 'directory' => $outputDir)); $metaloader = new sspmod_metarefresh_MetaLoader($expire, $stateFile, $oldMetadataSrc); # Get global blacklist, whitelist and caching info $blacklist = $mconfig->getArray('blacklist', array()); $whitelist = $mconfig->getArray('whitelist', array()); $conditionalGET = $mconfig->getBoolean('conditionalGET', FALSE); // get global type filters $available_types = array('saml20-idp-remote', 'saml20-sp-remote', 'shib13-idp-remote', 'shib13-sp-remote', 'attributeauthority-remote'); $set_types = $set->getArrayize('types', $available_types); foreach ($set->getArray('sources') as $source) { // filter metadata by type of entity if (isset($source['types'])) { $metaloader->setTypes($source['types']); } else { $metaloader->setTypes($set_types); } # Merge global and src specific blacklists if (isset($source['blacklist'])) { $source['blacklist'] = array_unique(array_merge($source['blacklist'], $blacklist)); } else { $source['blacklist'] = $blacklist; } # Merge global and src specific whitelists if (isset($source['whitelist'])) { $source['whitelist'] = array_unique(array_merge($source['whitelist'], $whitelist)); } else { $source['whitelist'] = $whitelist; } # Let src specific conditionalGET override global one if (!isset($source['conditionalGET'])) { $source['conditionalGET'] = $conditionalGET; } SimpleSAML\Logger::debug('cron [metarefresh]: In set [' . $setkey . '] loading source [' . $source['src'] . ']'); $metaloader->loadSource($source); } // Write state information back to disk $metaloader->writeState(); switch ($outputFormat) { case 'flatfile': $metaloader->writeMetadataFiles($outputDir); break; case 'serialize': $metaloader->writeMetadataSerialize($outputDir); break; } if ($set->hasValue('arp')) { $arpconfig = SimpleSAML_Configuration::loadFromArray($set->getValue('arp')); $metaloader->writeARPfile($arpconfig); } } } catch (Exception $e) { $croninfo['summary'][] = 'Error during metarefresh: ' . $e->getMessage(); } }
<?php /** * about2expire.php * * @package SimpleSAMLphp */ SimpleSAML\Logger::info('expirycheck - User has been warned that NetID is near to expirational date.'); if (!array_key_exists('StateId', $_REQUEST)) { throw new SimpleSAML_Error_BadRequest('Missing required StateId query parameter.'); } $state = SimpleSAML_Auth_State::loadState($_REQUEST['StateId'], 'expirywarning:expired'); $globalConfig = SimpleSAML_Configuration::getInstance(); $t = new SimpleSAML_XHTML_Template($globalConfig, 'expirycheck:expired.php'); $t->data['expireOnDate'] = $state['expireOnDate']; $t->data['netId'] = $state['netId']; $t->show();
/** * This function writes the metadata to to separate files in the output directory. */ function writeMetadataFiles($outputDir) { while (strlen($outputDir) > 0 && $outputDir[strlen($outputDir) - 1] === '/') { $outputDir = substr($outputDir, 0, strlen($outputDir) - 1); } if (!file_exists($outputDir)) { SimpleSAML\Logger::info('Creating directory: ' . $outputDir . "\n"); $res = @mkdir($outputDir, 0777, TRUE); if ($res === FALSE) { throw new Exception('Error creating directory: ' . $outputDir); } } foreach ($this->types as $type) { $filename = $outputDir . '/' . $type . '.php'; if (array_key_exists($type, $this->metadata)) { $elements = $this->metadata[$type]; SimpleSAML\Logger::debug('Writing: ' . $filename); $content = '<?php' . "\n" . '/* This file was generated by the metarefresh module at ' . $this->getTime() . "\n"; $content .= ' Do not update it manually as it will get overwritten' . "\n" . '*/' . "\n"; foreach ($elements as $m) { $entityID = $m['metadata']['entityid']; $content .= "\n"; $content .= '$metadata[\'' . addslashes($entityID) . '\'] = ' . var_export($m['metadata'], TRUE) . ';' . "\n"; } $content .= "\n" . '?>'; SimpleSAML\Utils\System::writeFile($filename, $content, 0644); } elseif (is_file($filename)) { if (unlink($filename)) { SimpleSAML\Logger::debug('Deleting stale metadata file: ' . $filename); } else { SimpleSAML\Logger::warning('Could not delete stale metadata file: ' . $filename); } } } }
/** * Log out of the given sessions. * * @param string $authId The authsource ID. * @param array $nameId The NameID of the user. * @param array $sessionIndexes The SessionIndexes we should log out of. Logs out of all if this is empty. * @returns int|FALSE Number of sessions logged out, or FALSE if not supported. */ public static function logoutSessions($authId, array $nameId, array $sessionIndexes) { assert('is_string($authId)'); $store = SimpleSAML_Store::getInstance(); if ($store === FALSE) { /* We don't have a datastore. */ return FALSE; } /* Normalize NameID. */ ksort($nameId); $strNameId = serialize($nameId); $strNameId = sha1($strNameId); /* Normalize SessionIndexes. */ foreach ($sessionIndexes as &$sessionIndex) { assert('is_string($sessionIndex)'); if (strlen($sessionIndex) > 50) { $sessionIndex = sha1($sessionIndex); } } unset($sessionIndex); // Remove reference if ($store instanceof SimpleSAML_Store_SQL) { $sessions = self::getSessionsSQL($store, $authId, $strNameId); } elseif (empty($sessionIndexes)) { /* We cannot fetch all sessions without a SQL store. */ return FALSE; } else { /** @var SimpleSAML_Store $sessions At this point the store cannot be false */ $sessions = self::getSessionsStore($store, $authId, $strNameId, $sessionIndexes); } if (empty($sessionIndexes)) { $sessionIndexes = array_keys($sessions); } $sessionHandler = SimpleSAML_SessionHandler::getSessionHandler(); $numLoggedOut = 0; foreach ($sessionIndexes as $sessionIndex) { if (!isset($sessions[$sessionIndex])) { SimpleSAML\Logger::info('saml.LogoutStore: Logout requested for unknown SessionIndex.'); continue; } $sessionId = $sessions[$sessionIndex]; $session = SimpleSAML_Session::getSession($sessionId); if ($session === NULL) { SimpleSAML\Logger::info('saml.LogoutStore: Skipping logout of missing session.'); continue; } if (!$session->isValid($authId)) { SimpleSAML\Logger::info('saml.LogoutStore: Skipping logout of session because it isn\'t authenticated.'); continue; } SimpleSAML\Logger::info('saml.LogoutStore: Logging out of session with trackId [' . $session->getTrackID() . '].'); $session->doLogout($authId); $numLoggedOut += 1; } return $numLoggedOut; }
<?php /** * ADFS PRP IDP protocol support for SimpleSAMLphp. * * @author Hans Zandbelt, SURFnet bv, <*****@*****.**> * @package SimpleSAMLphp */ SimpleSAML\Logger::info('ADFS - IdP.prp: Accessing ADFS IdP endpoint prp'); $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); $idpEntityId = $metadata->getMetaDataCurrentEntityID('adfs-idp-hosted'); $idp = SimpleSAML_IdP::getById('adfs:' . $idpEntityId); if (isset($_GET['wa'])) { if ($_GET['wa'] === 'wsignout1.0') { sspmod_adfs_IdP_ADFS::receiveLogoutMessage($idp); } else { if ($_GET['wa'] === 'wsignin1.0') { sspmod_adfs_IdP_ADFS::receiveAuthnRequest($idp); } } assert('FALSE'); } elseif (isset($_GET['assocId'])) { // logout response from ADFS SP $assocId = $_GET['assocId']; // Association ID of the SP that sent the logout response $relayState = $_GET['relayState']; // Data that was sent in the logout request to the SP. Can be null $logoutError = NULL; /* NULL on success, or an instance of a SimpleSAML_Error_Exception on failure. */ $idp->handleLogoutResponse($assocId, $relayState, $logoutError); }
/** * Log a message. * * This is an helper function for logging messages. It will prefix the messages with our discovery service type. * * @param string $message The message which should be logged. */ protected function log($message) { SimpleSAML\Logger::info('PowerIdPDisco.' . $this->instance . ': ' . $message); }
/** * Return OAuthConsumer-instance that a given requestToken was issued to * @param $requestTokenKey * @return unknown_type */ public function lookup_consumer_by_requestToken($requestTokenKey) { SimpleSAML\Logger::info('OAuth lookup_consumer_by_requestToken(' . $requestTokenKey . ')'); if (!$this->store->exists('requesttorequest', $requestTokenKey, '')) { return NULL; } $request = $this->store->get('requesttorequest', $requestTokenKey, ''); $consumerKey = $request['value']['consumerKey']; if (!$consumerKey) { return NULL; } $consumer = $this->store->get('consumers', $consumerKey['value'], ''); return $consumer['value']; }
/** * Attempt to log in using the given username and password. * * On a successful login, this function should return the users attributes. On failure, * it should throw an exception. If the error was caused by the user entering the wrong * username or password, a SimpleSAML_Error_Error('WRONGUSERPASS') should be thrown. * * Note that both the username and the password are UTF-8 encoded. * * @param string $username The username the user wrote. * @param string $password The password the user wrote. * @return array Associative array with the users attributes. */ protected function login($otp) { assert('is_string($otp)'); require_once dirname(dirname(dirname(dirname(__FILE__)))) . '/libextinc/Yubico.php'; $attributes = array(); try { $yubi = new Auth_Yubico($this->yubi_id, $this->yubi_key); $auth = $yubi->verify($otp); $uid = self::getYubiKeyPrefix($otp); $attributes = array('uid' => array($uid)); } catch (Exception $e) { SimpleSAML\Logger::info('YubiKey:' . $this->authId . ': Validation error (otp ' . $otp . '), debug output: ' . $yubi->getLastResponse()); throw new SimpleSAML_Error_Error('WRONGUSERPASS', $e); } SimpleSAML\Logger::info('YubiKey:' . $this->authId . ': YubiKey otp ' . $otp . ' validated successfully: ' . $yubi->getLastResponse()); return $attributes; }
/** * Save a metadata entry. * * @param string $entityId The entityId of the metadata entry. * @param string $set The metadata set this metadata entry belongs to. * @param array $metadata The metadata. * * @return boolean True if successfully saved, false otherwise. */ public function saveMetadata($entityId, $set, $metadata) { assert('is_string($entityId)'); assert('is_string($set)'); assert('is_array($metadata)'); $filePath = $this->getMetadataPath($entityId, $set); $newPath = $filePath . '.new'; $dir = dirname($filePath); if (!is_dir($dir)) { SimpleSAML\Logger::info('Creating directory: ' . $dir); $res = @mkdir($dir, 0777, true); if ($res === false) { $error = error_get_last(); SimpleSAML\Logger::error('Failed to create directory ' . $dir . ': ' . $error['message']); return false; } } $data = serialize($metadata); SimpleSAML\Logger::debug('Writing: ' . $newPath); $res = file_put_contents($newPath, $data); if ($res === false) { $error = error_get_last(); SimpleSAML\Logger::error('Error saving file ' . $newPath . ': ' . $error['message']); return false; } $res = rename($newPath, $filePath); if ($res === false) { $error = error_get_last(); SimpleSAML\Logger::error('Error renaming ' . $newPath . ' to ' . $filePath . ': ' . $error['message']); return false; } return true; }
<?php /** * This script warns a user that his/her certificate is about to expire. * * @package SimpleSAMLphp */ SimpleSAML\Logger::info('AuthX509 - Showing expiry warning to user'); if (!array_key_exists('StateId', $_REQUEST)) { throw new SimpleSAML_Error_BadRequest('Missing required StateId query parameter.'); } $id = $_REQUEST['StateId']; $state = SimpleSAML_Auth_State::loadState($id, 'warning:expire'); if (array_key_exists('proceed', $_REQUEST)) { // The user has pressed the proceed-button SimpleSAML_Auth_ProcessingChain::resumeProcessing($state); } $globalConfig = SimpleSAML_Configuration::getInstance(); $t = new SimpleSAML_XHTML_Template($globalConfig, 'authX509:X509warning.php'); $t->data['target'] = SimpleSAML\Module::getModuleURL('authX509/expirywarning.php'); $t->data['data'] = array('StateId' => $id); $t->data['daysleft'] = $state['daysleft']; $t->data['renewurl'] = $state['renewurl']; $t->data['errorcodes'] = SimpleSAML\Error\Errorcodes::getAllErrorCodeMessages(); $t->show();
} $session = SimpleSAML_Session::getSessionFromRequest(); $prevAuth = $session->getAuthData($sourceId, 'saml:sp:prevAuth'); if ($prevAuth !== null && $prevAuth['id'] === $response->getId() && $prevAuth['issuer'] === $idp) { /* OK, it looks like this message has the same issuer * and ID as the SP session we already have active. We * therefore assume that the user has somehow triggered * a resend of the message. * In that case we may as well just redo the previous redirect * instead of displaying a confusing error message. */ SimpleSAML\Logger::info('Duplicate SAML 2 response detected - ignoring the response and redirecting the user to the correct page.'); if (isset($prevAuth['redirect'])) { \SimpleSAML\Utils\HTTP::redirectTrustedURL($prevAuth['redirect']); } SimpleSAML\Logger::info('No RelayState or ReturnURL available, cannot redirect.'); throw new SimpleSAML_Error_Exception('Duplicate assertion received.'); } $idpMetadata = array(); $stateId = $response->getInResponseTo(); if (!empty($stateId)) { // this is a response to a request we sent earlier $state = SimpleSAML_Auth_State::loadState($stateId, 'saml:sp:sso'); // check that the authentication source is correct assert('array_key_exists("saml:sp:AuthId", $state)'); if ($state['saml:sp:AuthId'] !== $sourceId) { throw new SimpleSAML_Error_Exception('The authentication source id in the URL does not match the authentication source which sent the request.'); } // check that the issuer is the one we are expecting assert('array_key_exists("ExpectedIssuer", $state)'); if ($state['ExpectedIssuer'] !== $idp) {
<?php /** * This SAML 2.0 endpoint can receive incoming LogoutRequests. It will also send LogoutResponses, * and LogoutRequests and also receive LogoutResponses. It is implemeting SLO at the SAML 2.0 IdP. * * @author Andreas Åkre Solberg, UNINETT AS. <*****@*****.**> * @package SimpleSAMLphp */ require_once '../../_include.php'; SimpleSAML\Logger::info('SAML2.0 - IdP.SingleLogoutService: Accessing SAML 2.0 IdP endpoint SingleLogoutService'); $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); $idpEntityId = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); $idp = SimpleSAML_IdP::getById('saml2:' . $idpEntityId); if (isset($_REQUEST['ReturnTo'])) { $idp->doLogoutRedirect(\SimpleSAML\Utils\HTTP::checkURLAllowed((string) $_REQUEST['ReturnTo'])); } else { try { sspmod_saml_IdP_SAML2::receiveLogoutMessage($idp); } catch (Exception $e) { // TODO: look for a specific exception /* * This is dirty. Instead of checking the message of the exception, \SAML2\Binding::getCurrentBinding() should * throw an specific exception when the binding is unknown, and we should capture that here */ if ($e->getMessage() === 'Unable to find the current binding.') { throw new SimpleSAML_Error_Error('SLOSERVICEPARAMS', $e, 400); } else { throw $e; // do not ignore other exceptions! }
<?php /** * The SSOService is part of the Shibboleth 1.3 IdP code, and it receives incoming Authentication Requests * from a Shibboleth 1.3 SP, parses, and process it, and then authenticates the user and sends the user back * to the SP with an Authentication Response. * * @author Andreas Åkre Solberg, UNINETT AS. <*****@*****.**> * @package SimpleSAMLphp */ require_once '../../_include.php'; SimpleSAML\Logger::info('Shib1.3 - IdP.SSOService: Accessing Shibboleth 1.3 IdP endpoint SSOService'); $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); $idpEntityId = $metadata->getMetaDataCurrentEntityID('shib13-idp-hosted'); $idp = SimpleSAML_IdP::getById('saml1:' . $idpEntityId); sspmod_saml_IdP_SAML1::receiveAuthnRequest($idp); assert('FALSE');