* @package simpleSAMLphp
- * @version $Id: Utilities.php 3085 2012-05-04 08:03:01Z olavmrk $
*/
class SimpleSAML_Utilities {
@@ -59,7 +58,11 @@ class SimpleSAML_Utilities {
if(strstr($currenthost, ":")) {
$currenthostdecomposed = explode(":", $currenthost);
- $currenthost = $currenthostdecomposed[0];
+ $port = array_pop($currenthostdecomposed);
+ if (!is_numeric($port)) {
+ array_push($currenthostdecomposed, $port);
+ }
+ $currenthost = implode($currenthostdecomposed, ":");
}
return $currenthost;
@@ -193,7 +196,7 @@ class SimpleSAML_Utilities {
$requestURI = $_SERVER['REQUEST_URI'];
if ($requestURI[0] !== '/') {
- /* We probably have an url on the form: http://server/. */
+ /* We probably have a URL of the form: http://server/. */
if (preg_match('#^https?://[^/]*(/.*)#i', $requestURI, $matches)) {
$requestURI = $matches[1];
}
@@ -219,7 +222,7 @@ class SimpleSAML_Utilities {
$baseURL = $globalConfig->getString('baseurlpath', 'simplesaml/');
if (preg_match('#^https?://.*/$#D', $baseURL, $matches)) {
- /* full url in baseurlpath, override local server values */
+ /* full URL in baseurlpath, override local server values */
return $baseURL;
} elseif (
(preg_match('#^/?([^/]?.*/)$#D', $baseURL, $matches)) ||
@@ -294,18 +297,83 @@ 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) {
+ if (empty($url)) {
+ return '';
+ }
+ $url = self::normalizeURL($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 the ID and (optionally) a URL embedded in a StateID,
+ * in the form 'id:url'.
+ *
+ * @param string $stateId The state ID to use.
+ * @return array A hashed array with the ID and the URL (if any),
+ * in the 'id' and 'url' keys, respectively. If there's no URL
+ * in the input parameter, NULL will be returned as the value for
+ * the 'url' key.
+ */
+ public static function parseStateID($stateId) {
+ $tmp = explode(':', $stateId, 2);
+ $id = $tmp[0];
+ $url = NULL;
+ if (count($tmp) === 2) {
+ $url = $tmp[1];
+ }
+ return array('id' => $id, 'url' => $url);
+ }
+
+
public static function checkDateConditions($start=NULL, $end=NULL) {
$currentTime = time();
- if (! empty($start)) {
- $startTime = self::parseSAML2Time($start);
+ if (!empty($start)) {
+ $startTime = SAML2_Utils::xsDateTimeToTimestamp($start);
/* Allow for a 10 minute difference in Time */
if (($startTime < 0) || (($startTime - 600) > $currentTime)) {
return FALSE;
}
}
- if (! empty($end)) {
- $endTime = self::parseSAML2Time($end);
+ if (!empty($end)) {
+ $endTime = SAML2_Utils::xsDateTimeToTimestamp($end);
if (($endTime < 0) || ($endTime <= $currentTime)) {
return FALSE;
}
@@ -333,55 +401,6 @@ class SimpleSAML_Utilities {
}
- /* This function converts a SAML2 timestamp on the form
- * yyyy-mm-ddThh:mm:ss(\.s+)?Z to a UNIX timestamp. The sub-second
- * part is ignored.
- *
- * Andreas comments:
- * I got this timestamp from Shibboleth 1.3 IdP: 2008-01-17T11:28:03.577Z
- * Therefore I added to possibliity to have microseconds to the format.
- * Added: (\.\\d{1,3})? to the regex.
- *
- *
- * Parameters:
- * $time The time we should convert.
- *
- * Returns:
- * $time converted to a unix timestamp.
- */
- public static function parseSAML2Time($time) {
- $matches = array();
-
-
- /* We use a very strict regex to parse the timestamp. */
- if(preg_match('/^(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)' .
- 'T(\\d\\d):(\\d\\d):(\\d\\d)(?:\\.\\d+)?Z$/D',
- $time, $matches) == 0) {
- throw new Exception(
- 'Invalid SAML2 timestamp passed to' .
- ' parseSAML2Time: ' . $time);
- }
-
- /* Extract the different components of the time from the
- * matches in the regex. intval will ignore leading zeroes
- * in the string.
- */
- $year = intval($matches[1]);
- $month = intval($matches[2]);
- $day = intval($matches[3]);
- $hour = intval($matches[4]);
- $minute = intval($matches[5]);
- $second = intval($matches[6]);
-
- /* We use gmmktime because the timestamp will always be given
- * in UTC.
- */
- $ts = gmmktime($hour, $minute, $second, $month, $day, $year);
-
- return $ts;
- }
-
-
/**
* Interpret a ISO8601 duration value relative to a given timestamp.
*
@@ -538,95 +557,20 @@ class SimpleSAML_Utilities {
return true;
}
-
- /* This function redirects the user to the specified address.
- * An optional set of query parameters can be appended by passing
- * them in an array.
- *
- * This function will use the HTTP 303 See Other redirect if the
- * current request is a POST request and the HTTP version is HTTP/1.1.
- * Otherwise a HTTP 302 Found redirect will be used.
- *
- * The fuction will also generate a simple web page with a clickable
- * link to the target page.
- *
- * Parameters:
- * $url 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.
- * $parameters 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.
- *
- * Returns:
- * This function never returns.
+ /*
+ * This is a temporary function, holding the redirect() functionality,
+ * meanwhile we are deprecating the it.
*/
- public static function redirect($url, $parameters = array()) {
- assert(is_string($url));
- assert(strlen($url) > 0);
- assert(is_array($parameters));
-
- /* 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 is to a http or https site. */
- if (!preg_match('@^https?://@i', $url)) {
- throw new SimpleSAML_Error_Exception('Redirect to invalid 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 {
@@ -634,7 +578,7 @@ class SimpleSAML_Utilities {
}
if (strlen($url) > 2048) {
- SimpleSAML_Logger::warning('Redirecting to URL longer than 2048 bytes.');
+ SimpleSAML_Logger::warning('Redirecting to a URL longer than 2048 bytes.');
}
/* Set the location header. */
@@ -657,7 +601,8 @@ class SimpleSAML_Utilities {
echo 'Redirect
';
echo '';
echo 'You were redirected to: ';
- echo '' . htmlspecialchars($url) . '';
+ echo '' . htmlspecialchars($url) . '';
echo '';
echo '
';
echo '