From 57acd8378e263f6abd41a427f01fdcada0391a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20P=C3=A9rez=20Crespo?= <jaime.perez@uninett.no> Date: Fri, 24 Jan 2014 16:36:54 +0000 Subject: [PATCH] Followup on previous commits. Use redirectUntrustedURL() as a shortcut, and let everything else make use of redirectTrustedURL(). Move the responsibility to check the input out of the library, to the places where URLs are grabbed from input parameters. git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@3332 44740490-163a-0410-bde0-09ae8108e29a --- config-templates/config.php | 21 +- docs/simplesamlphp-sp-migration.txt | 3 + lib/SimpleSAML/Auth/BWC.php | 6 +- lib/SimpleSAML/Auth/Default.php | 64 +++-- lib/SimpleSAML/Auth/ProcessingChain.php | 11 +- lib/SimpleSAML/Auth/Simple.php | 2 +- lib/SimpleSAML/Auth/State.php | 8 +- lib/SimpleSAML/IdP.php | 2 +- lib/SimpleSAML/IdP/LogoutTraditional.php | 6 + lib/SimpleSAML/Utilities.php | 244 ++++++++++-------- lib/SimpleSAML/XHTML/IdPDisco.php | 8 +- modules/InfoCard/lib/Auth/Source/ICAuth.php | 13 +- modules/aselect/www/credentials.php | 13 +- .../authYubiKey/lib/Auth/Source/YubiKey.php | 6 + modules/authfacebook/www/linkback.php | 7 + modules/authlinkedin/www/linkback.php | 6 + modules/authmyspace/www/linkback.php | 6 + modules/authorize/www/authorize_403.php | 7 + modules/authtwitter/www/linkback.php | 6 + modules/authwindowslive/www/linkback.php | 7 + modules/cas/www/linkback.php | 6 + modules/casserver/www/login.php | 4 +- modules/cdc/lib/Server.php | 2 +- modules/cdc/www/resume.php | 6 + modules/consent/www/getconsent.php | 7 + modules/consent/www/logout.php | 7 + modules/consent/www/noconsent.php | 7 + modules/core/lib/Auth/UserPassBase.php | 6 + modules/core/lib/Auth/UserPassOrgBase.php | 12 + modules/core/www/as_login.php | 6 +- modules/core/www/as_logout.php | 2 +- modules/core/www/bwc_resumeauth.php | 2 +- modules/core/www/cleardiscochoices.php | 4 +- modules/core/www/idp/logout-iframe-done.php | 6 + modules/core/www/idp/logout-iframe.php | 6 + modules/core/www/idp/resumelogout.php | 6 + modules/core/www/login-admin.php | 3 +- modules/core/www/loginuserpass.php | 6 + modules/core/www/loginuserpassorg.php | 6 + modules/core/www/short_sso_interval.php | 7 + modules/discopower/lib/PowerIdPDisco.php | 4 +- .../exampleauth/lib/Auth/Source/External.php | 6 + modules/exampleauth/www/authpage.php | 12 +- modules/exampleauth/www/redirecttest.php | 7 + modules/expirycheck/www/about2expire.php | 7 + modules/expirycheck/www/expired.php | 7 + modules/multiauth/www/selectsource.php | 6 + modules/negotiate/www/backend.php | 7 + modules/negotiate/www/retry.php | 7 + modules/oauth/www/authorize.php | 2 +- modules/openid/www/consumer.php | 7 + modules/openid/www/linkback.php | 7 + modules/openidProvider/lib/Server.php | 6 + modules/papi/lib/Auth/Source/PAPI.php | 14 + modules/preprodwarning/www/showwarning.php | 7 + modules/saml/www/sp/discoresp.php | 10 +- modules/saml/www/sp/saml1-acs.php | 16 +- modules/saml/www/sp/saml2-acs.php | 9 +- modules/saml/www/sp/saml2-logout.php | 6 + www/auth/login-admin.php | 7 +- www/auth/login-cas-ldap.php | 15 +- www/auth/login-ldapmulti.php | 21 +- www/auth/login-radius.php | 21 +- www/auth/login-tlsclient.php | 15 +- www/auth/login-wayf-ldap.php | 4 +- www/saml2/idp/SingleLogoutService.php | 2 +- .../idp/idpInitSingleLogoutServiceiFrame.php | 2 +- www/saml2/idp/initSLO.php | 3 +- www/saml2/sp/AssertionConsumerService.php | 11 +- www/saml2/sp/SingleLogoutService.php | 4 +- www/saml2/sp/initSLO.php | 8 +- www/saml2/sp/initSSO.php | 7 +- www/shib13/sp/AssertionConsumerService.php | 9 +- www/shib13/sp/initSSO.php | 2 +- www/wsfed/sp/initSLO.php | 4 +- www/wsfed/sp/initSSO.php | 2 +- 76 files changed, 575 insertions(+), 266 deletions(-) diff --git a/config-templates/config.php b/config-templates/config.php index 66d1cf49e..989273329 100644 --- a/config-templates/config.php +++ b/config-templates/config.php @@ -701,13 +701,26 @@ $config = array ( 'proxy' => NULL, /* - * Array of URL's to allow a trusted redirect to. + * Array of domains that are allowed when generating links or redirections + * to URLs. simpleSAMLphp will use this option to determine whether to + * to consider a given URL valid or not, but you should always validate + * URLs obtained from the input on your own (i.e. ReturnTo or RelayState + * parameters obtained from the $_REQUEST array). * - * Set to NULL to disable. + * Set to NULL to disable checking of URLs. + * + * simpleSAMLphp will automatically add your own domain (either by checking + * it dinamically, or by using the domain defined in the 'baseurlpath' + * directive, the latter having precedence) to the list of trusted domains, + * in case this option is NOT set to NULL. In that case, you are explicitly + * telling simpleSAMLphp to verify URLs. + * + * Set to an empty array to disallow ALL redirections or links pointing to + * an external URL other than your own domain. * * Example: - * 'redirect.trustedsites' => array('sp.example.com', 'othersite.org'), + * 'trusted.url.domains' => array('sp.example.com', 'app.example.com'), */ - 'redirect.trustedsites' => NULL, + 'trusted.url.domains' => NULL, ); diff --git a/docs/simplesamlphp-sp-migration.txt b/docs/simplesamlphp-sp-migration.txt index 72a165e71..7b425e0e0 100644 --- a/docs/simplesamlphp-sp-migration.txt +++ b/docs/simplesamlphp-sp-migration.txt @@ -209,6 +209,9 @@ If you want to return to a specific URL after logging out, you should include th $as->logout('https://example.org/'); +Please make sure the URL is trusted. If you obtain the URL from the user input, make sure it is trusted before +calling $as->logout(), by using the SimpleSAML_Utilities::checkURLAllowed() method. + #### Login link diff --git a/lib/SimpleSAML/Auth/BWC.php b/lib/SimpleSAML/Auth/BWC.php index 290c514b5..923e9789a 100644 --- a/lib/SimpleSAML/Auth/BWC.php +++ b/lib/SimpleSAML/Auth/BWC.php @@ -143,19 +143,19 @@ class SimpleSAML_Auth_BWC extends SimpleSAML_Auth_Simple { $session = SimpleSAML_Session::getInstance(); if (!$session->isValid($this->authority)) { /* Not authenticated to this authentication source. */ - SimpleSAML_Utilities::redirectUntrustedURL($url); + SimpleSAML_Utilities::redirectTrustedURL($url); assert('FALSE'); } if ($this->authority === 'saml2') { $config = SimpleSAML_Configuration::getInstance(); - SimpleSAML_Utilities::redirectUntrustedURL('/' . $config->getBaseURL() . 'saml2/sp/initSLO.php', + SimpleSAML_Utilities::redirectTrustedURL('/' . $config->getBaseURL() . 'saml2/sp/initSLO.php', array('RelayState' => $url) ); } $session->doLogout($this->authority); - SimpleSAML_Utilities::redirectUntrustedURL($url); + SimpleSAML_Utilities::redirectTrustedURL($url); } } diff --git a/lib/SimpleSAML/Auth/Default.php b/lib/SimpleSAML/Auth/Default.php index 34686aa2d..ca02e748e 100644 --- a/lib/SimpleSAML/Auth/Default.php +++ b/lib/SimpleSAML/Auth/Default.php @@ -19,13 +19,21 @@ class SimpleSAML_Auth_Default { * This function never returns. * * @param string $authId The identifier of the authentication source. - * @param string|array $return The URL or function we should direct the user to after authentication. - * @param string|NULL $errorURL The URL we should direct the user to after failed authentication. - * Can be NULL, in which case a standard error page will be shown. - * @param array $params Extra information about the login. Different authentication requestors may - * provide different information. Optional, will default to an empty array. + * @param string|array $return The URL or function we should direct the + * user to after authentication. If using a URL obtained from user input, + * please make sure to check it by calling + * SimpleSAML_Utilities::checkURLAllowed(). + * @param string|NULL $errorURL The URL we should direct the user to after + * failed authentication. Can be NULL, in which case a standard error page + * will be shown. If using a URL obtained from user input, please make sure + * to check it by calling SimpleSAML_Utilities::checkURLAllowed(). + * @param array $params Extra information about the login. Different + * authentication requestors may provide different information. Optional, + * will default to an empty array. */ - public static function initLogin($authId, $return, $errorURL = NULL, array $params = array()) { + public static function initLogin($authId, $return, $errorURL = NULL, + array $params = array()) { + assert('is_string($authId)'); assert('is_string($return) || is_array($return)'); assert('is_string($errorURL) || is_null($errorURL)'); @@ -121,7 +129,7 @@ class SimpleSAML_Auth_Default { if (is_string($return)) { /* Redirect... */ - SimpleSAML_Utilities::redirectUntrustedURL($return); + SimpleSAML_Utilities::redirectTrustedURL($return); } else { call_user_func($return, $state); assert('FALSE'); @@ -132,11 +140,16 @@ class SimpleSAML_Auth_Default { /** * Start logout. * - * This function starts a logout operation from the current authentication source. This function - * will return if the logout operation does not require a redirect. + * This function starts a logout operation from the current authentication + * source. This function will return if the logout operation does not + * require a redirect. * - * @param string $returnURL The URL we should redirect the user to after logging out. - * @param string|NULL $authority The authentication source we are logging out from, or NULL to log out of the most recent. + * @param string $returnURL The URL we should redirect the user to after + * logging out. No checking is performed on the URL, so make sure to verify + * it on beforehand if the URL is obtained from user input. Refer to + * SimpleSAML_Utilities::checkURLAllowed() for more information. + * @param string|NULL $authority The authentication source we are logging + * out from, or NULL to log out from the most recent. */ public static function initLogoutReturn($returnURL, $authority = NULL) { assert('is_string($returnURL)'); @@ -171,11 +184,16 @@ class SimpleSAML_Auth_Default { /** * Start logout. * - * This function starts a logout operation from the current authentication source. This function - * never returns. + * This function starts a logout operation from the current authentication + * source. This function never returns. * - * @param string $returnURL The URL we should redirect the user to after logging out. - * @param string|NULL $authority The authentication source we are logging out from, or NULL to log out of the most recent. + * @param string $returnURL The URL we should redirect the user to after + * logging out. No checking is performed on the URL, so make sure to verify + * it on beforehand if the URL is obtained from user input. Refer to + * SimpleSAML_Utilities::checkURLAllowed() for more information. + * @param string|NULL $authority The authentication source we are logging + * out from, or NULL to log out from the most recent. + * @return void This function never returns. */ public static function initLogout($returnURL, $authority = NULL) { assert('is_string($returnURL)'); @@ -184,7 +202,7 @@ class SimpleSAML_Auth_Default { self::initLogoutReturn($returnURL, $authority); /* Redirect... */ - SimpleSAML_Utilities::redirectUntrustedURL($returnURL); + SimpleSAML_Utilities::redirectTrustedURL($returnURL); } @@ -202,7 +220,7 @@ class SimpleSAML_Auth_Default { $returnURL = $state['SimpleSAML_Auth_Default.ReturnURL']; /* Redirect... */ - SimpleSAML_Utilities::redirectUntrustedURL($returnURL); + SimpleSAML_Utilities::redirectTrustedURL($returnURL); } @@ -239,10 +257,14 @@ class SimpleSAML_Auth_Default { * * This is used to handle IdP initiated SSO. * - * @param string $authId The id of the authentication source that received the request. - * @param array $state A state array. - * @param string $redirectTo The URL we should redirect the user to after - * updating the session. + * @param string $authId The id of the authentication source that received + * the request. + * @param array $state A state array. + * @param string $redirectTo The URL we should redirect the user to after + * updating the session. The function will check if the URL is allowed, so + * there is no need to manually check the URL on beforehand. Please refer + * to the 'trusted.url.domains' configuration directive for more + * information about allowing (or disallowing) URLs. */ public static function handleUnsolicitedAuth($authId, array $state, $redirectTo) { assert('is_string($authId)'); diff --git a/lib/SimpleSAML/Auth/ProcessingChain.php b/lib/SimpleSAML/Auth/ProcessingChain.php index da0453264..1b60e3326 100644 --- a/lib/SimpleSAML/Auth/ProcessingChain.php +++ b/lib/SimpleSAML/Auth/ProcessingChain.php @@ -248,7 +248,7 @@ class SimpleSAML_Auth_ProcessingChain { * in $state['ReturnURL']. */ $id = SimpleSAML_Auth_State::saveState($state, self::COMPLETED_STAGE); - SimpleSAML_Utilities::redirectUntrustedURL($state['ReturnURL'], array(self::AUTHPARAM => $id)); + SimpleSAML_Utilities::redirectTrustedURL($state['ReturnURL'], array(self::AUTHPARAM => $id)); } else { /* Pass the state to the function defined in $state['ReturnCall']. */ @@ -302,8 +302,11 @@ class SimpleSAML_Auth_ProcessingChain { /** * Retrieve a state which has finished processing. * - * @param string $id The identifier of the state. This can be found in the request parameter - * with index from SimpleSAML_Auth_ProcessingChain::AUTHPARAM. + * @param string $id The state identifier. This can be found in the + * SimpleSAML_Auth_ProcessingChain::AUTHPARAM request parameter. Please + * make sure to sanitize it properly by calling the + * SimpleSAML_Utilities::checkURLAllowed() function with the embedded + * restart URL, if any. See also SimpleSAML_Utilities::getURLFromStateID(). */ public static function fetchProcessedState($id) { assert('is_string($id)'); @@ -351,5 +354,3 @@ class SimpleSAML_Auth_ProcessingChain { } } - -?> \ No newline at end of file diff --git a/lib/SimpleSAML/Auth/Simple.php b/lib/SimpleSAML/Auth/Simple.php index 8577379e8..4f7d8fb05 100644 --- a/lib/SimpleSAML/Auth/Simple.php +++ b/lib/SimpleSAML/Auth/Simple.php @@ -219,7 +219,7 @@ class SimpleSAML_Auth_Simple { $params[$state['ReturnStateParam']] = $stateID; } - SimpleSAML_Utilities::redirectUntrustedURL($state['ReturnTo'], $params); + SimpleSAML_Utilities::redirectTrustedURL($state['ReturnTo'], $params); } } diff --git a/lib/SimpleSAML/Auth/State.php b/lib/SimpleSAML/Auth/State.php index 57387665d..0b74da24c 100644 --- a/lib/SimpleSAML/Auth/State.php +++ b/lib/SimpleSAML/Auth/State.php @@ -211,13 +211,7 @@ class SimpleSAML_Auth_State { assert('is_bool($allowMissing)'); SimpleSAML_Logger::debug('Loading state: ' . var_export($id, TRUE)); - $tmp = explode(':', $id, 2); - $id = $tmp[0]; - if (count($tmp) === 2) { - $restartURL = $tmp[1]; - } else { - $restartURL = NULL; - } + $restartURL = SimpleSAML_Utilities::getURLFromStateID($id); $session = SimpleSAML_Session::getInstance(); $state = $session->getData('SimpleSAML_Auth_State', $id); diff --git a/lib/SimpleSAML/IdP.php b/lib/SimpleSAML/IdP.php index 630662dd7..d94c4274a 100644 --- a/lib/SimpleSAML/IdP.php +++ b/lib/SimpleSAML/IdP.php @@ -529,7 +529,7 @@ class SimpleSAML_IdP { public static function finishLogoutRedirect(SimpleSAML_IdP $idp, array $state) { assert('isset($state["core:Logout:URL"])'); - SimpleSAML_Utilities::redirectUntrustedURL($state['core:Logout:URL']); + SimpleSAML_Utilities::redirectTrustedURL($state['core:Logout:URL']); assert('FALSE'); } diff --git a/lib/SimpleSAML/IdP/LogoutTraditional.php b/lib/SimpleSAML/IdP/LogoutTraditional.php index 86ce301e9..f9fa132f7 100644 --- a/lib/SimpleSAML/IdP/LogoutTraditional.php +++ b/lib/SimpleSAML/IdP/LogoutTraditional.php @@ -76,6 +76,12 @@ class SimpleSAML_IdP_LogoutTraditional extends SimpleSAML_IdP_LogoutHandler { throw new SimpleSAML_Error_Exception('RelayState lost during logout.'); } + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($relayState); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + $state = SimpleSAML_Auth_State::loadState($relayState, 'core:LogoutTraditional'); if ($error === NULL) { diff --git a/lib/SimpleSAML/Utilities.php b/lib/SimpleSAML/Utilities.php index 0b37bf839..cd7ef1f28 100644 --- a/lib/SimpleSAML/Utilities.php +++ b/lib/SimpleSAML/Utilities.php @@ -298,17 +298,80 @@ class SimpleSAML_Utilities { } + /** + * Check if a URL is valid and is in our list of allowed URLs. + * + * @param string $url The URL to check. + * @param array $trustedSites An optional white list of domains. If none + * specified, the 'trusted.url.domains' configuration directive will be + * used. + * @return string The normalized URL itself if it is allowed. + * @throws SimpleSAML_Error_Exception if the URL is malformed or is not + * allowed by configuration. + */ + public static function checkURLAllowed($url, array $trustedSites = NULL) { + $url = self::normalizeURL($url); + + // verify that the URL points to an http or https site + if (!preg_match('@^https?://@i', $url)) { + throw new SimpleSAML_Error_Exception('Invalid URL: '.$url); + } + + // get the white list of domains + if ($trustedSites === NULL) { + $trustedSites = SimpleSAML_Configuration::getInstance()->getArray('trusted.url.domains', NULL); + if ($trustedSites === NULL) { + $trustedSites = SimpleSAML_Configuration::getInstance()->getArray('redirect.trustedsites', NULL); + } + } + + // validates the URL's host is among those allowed + if ($trustedSites !== NULL) { + assert(is_array($trustedSites)); + preg_match('@^https?://([^/]+)@i', $url, $matches); + $hostname = $matches[1]; + + // add self host to the white list + $self_host = self::getSelfHost(); + $trustedSites[] = $self_host; + + /* Throw exception due to redirection to untrusted site */ + if (!in_array($hostname, $trustedSites)) { + throw new SimpleSAML_Error_Exception('URL not allowed: '.$url); + } + } + return $url; + } + + + /** + * Get a URL embedded in a StateID, in the form 'id:url'. + * + * @param string $stateId The state ID to use. + * @return string The embedded URL if found, NULL otherwise. + */ + public static function getURLFromStateID($stateId) { + $tmp = explode(':', $stateId, 2); + $id = $tmp[0]; + $url = NULL; + if (count($tmp) === 2) { + $url = $tmp[1]; + } + return $url; + } + + public static function checkDateConditions($start=NULL, $end=NULL) { $currentTime = time(); - if (! empty($start)) { + if (!empty($start)) { $startTime = SAML2_Utils::parseSAML2Time($start); /* Allow for a 10 minute difference in Time */ if (($startTime < 0) || (($startTime - 600) > $currentTime)) { return FALSE; } } - if (! empty($end)) { + if (!empty($end)) { $endTime = SAML2_Utils::parseSAML2Time($end); if (($endTime < 0) || ($endTime <= $currentTime)) { return FALSE; @@ -493,113 +556,20 @@ class SimpleSAML_Utilities { return true; } - - /** - * This function redirects the user to the specified address. - * - * This function will use the "HTTP 303 See Other" redirection if the - * current request used the POST method and the HTTP version is 1.1. - * Otherwise, a "HTTP 302 Found" redirection will be used. - * - * The fuction will also generate a simple web page with a clickable - * link to the target page. - * - * @param string $url The URL we should redirect to. This URL may include - * query parameters. If this URL is a relative URL (starting with '/'), - * then it will be turned into an absolute URL by prefixing it with the - * absolute URL to the root of the website. - * @param string[] $parameters An array with extra query string parameters - * which should be appended to the URL. The name of the parameter is the - * array index. The value of the parameter is the value stored in the index. - * Both the name and the value will be urlencoded. If the value is NULL, - * then the parameter will be encoded as just the name, without a value. - * @param string[] $allowed_redirect_hosts An array with a whitelist of - * hosts for which redirects are allowed. If NULL, redirections will be - * allowed to any host. Otherwise, the host of the $url provided must be - * present in this parameter. If the host is not whitelisted, an exception - * will be thrown. - * - * @return void This function never returns. - * @deprecated 1.12.0 This function will be removed from the API. Use - * accordingly the redirectTrustedURL or redirectUntrustedURL functions - * instead. + /* + * This is a temporary function, holding the redirect() functionality, + * meanwhile we are deprecating the it. */ - public static function redirect($url, $parameters = array(), $allowed_redirect_hosts = NULL) { - assert(is_string($url)); - assert(strlen($url) > 0); - assert(is_array($parameters)); - if($allowed_redirect_hosts != NULL) assert(is_array($allowed_redirect_hosts)); - - /* Check for relative URL. */ - if(substr($url, 0, 1) === '/') { - /* Prefix the URL with the url to the root of the - * website. - */ - $url = self::selfURLhost() . $url; + private static function _doRedirect($url, $parameters = array()) { + if (!empty($parameters)) { + $url = self::addURLparameter($url, $parameters); } - /* Verify that the URL points to an http or https site. */ - if (!preg_match('@^https?://@i', $url)) { - throw new SimpleSAML_Error_Exception('Redirect to invalid URL: ' . $url); - } - - /* Validates the URL's host is among those allowed. */ - if ($allowed_redirect_hosts !== NULL) { - preg_match('@^https?://([^/]+)@i', $url, $matches); - $hostname = $matches[1]; - - // add self host to the white list - $self_host = self::getSelfHost(); - $allowed_redirect_hosts[] = $self_host; - - /* Throw exception due to redirection to untrusted site */ - if(!in_array($hostname, $allowed_redirect_hosts)) { - throw new SimpleSAML_Error_Exception('Redirection not to allowed to URL: ' . $url); - } - } - - /* Determine which prefix we should put before the first - * parameter. - */ - if(strpos($url, '?') === FALSE) { - $paramPrefix = '?'; - } else { - $paramPrefix = '&'; - } - - /* Iterate over the parameters and append them to the query - * string. - */ - foreach($parameters as $name => $value) { - - /* Encode the parameter. */ - if($value === NULL) { - $param = urlencode($name); - } elseif (is_array($value)) { - $param = ""; - foreach ($value as $val) { - $param .= urlencode($name) . "[]=" . urlencode($val) . '&'; - } - } else { - $param = urlencode($name) . '=' . - urlencode($value); - } - - /* Append the parameter to the query string. */ - $url .= $paramPrefix . $param; - - /* Every following parameter is guaranteed to follow - * another parameter. Therefore we use the '&' prefix. - */ - $paramPrefix = '&'; - } - - /* Set the HTTP result code. This is either 303 See Other or * 302 Found. HTTP 303 See Other is sent if the HTTP version * is HTTP/1.1 and the request type was a POST request. */ - if($_SERVER['SERVER_PROTOCOL'] === 'HTTP/1.1' && + if ($_SERVER['SERVER_PROTOCOL'] === 'HTTP/1.1' && $_SERVER['REQUEST_METHOD'] === 'POST') { $code = 303; } else { @@ -630,7 +600,8 @@ class SimpleSAML_Utilities { echo '<h1>Redirect</h1>'; echo '<p>'; echo 'You were redirected to: '; - echo '<a id="redirlink" href="' . htmlspecialchars($url) . '">' . htmlspecialchars($url) . '</a>'; + echo '<a id="redirlink" href="' . + htmlspecialchars($url) . '">' . htmlspecialchars($url) . '</a>'; echo '<script type="text/javascript">document.getElementById("redirlink").focus();</script>'; echo '</p>'; echo '</body>'; @@ -638,6 +609,51 @@ class SimpleSAML_Utilities { /* End script execution. */ exit; + } + + + /** + * This function redirects the user to the specified address. + * + * This function will use the "HTTP 303 See Other" redirection if the + * current request used the POST method and the HTTP version is 1.1. + * Otherwise, a "HTTP 302 Found" redirection will be used. + * + * The fuction will also generate a simple web page with a clickable + * link to the target page. + * + * @param string $url The URL we should redirect to. This URL may include + * query parameters. If this URL is a relative URL (starting with '/'), + * then it will be turned into an absolute URL by prefixing it with the + * absolute URL to the root of the website. + * @param string[] $parameters An array with extra query string parameters + * which should be appended to the URL. The name of the parameter is the + * array index. The value of the parameter is the value stored in the index. + * Both the name and the value will be urlencoded. If the value is NULL, + * then the parameter will be encoded as just the name, without a value. + * @param string[] $allowed_redirect_hosts An array with a whitelist of + * hosts for which redirects are allowed. If NULL, redirections will be + * allowed to any host. Otherwise, the host of the $url provided must be + * present in this parameter. If the host is not whitelisted, an exception + * will be thrown. + * + * @return void This function never returns. + * @deprecated 1.12.0 This function will be removed from the API. Instead, + * use the redirectTrustedURL or redirectUntrustedURL functions + * accordingly. + */ + public static function redirect($url, $parameters = array(), + $allowed_redirect_hosts = NULL) { + + assert(is_string($url)); + assert(strlen($url) > 0); + assert(is_array($parameters)); + + $url = self::normalizeURL($url); + if ($allowed_redirect_hosts !== NULL) { + $url = self::checkURLAllowed($url, $allowed_redirect_hosts); + } + self::_doRedirect($url, $parameters); } /** @@ -665,26 +681,27 @@ class SimpleSAML_Utilities { * @return void This function never returns. */ public static function redirectTrustedURL($url, $parameters = array()) { - self::redirect($url, $parameters); + $url = self::normalizeURL($url); + self::_doRedirect($url, $parameters); } /** * This function redirects to the specified URL after performing the - * appropriate security checks on it. Particularly, it will make sure - * that the provided URL is allowed by the 'redirect.trustedsites' - * directive in the configuration. + * appropriate security checks on it. Particularly, it will make sure that + * the provided URL is allowed by the 'redirect.trustedsites' directive + * in the configuration. * - * If the aforementioned option is not set or the URL does corresponds - * to a trusted site, it performs a redirection to it. If the site is - * not trusted, an exception will be thrown. + * If the aforementioned option is not set or the URL does correspond to a + * trusted site, it performs a redirection to it. If the site is not + * trusted, an exception will be thrown. * * See the redirectTrustedURL function for more details. * * @return void This function never returns. */ public static function redirectUntrustedURL($url, $parameters = array()) { - $trustedSites = SimpleSAML_Configuration::getInstance()->getArray('redirect.trustedsites', NULL); - self::redirect($url, $parameters, $trustedSites); + $url = self::checkURLAllowed($url); + self::_doRedirect($url, $parameters); } /** @@ -1150,8 +1167,7 @@ class SimpleSAML_Utilities { $base = self::getBaseURL(); } - - if(!preg_match('$^((((\w+:)//[^/]+)(/[^?#]*))(?:\?[^#]*)?)(?:#.*)?$', $base, $baseParsed)) { + if(!preg_match('/^((((\w+:)\/\/[^\/]+)(\/[^?#]*))(?:\?[^#]*)?)(?:#.*)?/', $base, $baseParsed)) { throw new Exception('Unable to parse base url: ' . $base); } diff --git a/lib/SimpleSAML/XHTML/IdPDisco.php b/lib/SimpleSAML/XHTML/IdPDisco.php index c1c5be0c4..7e7eeb938 100644 --- a/lib/SimpleSAML/XHTML/IdPDisco.php +++ b/lib/SimpleSAML/XHTML/IdPDisco.php @@ -124,7 +124,7 @@ class SimpleSAML_XHTML_IdPDisco { if(!array_key_exists('return', $_GET)) { throw new Exception('Missing parameter: return'); } else { - $this->returnURL = $_GET['return']; + $this->returnURL = SimpleSAML_Utilities::checkURLAllowed($_GET['return']); } $this->isPassive = FALSE; @@ -474,7 +474,7 @@ class SimpleSAML_XHTML_IdPDisco { } else { $this->log('Choice made [' . $idp . '] (Redirecting the user back. returnIDParam=' . $this->returnIdParam . ')'); - SimpleSAML_Utilities::redirectUntrustedURL($this->returnURL, array($this->returnIdParam => $idp)); + SimpleSAML_Utilities::redirectTrustedURL($this->returnURL, array($this->returnIdParam => $idp)); } return; @@ -482,7 +482,7 @@ class SimpleSAML_XHTML_IdPDisco { if ($this->isPassive) { $this->log('Choice not made. (Redirecting the user back without answer)'); - SimpleSAML_Utilities::redirectUntrustedURL($this->returnURL); + SimpleSAML_Utilities::redirectTrustedURL($this->returnURL); return; } @@ -500,7 +500,7 @@ class SimpleSAML_XHTML_IdPDisco { if(sizeof($idpintersection) == 1) { $this->log('Choice made [' . $idpintersection[0] . '] (Redirecting the user back. returnIDParam=' . $this->returnIdParam . ')'); - SimpleSAML_Utilities::redirectUntrustedURL($this->returnURL, array($this->returnIdParam => $idpintersection[0])); + SimpleSAML_Utilities::redirectTrustedURL($this->returnURL, array($this->returnIdParam => $idpintersection[0])); } /* diff --git a/modules/InfoCard/lib/Auth/Source/ICAuth.php b/modules/InfoCard/lib/Auth/Source/ICAuth.php index 39e746c56..0b76ed1c0 100644 --- a/modules/InfoCard/lib/Auth/Source/ICAuth.php +++ b/modules/InfoCard/lib/Auth/Source/ICAuth.php @@ -42,7 +42,6 @@ class sspmod_InfoCard_Auth_Source_ICAuth extends SimpleSAML_Auth_Source { public static function handleLogin($authStateId, $xmlToken) { -SimpleSAML_Logger::debug('ENTRA en icauth'); assert('is_string($authStateId)'); $config = SimpleSAML_Configuration::getInstance(); @@ -61,14 +60,20 @@ SimpleSAML_Logger::debug('ENTRA en icauth'); SimpleSAML_Logger::debug("NOXMLtoken: ".$xmlToken); $claims = $infocard->process($xmlToken); if($claims->isValid()) { -// if(false) { $attributes = array(); foreach ($Infocard['requiredClaims'] as $claim => $data){ $attributes[$claim] = array($claims->$claim); } foreach ($Infocard['optionalClaims'] as $claim => $data){ $attributes[$claim] = array($claims->$claim); - } + } + + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($authStateId); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + /* Retrieve the authentication state. */ $state = SimpleSAML_Auth_State::loadState($authStateId, self::STAGEID); /* Find authentication source. */ @@ -78,12 +83,10 @@ SimpleSAML_Logger::debug('ENTRA en icauth'); throw new Exception('Could not find authentication source with id ' . $state[self::AUTHID]); } $state['Attributes'] = $attributes; -SimpleSAML_Logger::debug('VALIDA'); unset($infocard); unset($claims); SimpleSAML_Auth_Source::completeAuth($state); } else { -SimpleSAML_Logger::debug('NO VALIDA ERROR:'.$claims->getErrorMsg()); unset($infocard); unset($claims); return 'wrong_IC'; diff --git a/modules/aselect/www/credentials.php b/modules/aselect/www/credentials.php index 3d3b8cba1..dc0afe7e4 100644 --- a/modules/aselect/www/credentials.php +++ b/modules/aselect/www/credentials.php @@ -7,7 +7,18 @@ * @author Wessel Dankers, Tilburg University */ function check_credentials() { - $state = SimpleSAML_Auth_State::loadState($_REQUEST['ssp_state'], 'aselect:login'); + + if (!array_key_exists('ssp_state', $_REQUEST)) + SimpleSAML_Auth_State::throwException($state, new SimpleSAML_Error_Exception("Missing ssp_state parameter")); + $id = $_REQUEST['ssp_state']; + + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($id); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + + $state = SimpleSAML_Auth_State::loadState($id, 'aselect:login'); if(!array_key_exists('a-select-server', $_REQUEST)) SimpleSAML_Auth_State::throwException($state, new SimpleSAML_Error_Exception("Missing a-select-server parameter")); diff --git a/modules/authYubiKey/lib/Auth/Source/YubiKey.php b/modules/authYubiKey/lib/Auth/Source/YubiKey.php index ae98920d6..6b1412897 100644 --- a/modules/authYubiKey/lib/Auth/Source/YubiKey.php +++ b/modules/authYubiKey/lib/Auth/Source/YubiKey.php @@ -124,6 +124,12 @@ class sspmod_authYubiKey_Auth_Source_YubiKey extends SimpleSAML_Auth_Source { assert('is_string($authStateId)'); assert('is_string($otp)'); + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($authStateId); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + /* Retrieve the authentication state. */ $state = SimpleSAML_Auth_State::loadState($authStateId, self::STAGEID); diff --git a/modules/authfacebook/www/linkback.php b/modules/authfacebook/www/linkback.php index 3a27fe5ea..2305f773d 100644 --- a/modules/authfacebook/www/linkback.php +++ b/modules/authfacebook/www/linkback.php @@ -9,6 +9,13 @@ if (!array_key_exists('AuthState', $_REQUEST) || empty($_REQUEST['AuthState'])) } $stateID = $_REQUEST['AuthState']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($stateID); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($stateID, sspmod_authfacebook_Auth_Source_Facebook::STAGE_INIT); /* Find authentication source. */ diff --git a/modules/authlinkedin/www/linkback.php b/modules/authlinkedin/www/linkback.php index 961eaa6bf..a169f04ee 100644 --- a/modules/authlinkedin/www/linkback.php +++ b/modules/authlinkedin/www/linkback.php @@ -10,6 +10,12 @@ if (array_key_exists('stateid', $_REQUEST)) { throw new Exception('Lost OAuth Client State'); } +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($stateId); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($stateId, sspmod_authlinkedin_Auth_Source_LinkedIn::STAGE_INIT); // http://developer.linkedin.com/docs/DOC-1008#2_Redirect_the_User_to_our_Authorization_Server diff --git a/modules/authmyspace/www/linkback.php b/modules/authmyspace/www/linkback.php index 93c45153c..81683c837 100644 --- a/modules/authmyspace/www/linkback.php +++ b/modules/authmyspace/www/linkback.php @@ -10,6 +10,12 @@ if (array_key_exists('stateid', $_REQUEST)) { throw new Exception('State Lost - not returned by MySpace Auth'); } +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($stateId); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($stateId, sspmod_authmyspace_Auth_Source_MySpace::STAGE_INIT); if (array_key_exists('oauth_problem', $_REQUEST)) { diff --git a/modules/authorize/www/authorize_403.php b/modules/authorize/www/authorize_403.php index 54d702fb0..613fa1034 100644 --- a/modules/authorize/www/authorize_403.php +++ b/modules/authorize/www/authorize_403.php @@ -11,6 +11,13 @@ if (!array_key_exists('StateId', $_REQUEST)) { } $id = $_REQUEST['StateId']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($id); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($id, 'authorize:Authorize'); $globalConfig = SimpleSAML_Configuration::getInstance(); diff --git a/modules/authtwitter/www/linkback.php b/modules/authtwitter/www/linkback.php index de6ec85ce..0ebea758a 100644 --- a/modules/authtwitter/www/linkback.php +++ b/modules/authtwitter/www/linkback.php @@ -9,6 +9,12 @@ if (!array_key_exists('AuthState', $_REQUEST) || empty($_REQUEST['AuthState'])) } $stateID = $_REQUEST['AuthState']; +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($stateID); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($stateID, sspmod_authtwitter_Auth_Source_Twitter::STAGE_INIT); /* Find authentication source. */ diff --git a/modules/authwindowslive/www/linkback.php b/modules/authwindowslive/www/linkback.php index 7ae42661f..ee8452de4 100644 --- a/modules/authwindowslive/www/linkback.php +++ b/modules/authwindowslive/www/linkback.php @@ -6,6 +6,13 @@ if (array_key_exists('wrap_client_state', $_REQUEST)) { $stateId = $_REQUEST['wrap_client_state']; + + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($stateId); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + $state = SimpleSAML_Auth_State::loadState($stateId, sspmod_authwindowslive_Auth_Source_LiveID::STAGE_INIT); } else { throw new Exception('Lost OAuth-WRAP Client State'); diff --git a/modules/cas/www/linkback.php b/modules/cas/www/linkback.php index 1e6740c0b..8fe43e2f5 100644 --- a/modules/cas/www/linkback.php +++ b/modules/cas/www/linkback.php @@ -13,6 +13,12 @@ if (!isset($_GET['ticket'])) { throw new SimpleSAML_Error_BadRequest('Missing ticket parameter.'); } +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($stateId); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($stateId, sspmod_cas_Auth_Source_CAS::STAGE_INIT); $state['cas:ticket'] = (string)$_GET['ticket']; diff --git a/modules/casserver/www/login.php b/modules/casserver/www/login.php index e59c33248..0410acdc3 100644 --- a/modules/casserver/www/login.php +++ b/modules/casserver/www/login.php @@ -48,10 +48,8 @@ storeTicket($ticket, $path, array('service' => $service, 'proxies' => array(), 'validbefore' => time() + 5)); -SimpleSAML_Utilities::redirectUntrustedURL( +SimpleSAML_Utilities::redirectTrustedURL( SimpleSAML_Utilities::addURLparameter($service, array('ticket' => $ticket) ) ); - -?> diff --git a/modules/cdc/lib/Server.php b/modules/cdc/lib/Server.php index 8c962eb19..d4dacf736 100644 --- a/modules/cdc/lib/Server.php +++ b/modules/cdc/lib/Server.php @@ -327,7 +327,7 @@ class sspmod_cdc_Server { $url = SimpleSAML_Utilities::addURLparameter($to, $params); if (strlen($url) < 2048) { - SimpleSAML_Utilities::redirectUntrustedURL($url); + SimpleSAML_Utilities::redirectTrustedURL($url); } else { SimpleSAML_Utilities::postRedirect($to, $params); } diff --git a/modules/cdc/www/resume.php b/modules/cdc/www/resume.php index 6e4fcf381..549be2288 100644 --- a/modules/cdc/www/resume.php +++ b/modules/cdc/www/resume.php @@ -17,6 +17,12 @@ if (!isset($response['id'])) { throw new SimpleSAML_Error_BadRequest('CDCResponse without id.'); } +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($response['id']); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($response['id'], 'cdc:resume'); SimpleSAML_Auth_ProcessingChain::resumeProcessing($state); diff --git a/modules/consent/www/getconsent.php b/modules/consent/www/getconsent.php index 3628bd1ab..3e3081702 100644 --- a/modules/consent/www/getconsent.php +++ b/modules/consent/www/getconsent.php @@ -31,6 +31,13 @@ if (!array_key_exists('StateId', $_REQUEST)) { } $id = $_REQUEST['StateId']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($id); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($id, 'consent:request'); if (array_key_exists('core:SP', $state)) { diff --git a/modules/consent/www/logout.php b/modules/consent/www/logout.php index 55903707b..1e464294a 100644 --- a/modules/consent/www/logout.php +++ b/modules/consent/www/logout.php @@ -10,6 +10,13 @@ if (!array_key_exists('StateId', $_GET)) { throw new SimpleSAML_Error_BadRequest('Missing required StateId query parameter.'); } $id = (string)$_GET['StateId']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($id); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($id, 'consent:request'); $state['Responder'] = array('sspmod_consent_Logout', 'postLogout'); diff --git a/modules/consent/www/noconsent.php b/modules/consent/www/noconsent.php index 06e5554c0..37b592011 100644 --- a/modules/consent/www/noconsent.php +++ b/modules/consent/www/noconsent.php @@ -12,6 +12,13 @@ if (!array_key_exists('StateId', $_REQUEST)) { } $id = $_REQUEST['StateId']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($id); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($id, 'consent:request'); $resumeFrom = SimpleSAML_Module::getModuleURL( diff --git a/modules/core/lib/Auth/UserPassBase.php b/modules/core/lib/Auth/UserPassBase.php index 633a7484a..c7ff0bebe 100644 --- a/modules/core/lib/Auth/UserPassBase.php +++ b/modules/core/lib/Auth/UserPassBase.php @@ -197,6 +197,12 @@ abstract class sspmod_core_Auth_UserPassBase extends SimpleSAML_Auth_Source { assert('is_string($username)'); assert('is_string($password)'); + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($authStateId); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + /* Here we retrieve the state array we saved in the authenticate-function. */ $state = SimpleSAML_Auth_State::loadState($authStateId, self::STAGEID); diff --git a/modules/core/lib/Auth/UserPassOrgBase.php b/modules/core/lib/Auth/UserPassOrgBase.php index f79c3aefa..9c7af9731 100644 --- a/modules/core/lib/Auth/UserPassOrgBase.php +++ b/modules/core/lib/Auth/UserPassOrgBase.php @@ -209,6 +209,12 @@ abstract class sspmod_core_Auth_UserPassOrgBase extends SimpleSAML_Auth_Source { assert('is_string($password)'); assert('is_string($organization)'); + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($authStateId); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + /* Retrieve the authentication state. */ $state = SimpleSAML_Auth_State::loadState($authStateId, self::STAGEID); @@ -257,6 +263,12 @@ abstract class sspmod_core_Auth_UserPassOrgBase extends SimpleSAML_Auth_Source { public static function listOrganizations($authStateId) { assert('is_string($authStateId)'); + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($authStateId); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + /* Retrieve the authentication state. */ $state = SimpleSAML_Auth_State::loadState($authStateId, self::STAGEID); diff --git a/modules/core/www/as_login.php b/modules/core/www/as_login.php index a30bd1f77..09c07209c 100644 --- a/modules/core/www/as_login.php +++ b/modules/core/www/as_login.php @@ -19,7 +19,7 @@ if (!is_string($_REQUEST['AuthId'])) { * Setting up the options for the requireAuth() call later.. */ $options = array( - 'ReturnTo' => $_REQUEST['ReturnTo'], + 'ReturnTo' => SimpleSAML_Utilities::checkURLAllowed($_REQUEST['ReturnTo']), ); /* @@ -30,9 +30,7 @@ if (!empty($_REQUEST['saml:idp'])) { $options['saml:idp'] = $_REQUEST['saml:idp']; } - - $as = new SimpleSAML_Auth_Simple($_REQUEST['AuthId']); $as->requireAuth($options); -SimpleSAML_Utilities::redirectUntrustedURL($_REQUEST['ReturnTo']); +SimpleSAML_Utilities::redirectTrustedURL($options['ReturnTo']); diff --git a/modules/core/www/as_logout.php b/modules/core/www/as_logout.php index 5ae8a39c3..dad67d213 100644 --- a/modules/core/www/as_logout.php +++ b/modules/core/www/as_logout.php @@ -16,4 +16,4 @@ if (!isset($_REQUEST['AuthId']) || !is_string($_REQUEST['AuthId'])) { } $as = new SimpleSAML_Auth_Simple($_REQUEST['AuthId']); -$as->logout($_REQUEST['ReturnTo']); +$as->logout(SimpleSAML_Utilities::checkURLAllowed($_REQUEST['ReturnTo'])); diff --git a/modules/core/www/bwc_resumeauth.php b/modules/core/www/bwc_resumeauth.php index 68b205504..df937077d 100644 --- a/modules/core/www/bwc_resumeauth.php +++ b/modules/core/www/bwc_resumeauth.php @@ -20,7 +20,7 @@ if ($requestcache['ForceAuthn'] && $requestcache['core:prevSession'] === $sessio } if (isset($state['ReturnTo'])) { - SimpleSAML_Utilities::redirectUntrustedURL($state['ReturnTo']); + SimpleSAML_Utilities::redirectTrustedURL($state['ReturnTo']); } foreach ($session->getAuthState($authority) as $k => $v) { diff --git a/modules/core/www/cleardiscochoices.php b/modules/core/www/cleardiscochoices.php index c94e411e2..afd72997f 100644 --- a/modules/core/www/cleardiscochoices.php +++ b/modules/core/www/cleardiscochoices.php @@ -26,12 +26,12 @@ foreach($_COOKIE as $cookieName => $value) { /* Find where we should go now. */ if(array_key_exists('ReturnTo', $_REQUEST)) { - $returnTo = $_REQUEST['ReturnTo']; + $returnTo = SimpleSAML_Utilities::checkURLAllowed($_REQUEST['ReturnTo']); } else { /* Return to the front page if no other destination is given. This is the same as the base cookie path. */ $returnTo = $cookiePath; } /* Redirect to destination. */ -SimpleSAML_Utilities::redirectUntrustedURL($returnTo); +SimpleSAML_Utilities::redirectTrustedURL($returnTo); diff --git a/modules/core/www/idp/logout-iframe-done.php b/modules/core/www/idp/logout-iframe-done.php index 72a1e26f7..62539988b 100644 --- a/modules/core/www/idp/logout-iframe-done.php +++ b/modules/core/www/idp/logout-iframe-done.php @@ -5,6 +5,12 @@ if (!isset($_REQUEST['id'])) { } $id = (string)$_REQUEST['id']; +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($id); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($id, 'core:Logout-IFrame'); $idp = SimpleSAML_IdP::getByState($state); diff --git a/modules/core/www/idp/logout-iframe.php b/modules/core/www/idp/logout-iframe.php index 1b751e931..53cdcfc94 100644 --- a/modules/core/www/idp/logout-iframe.php +++ b/modules/core/www/idp/logout-iframe.php @@ -19,6 +19,12 @@ if ($type !== 'embed' && $type !== 'async') { SimpleSAML_Stats::log('core:idp:logout-iframe:page', array('type' => $type)); } +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($id); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($id, 'core:Logout-IFrame'); $idp = SimpleSAML_IdP::getByState($state); diff --git a/modules/core/www/idp/resumelogout.php b/modules/core/www/idp/resumelogout.php index 37c3e60f5..f93c8e968 100644 --- a/modules/core/www/idp/resumelogout.php +++ b/modules/core/www/idp/resumelogout.php @@ -5,6 +5,12 @@ if (!isset($_REQUEST['id'])) { } $id = (string)$_REQUEST['id']; +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($id); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($id, 'core:Logout:afterbridge'); $idp = SimpleSAML_IdP::getByState($state); diff --git a/modules/core/www/login-admin.php b/modules/core/www/login-admin.php index 83886a181..3e6438f3e 100644 --- a/modules/core/www/login-admin.php +++ b/modules/core/www/login-admin.php @@ -6,9 +6,8 @@ if (!array_key_exists('ReturnTo', $_REQUEST)) { throw new SimpleSAML_Error_BadRequest('Missing ReturnTo parameter.'); } -$returnTo = $_REQUEST['ReturnTo']; SimpleSAML_Utilities::requireAdmin(); -SimpleSAML_Utilities::redirectUntrustedURL($returnTo); +SimpleSAML_Utilities::redirectUntrustedURL($_REQUEST['ReturnTo']); diff --git a/modules/core/www/loginuserpass.php b/modules/core/www/loginuserpass.php index 71da3aeae..cda363b40 100644 --- a/modules/core/www/loginuserpass.php +++ b/modules/core/www/loginuserpass.php @@ -15,6 +15,12 @@ if (!array_key_exists('AuthState', $_REQUEST)) { } $authStateId = $_REQUEST['AuthState']; +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($authStateId); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + /* Retrieve the authentication state. */ $state = SimpleSAML_Auth_State::loadState($authStateId, sspmod_core_Auth_UserPassBase::STAGEID); diff --git a/modules/core/www/loginuserpassorg.php b/modules/core/www/loginuserpassorg.php index cda773bfc..abd9a532e 100644 --- a/modules/core/www/loginuserpassorg.php +++ b/modules/core/www/loginuserpassorg.php @@ -15,6 +15,12 @@ if (!array_key_exists('AuthState', $_REQUEST)) { } $authStateId = $_REQUEST['AuthState']; +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($authStateId); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + /* Retrieve the authentication state. */ $state = SimpleSAML_Auth_State::loadState($authStateId, sspmod_core_Auth_UserPassOrgBase::STAGEID); diff --git a/modules/core/www/short_sso_interval.php b/modules/core/www/short_sso_interval.php index 5a51470a3..e9e5b159d 100644 --- a/modules/core/www/short_sso_interval.php +++ b/modules/core/www/short_sso_interval.php @@ -12,6 +12,13 @@ if (!array_key_exists('StateId', $_REQUEST)) { } $id = $_REQUEST['StateId']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($id); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($id, 'core:short_sso_interval'); if (array_key_exists('continue', $_REQUEST)) { diff --git a/modules/discopower/lib/PowerIdPDisco.php b/modules/discopower/lib/PowerIdPDisco.php index 22459312b..10aad002f 100644 --- a/modules/discopower/lib/PowerIdPDisco.php +++ b/modules/discopower/lib/PowerIdPDisco.php @@ -203,7 +203,7 @@ class sspmod_discopower_PowerIdPDisco extends SimpleSAML_XHTML_IdPDisco { } else { $this->log('Choice made [' . $idp . '] (Redirecting the user back. returnIDParam=' . $this->returnIdParam . ')'); - SimpleSAML_Utilities::redirectUntrustedURL($this->returnURL, array($this->returnIdParam => $idp)); + SimpleSAML_Utilities::redirectTrustedURL($this->returnURL, array($this->returnIdParam => $idp)); } return; @@ -211,7 +211,7 @@ class sspmod_discopower_PowerIdPDisco extends SimpleSAML_XHTML_IdPDisco { if ($this->isPassive) { $this->log('Choice not made. (Redirecting the user back without answer)'); - SimpleSAML_Utilities::redirectUntrustedURL($this->returnURL); + SimpleSAML_Utilities::redirectTrustedURL($this->returnURL); return; } diff --git a/modules/exampleauth/lib/Auth/Source/External.php b/modules/exampleauth/lib/Auth/Source/External.php index d3b16f020..282c6a46d 100644 --- a/modules/exampleauth/lib/Auth/Source/External.php +++ b/modules/exampleauth/lib/Auth/Source/External.php @@ -186,6 +186,12 @@ class sspmod_exampleauth_Auth_Source_External extends SimpleSAML_Auth_Source { } $stateId = (string)$_REQUEST['State']; + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($stateId); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + /* * Once again, note the second parameter to the loadState function. This must * match the string we used in the saveState-call above. diff --git a/modules/exampleauth/www/authpage.php b/modules/exampleauth/www/authpage.php index 21a284b22..bcd01b8d4 100644 --- a/modules/exampleauth/www/authpage.php +++ b/modules/exampleauth/www/authpage.php @@ -14,7 +14,7 @@ if (!isset($_REQUEST['ReturnTo'])) { die('Missing ReturnTo parameter.'); } -$returnTo = $_REQUEST['ReturnTo']; +$returnTo = SimpleSAML_Utilities::checkURLAllowed($_REQUEST['ReturnTo']); /* @@ -31,6 +31,13 @@ if (!preg_match('@State=(.*)@', $returnTo, $matches)) { die('Invalid ReturnTo URL for this example.'); } $stateId = urldecode($matches[1]); + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($stateId); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + SimpleSAML_Auth_State::loadState($stateId, 'exampleauth:External'); /* @@ -87,8 +94,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $_SESSION['mail'] = $user['mail']; $_SESSION['type'] = $user['type']; - header('Location: ' . $returnTo); - exit(); + SimpleSAML_Utilities::redirectTrustedURL($returnTo); } } diff --git a/modules/exampleauth/www/redirecttest.php b/modules/exampleauth/www/redirecttest.php index 39a1b5450..c6d4fb86e 100644 --- a/modules/exampleauth/www/redirecttest.php +++ b/modules/exampleauth/www/redirecttest.php @@ -13,6 +13,13 @@ if (!array_key_exists('StateId', $_REQUEST)) { } $id = $_REQUEST['StateId']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($id); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($id, 'exampleauth:redirectfilter-test'); $state['Attributes']['RedirectTest2'] = array('OK'); diff --git a/modules/expirycheck/www/about2expire.php b/modules/expirycheck/www/about2expire.php index c69624723..487b3f8df 100644 --- a/modules/expirycheck/www/about2expire.php +++ b/modules/expirycheck/www/about2expire.php @@ -14,6 +14,13 @@ if (!array_key_exists('StateId', $_REQUEST)) { } $id = $_REQUEST['StateId']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($id); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($id, 'expirywarning:about2expire'); diff --git a/modules/expirycheck/www/expired.php b/modules/expirycheck/www/expired.php index 9b94cadbc..5ec7b93a1 100644 --- a/modules/expirycheck/www/expired.php +++ b/modules/expirycheck/www/expired.php @@ -14,6 +14,13 @@ if (!array_key_exists('StateId', $_REQUEST)) { } $id = $_REQUEST['StateId']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($id); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($id, 'expirywarning:expired'); $globalConfig = SimpleSAML_Configuration::getInstance(); diff --git a/modules/multiauth/www/selectsource.php b/modules/multiauth/www/selectsource.php index 329a1bf49..afa28ed50 100644 --- a/modules/multiauth/www/selectsource.php +++ b/modules/multiauth/www/selectsource.php @@ -16,6 +16,12 @@ if (!array_key_exists('AuthState', $_REQUEST)) { } $authStateId = $_REQUEST['AuthState']; +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($authStateId); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + /* Retrieve the authentication state. */ $state = SimpleSAML_Auth_State::loadState($authStateId, sspmod_multiauth_Auth_Source_MultiAuth::STAGEID); diff --git a/modules/negotiate/www/backend.php b/modules/negotiate/www/backend.php index f9d362229..347ce8dc9 100644 --- a/modules/negotiate/www/backend.php +++ b/modules/negotiate/www/backend.php @@ -10,6 +10,13 @@ */ $authStateId = $_REQUEST['AuthState']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($authStateId); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($authStateId, sspmod_negotiate_Auth_Source_Negotiate::STAGEID); SimpleSAML_Logger::debug('backend - fallback: '.$state['LogoutState']['negotiate:backend']); diff --git a/modules/negotiate/www/retry.php b/modules/negotiate/www/retry.php index 2d12eab97..858b83678 100644 --- a/modules/negotiate/www/retry.php +++ b/modules/negotiate/www/retry.php @@ -10,6 +10,13 @@ */ $authStateId = $_REQUEST['AuthState']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($authStateId); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($authStateId, sspmod_negotiate_Auth_Source_Negotiate::STAGEID); $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); diff --git a/modules/oauth/www/authorize.php b/modules/oauth/www/authorize.php index 9b3e032e1..583c86efc 100644 --- a/modules/oauth/www/authorize.php +++ b/modules/oauth/www/authorize.php @@ -56,7 +56,7 @@ try { if ($url) { // If authorize() returns a URL, take user there (oauth1.0a) - SimpleSAML_Utilities::redirectUntrustedURL($url); + SimpleSAML_Utilities::redirectTrustedURL($url); } else if (isset($_REQUEST['oauth_callback'])) { // If callback was provided in the request (oauth1.0) diff --git a/modules/openid/www/consumer.php b/modules/openid/www/consumer.php index 889fbf59c..0f8067c0e 100644 --- a/modules/openid/www/consumer.php +++ b/modules/openid/www/consumer.php @@ -6,6 +6,13 @@ if (!array_key_exists('AuthState', $_REQUEST) || empty($_REQUEST['AuthState'])) } $authState = $_REQUEST['AuthState']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($authState); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($authState, 'openid:init'); $sourceId = $state['openid:AuthId']; $authSource = SimpleSAML_Auth_Source::getById($sourceId); diff --git a/modules/openid/www/linkback.php b/modules/openid/www/linkback.php index 180f9e18e..6108ca73e 100644 --- a/modules/openid/www/linkback.php +++ b/modules/openid/www/linkback.php @@ -6,6 +6,13 @@ if (!array_key_exists('AuthState', $_REQUEST) || empty($_REQUEST['AuthState'])) } $authState = $_REQUEST['AuthState']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($authState); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($authState, 'openid:auth'); $sourceId = $state['openid:AuthId']; $authSource = SimpleSAML_Auth_Source::getById($sourceId); diff --git a/modules/openidProvider/lib/Server.php b/modules/openidProvider/lib/Server.php index f0596d18f..1382cd84d 100644 --- a/modules/openidProvider/lib/Server.php +++ b/modules/openidProvider/lib/Server.php @@ -329,6 +329,12 @@ class sspmod_openidProvider_Server { public function loadState($stateId) { assert('is_string($stateId)'); + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($stateId); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + return SimpleSAML_Auth_State::loadState($stateId, 'openidProvider:resumeState'); } diff --git a/modules/papi/lib/Auth/Source/PAPI.php b/modules/papi/lib/Auth/Source/PAPI.php index ead66c883..c07409346 100644 --- a/modules/papi/lib/Auth/Source/PAPI.php +++ b/modules/papi/lib/Auth/Source/PAPI.php @@ -115,6 +115,13 @@ class sspmod_papi_Auth_Source_PAPI extends SimpleSAML_Auth_Source { if (isset($_REQUEST['SSPStateID'])) { // yes! restore original request $this->_stateId = (string)$_REQUEST['SSPStateID']; + + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($this->_stateId); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + $state = SimpleSAML_Auth_State::loadState($this->_stateId, self::STAGE_INIT); } else if (!$this->_poa->isAuthenticated()) { // no! we have to save the request @@ -161,6 +168,13 @@ class sspmod_papi_Auth_Source_PAPI extends SimpleSAML_Auth_Source { $this->_poa->logout(true); } else if (isset($_REQUEST['SSPStateID'])) { $this->_stateId = (string)$_REQUEST['SSPStateID']; + + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($this->_stateId); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + $state = SimpleSAML_Auth_State::loadState($this->_stateId, self::STAGE_INIT); } else { return; diff --git a/modules/preprodwarning/www/showwarning.php b/modules/preprodwarning/www/showwarning.php index d8db64560..2c50860dd 100644 --- a/modules/preprodwarning/www/showwarning.php +++ b/modules/preprodwarning/www/showwarning.php @@ -15,6 +15,13 @@ if (!array_key_exists('StateId', $_REQUEST)) { } $id = $_REQUEST['StateId']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($id); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + $state = SimpleSAML_Auth_State::loadState($id, 'warning:request'); diff --git a/modules/saml/www/sp/discoresp.php b/modules/saml/www/sp/discoresp.php index 6d9af381e..1479f8a42 100644 --- a/modules/saml/www/sp/discoresp.php +++ b/modules/saml/www/sp/discoresp.php @@ -12,7 +12,15 @@ if (!array_key_exists('idpentityid', $_REQUEST)) { throw new SimpleSAML_Error_BadRequest('Missing idpentityid to discovery service response handler'); } -$state = SimpleSAML_Auth_State::loadState($_REQUEST['AuthID'], 'saml:sp:sso'); +$stateID = $_REQUEST['AuthID']; + +// sanitize the input +$restartURL = SimpleSAML_Utilities::getURLFromStateID($stateID); +if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); +} + +$state = SimpleSAML_Auth_State::loadState($stateID, 'saml:sp:sso'); /* Find authentication source. */ assert('array_key_exists("saml:sp:AuthId", $state)'); diff --git a/modules/saml/www/sp/saml1-acs.php b/modules/saml/www/sp/saml1-acs.php index 28ba71143..d9a594c5e 100644 --- a/modules/saml/www/sp/saml1-acs.php +++ b/modules/saml/www/sp/saml1-acs.php @@ -19,17 +19,25 @@ $source = SimpleSAML_Auth_Source::getById($sourceId, 'sspmod_saml_Auth_Source_SP SimpleSAML_Logger::debug('Received SAML1 response'); - $target = (string)$_REQUEST['TARGET']; + if (preg_match('@^https?://@i', $target)) { /* Unsolicited response. */ $state = array( 'saml:sp:isUnsolicited' => TRUE, 'saml:sp:AuthId' => $sourceId, - 'saml:sp:RelayState' => $target, + 'saml:sp:RelayState' => SimpleSAML_Utilities::checkURLAllowed($target), ); } else { - $state = SimpleSAML_Auth_State::loadState($_REQUEST['TARGET'], 'saml:sp:sso'); + $stateID = $_REQUEST['TARGET']; + + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($stateID); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + + $state = SimpleSAML_Auth_State::loadState($stateID, 'saml:sp:sso'); /* Check that the authentication source is correct. */ assert('array_key_exists("saml:sp:AuthId", $state)'); @@ -80,4 +88,4 @@ $state['LogoutState'] = $logoutState; $source->handleResponse($state, $responseIssuer, $attributes); assert('FALSE'); -?> \ No newline at end of file +?> diff --git a/modules/saml/www/sp/saml2-acs.php b/modules/saml/www/sp/saml2-acs.php index a3c8200bc..09723b642 100644 --- a/modules/saml/www/sp/saml2-acs.php +++ b/modules/saml/www/sp/saml2-acs.php @@ -52,6 +52,13 @@ $idpMetadata = array(); $stateId = $response->getInResponseTo(); if (!empty($stateId)) { + + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($stateId); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + /* This is a response to a request we sent earlier. */ $state = SimpleSAML_Auth_State::loadState($stateId, 'saml:sp:sso'); @@ -75,7 +82,7 @@ if (!empty($stateId)) { $state = array( 'saml:sp:isUnsolicited' => TRUE, 'saml:sp:AuthId' => $sourceId, - 'saml:sp:RelayState' => $response->getRelayState(), + 'saml:sp:RelayState' => SimpleSAML_Utilities::checkURLAllowed($response->getRelayState()), ); } diff --git a/modules/saml/www/sp/saml2-logout.php b/modules/saml/www/sp/saml2-logout.php index d2b26b06e..5d3602433 100644 --- a/modules/saml/www/sp/saml2-logout.php +++ b/modules/saml/www/sp/saml2-logout.php @@ -54,6 +54,12 @@ if ($message instanceof SAML2_LogoutResponse) { SimpleSAML_Logger::warning('Unsuccessful logout. Status was: ' . sspmod_saml_Message::getResponseError($message)); } + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($relayState); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + $state = SimpleSAML_Auth_State::loadState($relayState, 'saml:slosent'); $state['saml:sp:LogoutStatus'] = $message->getStatus(); SimpleSAML_Auth_Source::completeLogout($state); diff --git a/www/auth/login-admin.php b/www/auth/login-admin.php index 42ac0ecf0..597b353aa 100644 --- a/www/auth/login-admin.php +++ b/www/auth/login-admin.php @@ -20,7 +20,7 @@ if (!array_key_exists('RelayState', $_REQUEST)) { throw new SimpleSAML_Error_Error('NORELAYSTATE'); } -$relaystate = $_REQUEST['RelayState']; +$relaystate = SimpleSAML_Utilities::checkURLAllowed($_REQUEST['RelayState']); $correctpassword = $config->getString('auth.adminpassword', '123'); @@ -59,7 +59,7 @@ if (isset($_POST['password'])) { else SimpleSAML_Logger::stats('AUTH-login-admin OK'); - SimpleSAML_Utilities::redirectUntrustedURL($relaystate); + SimpleSAML_Utilities::redirectTrustedURL($relaystate); exit(0); } else { SimpleSAML_Logger::stats('AUTH-login-admin Failed'); @@ -82,6 +82,3 @@ if (isset($error)) { } $t->show(); - - -?> diff --git a/www/auth/login-cas-ldap.php b/www/auth/login-cas-ldap.php index 2ba907ade..281488d97 100644 --- a/www/auth/login-cas-ldap.php +++ b/www/auth/login-cas-ldap.php @@ -30,8 +30,6 @@ try { $casconfig = $casldapconfig[$idpentityid]['cas']; $ldapconfig = $casldapconfig[$idpentityid]['ldap']; - - } catch (Exception $exception) { throw new SimpleSAML_Error_Error('METADATA', $exception); } @@ -44,8 +42,6 @@ if (!array_key_exists('RelayState', $_REQUEST)) { throw new SimpleSAML_Error_Error('NORELAYSTATE'); } - - function casValidate($cas) { $service = SimpleSAML_Utilities::selfURL(); @@ -110,12 +106,7 @@ function casValidate($cas) { } } - - try { - - $relaystate = $_REQUEST['RelayState']; - list($username, $casattributes) = casValidate($casconfig); SimpleSAML_Logger::info('AUTH - cas-ldap: '. $username . ' authenticated by ' . $casconfig['validate']); @@ -132,11 +123,9 @@ try { $session->setNameID(array( 'value' => SimpleSAML_Utilities::generateID(), 'Format' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient')); - SimpleSAML_Utilities::redirectUntrustedURL($relaystate); + + SimpleSAML_Utilities::redirectUntrustedURL($_REQUEST['RelayState']); } catch(Exception $exception) { throw new SimpleSAML_Error_Error('CASERROR', $exception); } - - -?> \ No newline at end of file diff --git a/www/auth/login-ldapmulti.php b/www/auth/login-ldapmulti.php index aab198afc..169800bc3 100644 --- a/www/auth/login-ldapmulti.php +++ b/www/auth/login-ldapmulti.php @@ -10,7 +10,6 @@ $session = SimpleSAML_Session::getInstance(); $ldapconfigfile = $config->getBaseDir() . 'config/ldapmulti.php'; require_once($ldapconfigfile); - SimpleSAML_Logger::info('AUTH - ldap-multi: Accessing auth endpoint login-ldapmulti'); $error = null; @@ -23,6 +22,8 @@ if (!array_key_exists('RelayState', $_REQUEST)) { throw new SimpleSAML_Error_Error('NORELAYSTATE'); } +$relaystate = SimpleSAML_Utilities::checkURLAllowed($_REQUEST['RelayState']); + if (isset($_POST['username'])) { try { @@ -50,8 +51,7 @@ if (isset($_POST['username'])) { $attributes = $ldap->getAttributes($dn, $ldapconfig['attributes']); SimpleSAML_Logger::info('AUTH - ldap-multi: '. $_POST['username'] . ' successfully authenticated'); - - + $session->doLogin('login-ldapmulti'); $session->setAttributes($attributes); @@ -64,19 +64,16 @@ if (isset($_POST['username'])) { * Also log a specific attribute as set in the config: statistics.authlogattr */ $authlogattr = $config->getValue('statistics.authlogattr', null); - if ($authlogattr && array_key_exists($authlogattr, $attributes)) + if ($authlogattr && array_key_exists($authlogattr, $attributes)) { SimpleSAML_Logger::stats('AUTH-login-ldapmulti OK ' . $attributes[$authlogattr][0]); - else + } else { SimpleSAML_Logger::stats('AUTH-login-ldapmulti OK'); - - - $returnto = $_REQUEST['RelayState']; - SimpleSAML_Utilities::redirectUntrustedURL($returnto); + } + + SimpleSAML_Utilities::redirectTrustedURL($relaystate); } catch (Exception $e) { - $error = $e->getMessage(); - } } @@ -84,7 +81,7 @@ if (isset($_POST['username'])) { $t = new SimpleSAML_XHTML_Template($config, 'login-ldapmulti.php', 'login'); $t->data['header'] = 'simpleSAMLphp: Enter username and password'; -$t->data['relaystate'] = $_REQUEST['RelayState']; +$t->data['relaystate'] = $relaystate; $t->data['ldapconfig'] = $ldapmulti; $t->data['org'] = $_REQUEST['org']; $t->data['error'] = $error; diff --git a/www/auth/login-radius.php b/www/auth/login-radius.php index b95c7ae92..5a80f49f0 100644 --- a/www/auth/login-radius.php +++ b/www/auth/login-radius.php @@ -19,6 +19,8 @@ if (!array_key_exists('RelayState', $_REQUEST)) { throw new SimpleSAML_Error_Error('NORELAYSTATE'); } +$relaystate = SimpleSAML_Utilities::checkURLAllowed($_REQUEST['RelayState']); + if (isset($_POST['username'])) { @@ -97,21 +99,18 @@ if (isset($_POST['username'])) { 'value' => SimpleSAML_Utilities::generateID(), 'Format' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient')); - /** * Create a statistics log entry for every successfull login attempt. * Also log a specific attribute as set in the config: statistics.authlogattr */ $authlogattr = $config->getValue('statistics.authlogattr', null); - if ($authlogattr && array_key_exists($authlogattr, $attributes)) + if ($authlogattr && array_key_exists($authlogattr, $attributes)) { SimpleSAML_Logger::stats('AUTH-login-radius OK ' . $attributes[$authlogattr][0]); - else + } else { SimpleSAML_Logger::stats('AUTH-login-radius OK'); + } - - $returnto = $_REQUEST['RelayState']; - SimpleSAML_Utilities::redirectUntrustedURL($returnto); - + SimpleSAML_Utilities::redirectTrustedURL($relaystate); case RADIUS_ACCESS_REJECT: @@ -125,13 +124,10 @@ if (isset($_POST['username'])) { default: SimpleSAML_Logger::critical('AUTH -radius: General radius error: ' . radius_strerror($radius)); throw new Exception('Error during radius authentication: ' . radius_strerror($radius)); - } } catch (Exception $e) { - $error = $e->getMessage(); - } } @@ -139,13 +135,10 @@ if (isset($_POST['username'])) { $t = new SimpleSAML_XHTML_Template($config, 'login.php', 'login'); $t->data['header'] = 'simpleSAMLphp: Enter username and password'; -$t->data['relaystate'] = $_REQUEST['RelayState']; +$t->data['relaystate'] = $relaystate; $t->data['error'] = $error; if (isset($error)) { $t->data['username'] = $_POST['username']; } $t->show(); - - -?> diff --git a/www/auth/login-tlsclient.php b/www/auth/login-tlsclient.php index 42e2678ae..0fb6e9392 100644 --- a/www/auth/login-tlsclient.php +++ b/www/auth/login-tlsclient.php @@ -23,9 +23,6 @@ if (!array_key_exists('RelayState', $_REQUEST)) { throw new SimpleSAML_Error_Error('NORELAYSTATE'); } -$relaystate = $_REQUEST['RelayState']; - - try { $attributes = array(); @@ -55,21 +52,21 @@ try { $session->setNameID(array( 'value' => SimpleSAML_Utilities::generateID(), - 'Format' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient')); + 'Format' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient') + ); /** * Create a statistics log entry for every successfull login attempt. * Also log a specific attribute as set in the config: statistics.authlogattr */ $authlogattr = $config->getValue('statistics.authlogattr', null); - if ($authlogattr && array_key_exists($authlogattr, $attributes)) + if ($authlogattr && array_key_exists($authlogattr, $attributes)) { SimpleSAML_Logger::stats('AUTH-tlsclient OK ' . $attributes[$authlogattr][0]); - else + } else { SimpleSAML_Logger::stats('AUTH-tlsclient OK'); - + } - $returnto = $_REQUEST['RelayState']; - SimpleSAML_Utilities::redirectUntrustedURL($returnto); + SimpleSAML_Utilities::redirectUntrustedURL($_REQUEST['RelayState']); } catch (Exception $e) { diff --git a/www/auth/login-wayf-ldap.php b/www/auth/login-wayf-ldap.php index 73d5f57a4..9c8c25e52 100644 --- a/www/auth/login-wayf-ldap.php +++ b/www/auth/login-wayf-ldap.php @@ -42,7 +42,7 @@ if (!array_key_exists('RelayState', $_REQUEST)) { throw new SimpleSAML_Error_Error('NORELAYSTATE'); } -$relaystate = $_REQUEST['RelayState']; +$relaystate = SimpleSAML_Utilities::checkURLAllowed($_REQUEST['RelayState']); if ($username = $_POST['username']) { try { @@ -59,7 +59,7 @@ if ($username = $_POST['username']) { $session->setNameID(array( 'value' => SimpleSAML_Utilities::generateID(), 'Format' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient')); - SimpleSAML_Utilities::redirectUntrustedURL($relaystate); + SimpleSAML_Utilities::redirectTrustedURL($relaystate); } } catch(Exception $e) { throw new SimpleSAML_Error_Error('LDAPERROR', $e); diff --git a/www/saml2/idp/SingleLogoutService.php b/www/saml2/idp/SingleLogoutService.php index 618068cd8..45a1012a4 100644 --- a/www/saml2/idp/SingleLogoutService.php +++ b/www/saml2/idp/SingleLogoutService.php @@ -18,7 +18,7 @@ $idpEntityId = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); $idp = SimpleSAML_IdP::getById('saml2:' . $idpEntityId); if (isset($_REQUEST['ReturnTo'])) { - $idp->doLogoutRedirect((string)$_REQUEST['ReturnTo']); + $idp->doLogoutRedirect(SimpleSAML_Utilities::checkURLAllowed((string)$_REQUEST['ReturnTo'])); } else { sspmod_saml_IdP_SAML2::receiveLogoutMessage($idp); } diff --git a/www/saml2/idp/idpInitSingleLogoutServiceiFrame.php b/www/saml2/idp/idpInitSingleLogoutServiceiFrame.php index 24f89b013..977265932 100644 --- a/www/saml2/idp/idpInitSingleLogoutServiceiFrame.php +++ b/www/saml2/idp/idpInitSingleLogoutServiceiFrame.php @@ -16,5 +16,5 @@ if (!isset($_REQUEST['RelayState'])) { throw new SimpleSAML_Error_BadRequest('Missing required RelayState parameter.'); } -$idp->doLogoutRedirect((string)$_REQUEST['RelayState']); +$idp->doLogoutRedirect(SimpleSAML_Utilities::checkURLAllowed((string)$_REQUEST['RelayState'])); assert('FALSE'); diff --git a/www/saml2/idp/initSLO.php b/www/saml2/idp/initSLO.php index d6b23adc3..87191b777 100644 --- a/www/saml2/idp/initSLO.php +++ b/www/saml2/idp/initSLO.php @@ -11,6 +11,5 @@ if (!isset($_GET['RelayState'])) { throw new SimpleSAML_Error_Error('NORELAYSTATE'); } -$returnTo = (string)$_GET['RelayState']; -$idp->doLogoutRedirect($returnTo); +$idp->doLogoutRedirect(SimpleSAML_Utilities::checkURLAllowed((string)$_GET['RelayState'])); assert('FALSE'); \ No newline at end of file diff --git a/www/saml2/sp/AssertionConsumerService.php b/www/saml2/sp/AssertionConsumerService.php index e024489ba..c7dc96f29 100644 --- a/www/saml2/sp/AssertionConsumerService.php +++ b/www/saml2/sp/AssertionConsumerService.php @@ -47,7 +47,7 @@ function finishLogin($authProcState) { global $session; $session->doLogin('saml2', $authData); - SimpleSAML_Utilities::redirectUntrustedURL($authProcState['core:saml20-sp:TargetURL']); + SimpleSAML_Utilities::redirectTrustedURL($authProcState['core:saml20-sp:TargetURL']); } SimpleSAML_Logger::info('SAML2.0 - SP.AssertionConsumerService: Accessing SAML 2.0 SP endpoint AssertionConsumerService'); @@ -59,6 +59,13 @@ if (array_key_exists(SimpleSAML_Auth_ProcessingChain::AUTHPARAM, $_REQUEST)) { /* We have returned from the authentication processing filters. */ $authProcId = $_REQUEST[SimpleSAML_Auth_ProcessingChain::AUTHPARAM]; + + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($authProcId); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + $authProcState = SimpleSAML_Auth_ProcessingChain::fetchProcessedState($authProcId); finishLogin($authProcState); } @@ -93,7 +100,7 @@ try { if($info === NULL) { /* Fall back to RelayState. */ $info = array(); - $info['RelayState'] = $response->getRelayState(); + $info['RelayState'] = SimpleSAML_Utilities::checkURLAllowed($response->getRelayState()); if(empty($info['RelayState'])) { $info['RelayState'] = $spMetadata->getString('RelayState', NULL); } diff --git a/www/saml2/sp/SingleLogoutService.php b/www/saml2/sp/SingleLogoutService.php index 0bd8c731e..cf9f94cd0 100644 --- a/www/saml2/sp/SingleLogoutService.php +++ b/www/saml2/sp/SingleLogoutService.php @@ -83,12 +83,14 @@ if ($message instanceof SAML2_LogoutRequest) { $id = $message->getInResponseTo(); } + // 'spLogoutReturnTo' is checked before storing it in the + // session, so we trust it here. $returnTo = $session->getData('spLogoutReturnTo', $id); if (empty($returnTo)) { throw new SimpleSAML_Error_Error('LOGOUTINFOLOST'); } - SimpleSAML_Utilities::redirectUntrustedURL($returnTo); + SimpleSAML_Utilities::redirectTrustedURL($returnTo); } else { throw new SimpleSAML_Error_Error('SLOSERVICEPARAMS'); diff --git a/www/saml2/sp/initSLO.php b/www/saml2/sp/initSLO.php index 8402a36c0..85c13bbe0 100644 --- a/www/saml2/sp/initSLO.php +++ b/www/saml2/sp/initSLO.php @@ -13,7 +13,7 @@ if (!$config->getBoolean('enable.saml20-sp', TRUE)) if (isset($_REQUEST['RelayState'])) { - $returnTo = $_REQUEST['RelayState']; + $returnTo = SimpleSAML_Utilities::checkURLAllowed($_REQUEST['RelayState']); } else { throw new SimpleSAML_Error_Error('NORELAYSTATE'); } @@ -25,7 +25,7 @@ try { $idpEntityId = $session->getAuthData('saml2', 'saml:sp:IdP'); if ($idpEntityId === NULL) { SimpleSAML_Logger::info('SAML2.0 - SP.initSLO: User not authenticated with an IdP.'); - SimpleSAML_Utilities::redirectUntrustedURL($returnTo); + SimpleSAML_Utilities::redirectTrustedURL($returnTo); } $idpMetadata = $metadata->getMetaDataConfig($idpEntityId, 'saml20-idp-remote'); $SLOendpoint = $idpMetadata->getEndpointPrioritizedByBinding('SingleLogoutService', array( @@ -34,8 +34,8 @@ try { NULL); if ($SLOendpoint === NULL) { $session->doLogout('saml2'); - SimpleSAML_Logger::info('SAML2.0 - SP.initSLO: No supported SingleLogoutService endpoint in IdP.'); - SimpleSAML_Utilities::redirectUntrustedURL($returnTo); + SimpleSAML_Logger::info('SAML2.0 - SP.initSLO: No SingleLogoutService endpoint supported in the IdP.'); + SimpleSAML_Utilities::redirectTrustedURL($returnTo); } $spEntityId = isset($_GET['spentityid']) ? $_GET['spentityid'] : $metadata->getMetaDataCurrentEntityID(); diff --git a/www/saml2/sp/initSSO.php b/www/saml2/sp/initSSO.php index a9ee60fbe..c6d178036 100644 --- a/www/saml2/sp/initSSO.php +++ b/www/saml2/sp/initSSO.php @@ -23,6 +23,7 @@ if (!$config->getBoolean('enable.saml20-sp', TRUE)) if (empty($_GET['RelayState'])) { throw new SimpleSAML_Error_Error('NORELAYSTATE'); } +$returnTo = SimpleSAML_Utilities::checkURLAllowed($_GET['RelayState']); $reachableIDPs = array(); @@ -134,7 +135,7 @@ try { $assertionConsumerServiceURL = $metadata->getGenerated('AssertionConsumerService', 'saml20-sp-hosted'); $ar->setAssertionConsumerServiceURL($assertionConsumerServiceURL); - $ar->setRelayState($_REQUEST['RelayState']); + $ar->setRelayState($returnTo); if ($isPassive) { $ar->setIsPassive(TRUE); @@ -156,9 +157,9 @@ try { /* Save request information. */ $info = array(); - $info['RelayState'] = $_REQUEST['RelayState']; + $info['RelayState'] = $returnTo; if(array_key_exists('OnError', $_REQUEST)) { - $info['OnError'] = $_REQUEST['OnError']; + $info['OnError'] = SimpleSAML_Utilities::checkURLAllowed($_REQUEST['OnError']); } $session->setData('SAML2:SP:SSO:Info', $ar->getId(), $info); diff --git a/www/shib13/sp/AssertionConsumerService.php b/www/shib13/sp/AssertionConsumerService.php index f523c28a4..bc5a4368c 100644 --- a/www/shib13/sp/AssertionConsumerService.php +++ b/www/shib13/sp/AssertionConsumerService.php @@ -47,6 +47,13 @@ if (array_key_exists(SimpleSAML_Auth_ProcessingChain::AUTHPARAM, $_REQUEST)) { /* We have returned from the authentication processing filters. */ $authProcId = $_REQUEST[SimpleSAML_Auth_ProcessingChain::AUTHPARAM]; + + // sanitize the input + $restartURL = SimpleSAML_Utilities::getURLFromStateID($authProcId); + if (!is_null($restartURL)) { + SimpleSAML_Utilities::checkURLAllowed($restartURL); + } + $authProcState = SimpleSAML_Auth_ProcessingChain::fetchProcessedState($authProcId); finishLogin($authProcState); } @@ -86,7 +93,7 @@ try { $authProcState = array( 'core:shib13-sp:NameID' => $authnResponse->getNameID(), 'core:shib13-sp:SessionIndex' => $authnResponse->getSessionIndex(), - 'core:shib13-sp:TargetURL' => $relayState, + 'core:shib13-sp:TargetURL' => SimpleSAML_Utilities::checkURLAllowed($relayState), 'ReturnURL' => SimpleSAML_Utilities::selfURLNoQuery(), 'Attributes' => $authnResponse->getAttributes(), 'Destination' => $spmetadata, diff --git a/www/shib13/sp/initSSO.php b/www/shib13/sp/initSSO.php index d666b8b86..5d40daae2 100644 --- a/www/shib13/sp/initSSO.php +++ b/www/shib13/sp/initSSO.php @@ -70,7 +70,7 @@ if (!isset($session) || !$session->isValid('shib13') ) { $ar = new SimpleSAML_XML_Shib13_AuthnRequest(); $ar->setIssuer($spentityid); if(isset($_GET['RelayState'])) - $ar->setRelayState($_GET['RelayState']); + $ar->setRelayState(SimpleSAML_Utilities::checkURLAllowed($_GET['RelayState'])); SimpleSAML_Logger::info('Shib1.3 - SP.initSSO: SP (' . $spentityid . ') is sending AuthNRequest to IdP (' . $idpentityid . ')'); diff --git a/www/wsfed/sp/initSLO.php b/www/wsfed/sp/initSLO.php index f31ebaaca..318bfa2fe 100644 --- a/www/wsfed/sp/initSLO.php +++ b/www/wsfed/sp/initSLO.php @@ -13,7 +13,7 @@ if (!$config->getBoolean('enable.wsfed-sp', false)) if (isset($_REQUEST['RelayState'])) { - $returnTo = $_REQUEST['RelayState']; + $returnTo = SimpleSAML_Utilities::checkURLAllowed($_REQUEST['RelayState']); } else { throw new SimpleSAML_Error_Error('NORELAYSTATE'); } @@ -53,7 +53,7 @@ if (isset($session) ) { } else { SimpleSAML_Logger::info('WS-Fed - SP.initSLO: User is already logged out. Go back to relaystate'); - SimpleSAML_Utilities::redirectUntrustedURL($returnTo); + SimpleSAML_Utilities::redirectTrustedURL($returnTo); } diff --git a/www/wsfed/sp/initSSO.php b/www/wsfed/sp/initSSO.php index 0e0b8613f..f3f1ec7d7 100644 --- a/www/wsfed/sp/initSSO.php +++ b/www/wsfed/sp/initSSO.php @@ -46,7 +46,7 @@ if ($idpentityid == null) { } try { - $relaystate = $_GET['RelayState']; + $relaystate = SimpleSAML_Utilities::checkURLAllowed($_GET['RelayState']); $idpmeta = $metadata->getMetaData($idpentityid, 'wsfed-idp-remote'); $spmeta = $metadata->getMetaData($spentityid, 'wsfed-sp-hosted'); -- GitLab