summaryrefslogtreecommitdiffstats
path: root/lib/SimpleSAML
diff options
context:
space:
mode:
Diffstat (limited to 'lib/SimpleSAML')
-rw-r--r--lib/SimpleSAML/Auth/Default.php16
-rw-r--r--lib/SimpleSAML/Auth/LDAP.php4
-rw-r--r--lib/SimpleSAML/Auth/ProcessingChain.php4
-rw-r--r--lib/SimpleSAML/Auth/Simple.php13
-rw-r--r--lib/SimpleSAML/Auth/State.php32
-rw-r--r--lib/SimpleSAML/Auth/TimeLimitedToken.php13
-rw-r--r--lib/SimpleSAML/Bindings/Shib13/Artifact.php22
-rw-r--r--lib/SimpleSAML/Bindings/Shib13/HTTPPost.php18
-rw-r--r--lib/SimpleSAML/Configuration.php8
-rw-r--r--lib/SimpleSAML/Error/Error.php6
-rw-r--r--lib/SimpleSAML/Error/NotFound.php2
-rw-r--r--lib/SimpleSAML/IdP.php2
-rw-r--r--lib/SimpleSAML/IdP/LogoutIFrame.php2
-rw-r--r--lib/SimpleSAML/IdP/LogoutTraditional.php2
-rw-r--r--lib/SimpleSAML/Logger/LoggingHandlerFile.php2
-rw-r--r--lib/SimpleSAML/Logger/LoggingHandlerSyslog.php2
-rw-r--r--lib/SimpleSAML/Memcache.php2
-rw-r--r--lib/SimpleSAML/Metadata/MetaDataStorageHandler.php8
-rw-r--r--lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php6
-rw-r--r--lib/SimpleSAML/Metadata/MetaDataStorageHandlerMDX.php8
-rw-r--r--lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php15
-rw-r--r--lib/SimpleSAML/Metadata/MetaDataStorageSource.php4
-rw-r--r--lib/SimpleSAML/Metadata/SAMLBuilder.php23
-rw-r--r--lib/SimpleSAML/Metadata/SAMLParser.php16
-rw-r--r--lib/SimpleSAML/Metadata/Signer.php4
-rw-r--r--lib/SimpleSAML/Module.php4
-rw-r--r--lib/SimpleSAML/Session.php4
-rw-r--r--lib/SimpleSAML/SessionHandler.php2
-rw-r--r--lib/SimpleSAML/SessionHandlerCookie.php2
-rw-r--r--lib/SimpleSAML/SessionHandlerPHP.php6
-rw-r--r--lib/SimpleSAML/Stats.php2
-rw-r--r--lib/SimpleSAML/Store.php3
-rw-r--r--lib/SimpleSAML/Utilities.php1989
-rw-r--r--lib/SimpleSAML/Utils/Arrays.php104
-rw-r--r--lib/SimpleSAML/Utils/Auth.php72
-rw-r--r--lib/SimpleSAML/Utils/Config.php58
-rw-r--r--lib/SimpleSAML/Utils/Config/Metadata.php109
-rw-r--r--lib/SimpleSAML/Utils/Crypto.php464
-rw-r--r--lib/SimpleSAML/Utils/HTTP.php1027
-rw-r--r--lib/SimpleSAML/Utils/Net.php82
-rw-r--r--lib/SimpleSAML/Utils/Random.php25
-rw-r--r--lib/SimpleSAML/Utils/System.php199
-rw-r--r--lib/SimpleSAML/Utils/Time.php162
-rw-r--r--lib/SimpleSAML/Utils/XML.php428
-rw-r--r--lib/SimpleSAML/XHTML/EMail.php2
-rw-r--r--lib/SimpleSAML/XHTML/IdPDisco.php25
-rw-r--r--lib/SimpleSAML/XHTML/Template.php5
-rw-r--r--lib/SimpleSAML/XML/Parser.php5
-rw-r--r--lib/SimpleSAML/XML/Shib13/AuthnRequest.php2
-rw-r--r--lib/SimpleSAML/XML/Shib13/AuthnResponse.php57
-rw-r--r--lib/SimpleSAML/XML/Signer.php6
-rw-r--r--lib/SimpleSAML/XML/Validator.php129
52 files changed, 3046 insertions, 2161 deletions
diff --git a/lib/SimpleSAML/Auth/Default.php b/lib/SimpleSAML/Auth/Default.php
index e3687bd..0498554 100644
--- a/lib/SimpleSAML/Auth/Default.php
+++ b/lib/SimpleSAML/Auth/Default.php
@@ -21,11 +21,11 @@ class SimpleSAML_Auth_Default {
* @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().
+ * \SimpleSAML\Utils\HTTP::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().
+ * to check it by calling \SimpleSAML\Utils\HTTP::checkURLAllowed().
* @param array $params Extra information about the login. Different
* authentication requestors may provide different information. Optional,
* will default to an empty array.
@@ -128,7 +128,7 @@ class SimpleSAML_Auth_Default {
if (is_string($return)) {
/* Redirect... */
- SimpleSAML_Utilities::redirectTrustedURL($return);
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($return);
} else {
call_user_func($return, $state);
assert('FALSE');
@@ -146,7 +146,7 @@ class SimpleSAML_Auth_Default {
* @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.
+ * \SimpleSAML\Utils\HTTP::checkURLAllowed() for more information.
* @param string $authority The authentication source we are logging
* out from.
*/
@@ -181,7 +181,7 @@ class SimpleSAML_Auth_Default {
* @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.
+ * \SimpleSAML\Utils\HTTP::checkURLAllowed() for more information.
* @param string|NULL $authority The authentication source we are logging
* out from.
* @return void This function never returns.
@@ -193,7 +193,7 @@ class SimpleSAML_Auth_Default {
self::initLogoutReturn($returnURL, $authority);
/* Redirect... */
- SimpleSAML_Utilities::redirectTrustedURL($returnURL);
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($returnURL);
}
@@ -211,7 +211,7 @@ class SimpleSAML_Auth_Default {
$returnURL = $state['SimpleSAML_Auth_Default.ReturnURL'];
/* Redirect... */
- SimpleSAML_Utilities::redirectTrustedURL($returnURL);
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($returnURL);
}
@@ -265,7 +265,7 @@ class SimpleSAML_Auth_Default {
$session = SimpleSAML_Session::getSessionFromRequest();
$session->doLogin($authId, self::extractPersistentAuthState($state));
- SimpleSAML_Utilities::redirectUntrustedURL($redirectTo);
+ \SimpleSAML\Utils\HTTP::redirectUntrustedURL($redirectTo);
}
}
diff --git a/lib/SimpleSAML/Auth/LDAP.php b/lib/SimpleSAML/Auth/LDAP.php
index 24e0a28..50cd5da 100644
--- a/lib/SimpleSAML/Auth/LDAP.php
+++ b/lib/SimpleSAML/Auth/LDAP.php
@@ -261,7 +261,7 @@ class SimpleSAML_Auth_LDAP {
public function searchfordn($base, $attribute, $value, $allowZeroHits = FALSE) {
// Traverse all search bases, returning DN if found.
- $bases = SimpleSAML_Utilities::arrayize($base);
+ $bases = SimpleSAML\Utils\Arrays::arrayize($base);
$result = NULL;
foreach ($bases AS $current) {
try {
@@ -586,7 +586,7 @@ class SimpleSAML_Auth_LDAP {
$dn = $this->searchfordn($config['searchbase'], $config['searchattributes'], $username);
}
- if ($password != null) { /* checking users credentials ... assuming below that she may read her own attributes ... */
+ if ($password !== null) { /* checking users credentials ... assuming below that she may read her own attributes ... */
if (!$this->bind($dn, $password)) {
SimpleSAML_Logger::info('Library - LDAP validate(): Failed to authenticate \''. $username . '\' using DN \'' . $dn . '\'');
return FALSE;
diff --git a/lib/SimpleSAML/Auth/ProcessingChain.php b/lib/SimpleSAML/Auth/ProcessingChain.php
index da75fcc..b034220 100644
--- a/lib/SimpleSAML/Auth/ProcessingChain.php
+++ b/lib/SimpleSAML/Auth/ProcessingChain.php
@@ -247,7 +247,7 @@ class SimpleSAML_Auth_ProcessingChain {
* in $state['ReturnURL'].
*/
$id = SimpleSAML_Auth_State::saveState($state, self::COMPLETED_STAGE);
- SimpleSAML_Utilities::redirectTrustedURL($state['ReturnURL'], array(self::AUTHPARAM => $id));
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($state['ReturnURL'], array(self::AUTHPARAM => $id));
} else {
/* Pass the state to the function defined in $state['ReturnCall']. */
@@ -302,7 +302,7 @@ class SimpleSAML_Auth_ProcessingChain {
* Retrieve a state which has finished processing.
*
* @param string $id The state identifier.
- * @see SimpleSAML_Utilities::parseStateID()
+ * @see SimpleSAML_Auth_State::parseStateID()
* @return Array The state referenced by the $id parameter.
*/
public static function fetchProcessedState($id) {
diff --git a/lib/SimpleSAML/Auth/Simple.php b/lib/SimpleSAML/Auth/Simple.php
index da0881d..a82419f 100644
--- a/lib/SimpleSAML/Auth/Simple.php
+++ b/lib/SimpleSAML/Auth/Simple.php
@@ -110,11 +110,11 @@ class SimpleSAML_Auth_Simple {
} else if (array_key_exists('ReturnCallback', $params)) {
$returnTo = (array)$params['ReturnCallback'];
} else {
- $returnTo = SimpleSAML_Utilities::selfURL();
+ $returnTo = \SimpleSAML\Utils\HTTP::getSelfURL();
}
if (is_string($returnTo) && $keepPost && $_SERVER['REQUEST_METHOD'] === 'POST') {
- $returnTo = SimpleSAML_Utilities::createPostRedirectLink($returnTo, $_POST);
+ $returnTo = \SimpleSAML\Utils\HTTP::getPOSTRedirectURL($returnTo, $_POST);
}
if (array_key_exists('ErrorURL', $params)) {
@@ -159,7 +159,7 @@ class SimpleSAML_Auth_Simple {
assert('is_array($params) || is_string($params) || is_null($params)');
if ($params === NULL) {
- $params = SimpleSAML_Utilities::selfURL();
+ $params = \SimpleSAML\Utils\HTTP::getSelfURL();
}
if (is_string($params)) {
@@ -217,8 +217,7 @@ class SimpleSAML_Auth_Simple {
$stateID = SimpleSAML_Auth_State::saveState($state, $state['ReturnStateStage']);
$params[$state['ReturnStateParam']] = $stateID;
}
-
- SimpleSAML_Utilities::redirectTrustedURL($state['ReturnTo'], $params);
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($state['ReturnTo'], $params);
}
}
@@ -290,7 +289,7 @@ class SimpleSAML_Auth_Simple {
assert('is_null($returnTo) || is_string($returnTo)');
if ($returnTo === NULL) {
- $returnTo = SimpleSAML_Utilities::selfURL();
+ $returnTo = \SimpleSAML\Utils\HTTP::getSelfURL();
}
$login = SimpleSAML_Module::getModuleURL('core/as_login.php', array(
@@ -313,7 +312,7 @@ class SimpleSAML_Auth_Simple {
assert('is_null($returnTo) || is_string($returnTo)');
if ($returnTo === NULL) {
- $returnTo = SimpleSAML_Utilities::selfURL();
+ $returnTo = \SimpleSAML\Utils\HTTP::getSelfURL();
}
$logout = SimpleSAML_Module::getModuleURL('core/as_logout.php', array(
diff --git a/lib/SimpleSAML/Auth/State.php b/lib/SimpleSAML/Auth/State.php
index 4684f5d..4f5e263 100644
--- a/lib/SimpleSAML/Auth/State.php
+++ b/lib/SimpleSAML/Auth/State.php
@@ -105,7 +105,7 @@ class SimpleSAML_Auth_State {
assert('is_bool($rawId)');
if (!array_key_exists(self::ID, $state)) {
- $state[self::ID] = SimpleSAML_Utilities::generateID();
+ $state[self::ID] = SimpleSAML\Utils\Random::generateID();
}
$id = $state[self::ID];
@@ -210,7 +210,7 @@ class SimpleSAML_Auth_State {
assert('is_bool($allowMissing)');
SimpleSAML_Logger::debug('Loading state: ' . var_export($id, TRUE));
- $sid = SimpleSAML_Utilities::parseStateID($id);
+ $sid = self::parseStateID($id);
$session = SimpleSAML_Session::getSessionFromRequest();
$state = $session->getData('SimpleSAML_Auth_State', $sid['id']);
@@ -225,7 +225,7 @@ class SimpleSAML_Auth_State {
throw new SimpleSAML_Error_NoState();
}
- SimpleSAML_Utilities::redirectUntrustedURL($sid['url']);
+ \SimpleSAML\Utils\HTTP::redirectUntrustedURL($sid['url']);
}
$state = unserialize($state);
@@ -249,7 +249,7 @@ class SimpleSAML_Auth_State {
throw new Exception($msg);
}
- SimpleSAML_Utilities::redirectUntrustedURL($sid['url']);
+ \SimpleSAML\Utils\HTTP::redirectUntrustedURL($sid['url']);
}
return $state;
@@ -294,7 +294,7 @@ class SimpleSAML_Auth_State {
$id = self::saveState($state, self::EXCEPTION_STAGE);
/* Redirect to the exception handler. */
- SimpleSAML_Utilities::redirectTrustedURL($state[self::EXCEPTION_HANDLER_URL], array(self::EXCEPTION_PARAM => $id));
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($state[self::EXCEPTION_HANDLER_URL], array(self::EXCEPTION_PARAM => $id));
} elseif (array_key_exists(self::EXCEPTION_HANDLER_FUNC, $state)) {
/* Call the exception handler. */
@@ -337,4 +337,26 @@ class SimpleSAML_Auth_State {
return $state;
}
+
+ /**
+ * 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.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ 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);
+ }
+
}
diff --git a/lib/SimpleSAML/Auth/TimeLimitedToken.php b/lib/SimpleSAML/Auth/TimeLimitedToken.php
index 3c991ce..5a59b7a 100644
--- a/lib/SimpleSAML/Auth/TimeLimitedToken.php
+++ b/lib/SimpleSAML/Auth/TimeLimitedToken.php
@@ -14,7 +14,7 @@ class SimpleSAML_Auth_TimeLimitedToken {
*/
public function __construct( $lifetime = 900, $secretSalt = NULL, $skew = 1) {
if ($secretSalt === NULL) {
- $secretSalt = SimpleSAML_Utilities::getSecretSalt();
+ $secretSalt = SimpleSAML\Utils\Config::getSecretSalt();
}
$this->secretSalt = $secretSalt;
@@ -39,9 +39,6 @@ class SimpleSAML_Auth_TimeLimitedToken {
* Calculate the given time slot for a given offset.
*/
private function calculate_time_slot($offset) {
-
- #echo 'lifetime is: ' . $this->lifetime;
-
$timeslot = floor( (time() - $offset) / ($this->lifetime + $this->skew) );
return $timeslot;
}
@@ -51,10 +48,6 @@ class SimpleSAML_Auth_TimeLimitedToken {
*/
private function calculate_tokenvalue($offset) {
// A secret salt that should be randomly generated for each installation.
- #echo 'Secret salt is: ' . $this->secretSalt;
-
- #echo '<p>Calculating sha1( ' . $this->calculate_time_slot($offset) . ':' . $this->secretSalt . ' )<br />';
-
return sha1( $this->calculate_time_slot($offset) . ':' . $this->secretSalt);
}
@@ -74,10 +67,6 @@ class SimpleSAML_Auth_TimeLimitedToken {
$splittedtoken = explode('-', $token);
$offset = hexdec($splittedtoken[0]);
$value = $splittedtoken[1];
-
-
- #echo 'compare [' . $this->calculate_tokenvalue($offset). '] with [' . $value . '] offset was [' . $offset. ']';
-
return ($this->calculate_tokenvalue($offset) === $value);
}
diff --git a/lib/SimpleSAML/Bindings/Shib13/Artifact.php b/lib/SimpleSAML/Bindings/Shib13/Artifact.php
index 4eda10b..0b27c03 100644
--- a/lib/SimpleSAML/Bindings/Shib13/Artifact.php
+++ b/lib/SimpleSAML/Bindings/Shib13/Artifact.php
@@ -48,9 +48,9 @@ class SimpleSAML_Bindings_Shib13_Artifact {
$msg = '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">' .
'<SOAP-ENV:Body>' .
'<samlp:Request xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol"' .
- ' RequestID="' . SimpleSAML_Utilities::generateID() . '"' .
+ ' RequestID="' . SimpleSAML\Utils\Random::generateID() . '"' .
' MajorVersion="1" MinorVersion="1"' .
- ' IssueInstant="' . SimpleSAML_Utilities::generateTimestamp() . '"' .
+ ' IssueInstant="' . SimpleSAML\Utils\Time::generateTimestamp() . '"' .
'>';
foreach ($artifacts as $a) {
@@ -80,18 +80,18 @@ class SimpleSAML_Bindings_Shib13_Artifact {
}
$soapEnvelope = $doc->firstChild;
- if (!SimpleSAML_Utilities::isDOMElementOfType($soapEnvelope, 'Envelope', 'http://schemas.xmlsoap.org/soap/envelope/')) {
+ if (!SimpleSAML\Utils\XML::isDOMElementOfType($soapEnvelope, 'Envelope', 'http://schemas.xmlsoap.org/soap/envelope/')) {
throw new SimpleSAML_Error_Exception('Expected artifact response to contain a <soap:Envelope> element.');
}
- $soapBody = SimpleSAML_Utilities::getDOMChildren($soapEnvelope, 'Body', 'http://schemas.xmlsoap.org/soap/envelope/');
+ $soapBody = SimpleSAML\Utils\XML::getDOMChildren($soapEnvelope, 'Body', 'http://schemas.xmlsoap.org/soap/envelope/');
if (count($soapBody) === 0) {
throw new SimpleSAML_Error_Exception('Couldn\'t find <soap:Body> in <soap:Envelope>.');
}
$soapBody = $soapBody[0];
- $responseElement = SimpleSAML_Utilities::getDOMChildren($soapBody, 'Response', 'urn:oasis:names:tc:SAML:1.0:protocol');
+ $responseElement = SimpleSAML\Utils\XML::getDOMChildren($soapBody, 'Response', 'urn:oasis:names:tc:SAML:1.0:protocol');
if (count($responseElement) === 0) {
throw new SimpleSAML_Error_Exception('Couldn\'t find <saml1p:Response> in <soap:Body>.');
}
@@ -121,7 +121,7 @@ class SimpleSAML_Bindings_Shib13_Artifact {
$artifacts = self::getArtifacts();
$request = self::buildRequest($artifacts);
- SimpleSAML_Utilities::debugMessage($msgStr, 'out');
+ \SimpleSAML\Utils\XML::debugSAMLMessage($request, 'out');
$url = $idpMetadata->getDefaultEndpoint('ArtifactResolutionService', array('urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding'));
$url = $url['Location'];
@@ -137,12 +137,12 @@ class SimpleSAML_Bindings_Shib13_Artifact {
"-----END CERTIFICATE-----\n";
}
- $file = SimpleSAML_Utilities::getTempDir() . '/' . sha1($certData) . '.crt';
+ $file = SimpleSAML\Utils\System::getTempDir() . DIRECTORY_SEPARATOR . sha1($certData) . '.crt';
if (!file_exists($file)) {
- SimpleSAML_Utilities::writeFile($file, $certData);
+ SimpleSAML\Utils\System::writeFile($file, $certData);
}
- $spKeyCertFile = SimpleSAML_Utilities::resolveCert($spMetadata->getString('privatekey'));
+ $spKeyCertFile = \SimpleSAML\Utils\Config::getCertPath($spMetadata->getString('privatekey'));
$opts = array(
'ssl' => array(
@@ -161,12 +161,12 @@ class SimpleSAML_Bindings_Shib13_Artifact {
);
/* Fetch the artifact. */
- $response = SimpleSAML_Utilities::fetch($url, $opts);
+ $response = \SimpleSAML\Utils\HTTP::fetch($url, $opts);
if ($response === FALSE) {
throw new SimpleSAML_Error_Exception('Failed to retrieve assertion from IdP.');
}
- SimpleSAML_Utilities::debugMessage($response, 'in');
+ \SimpleSAML\Utils\XML::debugSAMLMessage($response, 'in');
/* Find the response in the SOAP message. */
$response = self::extractResponse($response);
diff --git a/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php b/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php
index f9f5d8a..c97cf89 100644
--- a/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php
+++ b/lib/SimpleSAML/Bindings/Shib13/HTTPPost.php
@@ -3,7 +3,7 @@
/**
* Implementation of the Shibboleth 1.3 HTTP-POST binding.
*
- * @author Andreas Åkre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
+ * @author Andreas Ã…kre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
* @package simpleSAMLphp
*/
class SimpleSAML_Bindings_Shib13_HTTPPost {
@@ -27,10 +27,10 @@ class SimpleSAML_Bindings_Shib13_HTTPPost {
*/
public function sendResponse($response, SimpleSAML_Configuration $idpmd, SimpleSAML_Configuration $spmd, $relayState, $shire) {
- SimpleSAML_Utilities::validateXMLDocument($response, 'saml11');
+ \SimpleSAML\Utils\XML::checkSAMLMessage($response, 'saml11');
- $privatekey = SimpleSAML_Utilities::loadPrivateKey($idpmd, TRUE);
- $publickey = SimpleSAML_Utilities::loadPublicKey($idpmd, TRUE);
+ $privatekey = SimpleSAML\Utils\Crypto::loadPrivateKey($idpmd, TRUE);
+ $publickey = SimpleSAML\Utils\Crypto::loadPublicKey($idpmd, TRUE);
$responsedom = new DOMDocument();
$responsedom->loadXML(str_replace ("\r", "", $response));
@@ -67,7 +67,7 @@ class SimpleSAML_Bindings_Shib13_HTTPPost {
if ($signResponse) {
/* Sign the response - this must be done after encrypting the assertion. */
/* We insert the signature before the saml2p:Status element. */
- $statusElements = SimpleSAML_Utilities::getDOMChildren($responseroot, 'Status', '@saml1p');
+ $statusElements = SimpleSAML\Utils\XML::getDOMChildren($responseroot, 'Status', '@saml1p');
assert('count($statusElements) === 1');
$signer->sign($responseroot, $responseroot, $statusElements[0]);
@@ -78,9 +78,9 @@ class SimpleSAML_Bindings_Shib13_HTTPPost {
$response = $responsedom->saveXML();
- SimpleSAML_Utilities::debugMessage($response, 'out');
+ \SimpleSAML\Utils\XML::debugSAMLMessage($response, 'out');
- SimpleSAML_Utilities::postRedirect($shire, array(
+ \SimpleSAML\Utils\HTTP::submitPOSTData($shire, array(
'TARGET' => $relayState,
'SAMLResponse' => base64_encode($response),
));
@@ -103,9 +103,9 @@ class SimpleSAML_Bindings_Shib13_HTTPPost {
$rawResponse = $post['SAMLResponse'];
$samlResponseXML = base64_decode($rawResponse);
- SimpleSAML_Utilities::debugMessage($samlResponseXML, 'in');
+ \SimpleSAML\Utils\XML::debugSAMLMessage($samlResponseXML, 'in');
- SimpleSAML_Utilities::validateXMLDocument($samlResponseXML, 'saml11');
+ \SimpleSAML\Utils\XML::checkSAMLMessage($samlResponseXML, 'saml11');
$samlResponse = new SimpleSAML_XML_Shib13_AuthnResponse();
$samlResponse->setXML($samlResponseXML);
diff --git a/lib/SimpleSAML/Configuration.php b/lib/SimpleSAML/Configuration.php
index 40941d5..deff4a6 100644
--- a/lib/SimpleSAML/Configuration.php
+++ b/lib/SimpleSAML/Configuration.php
@@ -111,7 +111,7 @@ class SimpleSAML_Configuration {
$host = $_SERVER['HTTP_HOST'];
if (array_key_exists($host, $config['override.host'])) {
$ofs = $config['override.host'][$host];
- foreach (SimpleSAML_Utilities::arrayize($ofs) AS $of) {
+ foreach (SimpleSAML\Utils\Arrays::arrayize($ofs) AS $of) {
$overrideFile = dirname($filename) . '/' . $of;
if (!file_exists($overrideFile)) {
throw new Exception('Config file [' . $filename . '] requests override for host ' . $host . ' but file does not exists [' . $of . ']');
@@ -346,7 +346,7 @@ class SimpleSAML_Configuration {
if (preg_match('/^\*(.*)$/D', $baseURL, $matches)) {
/* deprecated behaviour, will be removed in the future */
- return SimpleSAML_Utilities::getFirstPathElement(false) . $matches[1];
+ return \SimpleSAML\Utils\HTTP::getFirstPathElement(false) . $matches[1];
}
if (preg_match('#^https?://[^/]*/(.*)$#', $baseURL, $matches)) {
@@ -1020,7 +1020,7 @@ class SimpleSAML_Configuration {
$endpoints = $this->getEndpoints($endpointType);
- $defaultEndpoint = SimpleSAML_Utilities::getDefaultEndpoint($endpoints, $bindings);
+ $defaultEndpoint = \SimpleSAML\Utils\Config\Metadata::getDefaultEndpoint($endpoints, $bindings);
if ($defaultEndpoint !== NULL) {
return $defaultEndpoint;
}
@@ -1118,7 +1118,7 @@ class SimpleSAML_Configuration {
);
} elseif ($this->hasValue($prefix . 'certificate')) {
$file = $this->getString($prefix . 'certificate');
- $file = SimpleSAML_Utilities::resolveCert($file);
+ $file = \SimpleSAML\Utils\Config::getCertPath($file);
$data = @file_get_contents($file);
if ($data === FALSE) {
diff --git a/lib/SimpleSAML/Error/Error.php b/lib/SimpleSAML/Error/Error.php
index a08ffdd..a276d8f 100644
--- a/lib/SimpleSAML/Error/Error.php
+++ b/lib/SimpleSAML/Error/Error.php
@@ -202,7 +202,7 @@ class SimpleSAML_Error_Error extends SimpleSAML_Error_Exception {
$emsg = array_shift($data);
$etrace = implode("\n", $data);
- $reportId = SimpleSAML_Utilities::stringToHex(SimpleSAML_Utilities::generateRandomBytes(4));
+ $reportId = bin2hex(openssl_random_pseudo_bytes(4));
SimpleSAML_Logger::error('Error report with id ' . $reportId . ' generated.');
$config = SimpleSAML_Configuration::getInstance();
@@ -226,7 +226,7 @@ class SimpleSAML_Error_Error extends SimpleSAML_Error_Exception {
'exceptionTrace' => $etrace,
'reportId' => $reportId,
'trackId' => $session->getTrackID(),
- 'url' => SimpleSAML_Utilities::selfURLNoQuery(),
+ 'url' => \SimpleSAML\Utils\HTTP::getSelfURLNoQuery(),
'version' => $config->getVersion(),
'referer' => $referer,
);
@@ -265,7 +265,7 @@ class SimpleSAML_Error_Error extends SimpleSAML_Error_Exception {
if($config->getBoolean('errorreporting', TRUE) &&
$config->getString('technicalcontact_email', 'na@example.org') !== 'na@example.org') {
/* Enable error reporting. */
- $baseurl = SimpleSAML_Utilities::getBaseURL();
+ $baseurl = \SimpleSAML\Utils\HTTP::getBaseURL();
$data['errorReportAddress'] = $baseurl . 'errorreport.php';
}
diff --git a/lib/SimpleSAML/Error/NotFound.php b/lib/SimpleSAML/Error/NotFound.php
index cb868e8..251ff19 100644
--- a/lib/SimpleSAML/Error/NotFound.php
+++ b/lib/SimpleSAML/Error/NotFound.php
@@ -27,7 +27,7 @@ class SimpleSAML_Error_NotFound extends SimpleSAML_Error_Error {
assert('is_null($reason) || is_string($reason)');
- $url = SimpleSAML_Utilities::selfURL();
+ $url = \SimpleSAML\Utils\HTTP::getSelfURL();
if($reason === NULL) {
parent::__construct(array('NOTFOUND', '%URL%' => $url));
diff --git a/lib/SimpleSAML/IdP.php b/lib/SimpleSAML/IdP.php
index e5566b8..7ba6193 100644
--- a/lib/SimpleSAML/IdP.php
+++ b/lib/SimpleSAML/IdP.php
@@ -531,7 +531,7 @@ class SimpleSAML_IdP {
public static function finishLogoutRedirect(SimpleSAML_IdP $idp, array $state) {
assert('isset($state["core:Logout:URL"])');
- SimpleSAML_Utilities::redirectTrustedURL($state['core:Logout:URL']);
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($state['core:Logout:URL']);
assert('FALSE');
}
diff --git a/lib/SimpleSAML/IdP/LogoutIFrame.php b/lib/SimpleSAML/IdP/LogoutIFrame.php
index 44c3b3d..e7fdc6e 100644
--- a/lib/SimpleSAML/IdP/LogoutIFrame.php
+++ b/lib/SimpleSAML/IdP/LogoutIFrame.php
@@ -48,7 +48,7 @@ class SimpleSAML_IdP_LogoutIFrame extends SimpleSAML_IdP_LogoutHandler {
}
$url = SimpleSAML_Module::getModuleURL('core/idp/logout-iframe.php', $params);
- SimpleSAML_Utilities::redirectTrustedURL($url);
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($url);
}
diff --git a/lib/SimpleSAML/IdP/LogoutTraditional.php b/lib/SimpleSAML/IdP/LogoutTraditional.php
index 4cd16dd..7632cab 100644
--- a/lib/SimpleSAML/IdP/LogoutTraditional.php
+++ b/lib/SimpleSAML/IdP/LogoutTraditional.php
@@ -29,7 +29,7 @@ class SimpleSAML_IdP_LogoutTraditional extends SimpleSAML_IdP_LogoutHandler {
try {
$idp = SimpleSAML_IdP::getByState($association);
$url = call_user_func(array($association['Handler'], 'getLogoutURL'), $idp, $association, $relayState);
- SimpleSAML_Utilities::redirectTrustedURL($url);
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($url);
} catch (Exception $e) {
SimpleSAML_Logger::warning('Unable to initialize logout to ' . var_export($id, TRUE) . '.');
$this->idp->terminateAssociation($id);
diff --git a/lib/SimpleSAML/Logger/LoggingHandlerFile.php b/lib/SimpleSAML/Logger/LoggingHandlerFile.php
index 9ed795e..feab437 100644
--- a/lib/SimpleSAML/Logger/LoggingHandlerFile.php
+++ b/lib/SimpleSAML/Logger/LoggingHandlerFile.php
@@ -56,7 +56,7 @@ class SimpleSAML_Logger_LoggingHandlerFile implements SimpleSAML_Logger_LoggingH
}
}
- SimpleSAML_Utilities::initTimezone();
+ SimpleSAML\Utils\Time::initTimezone();
}
diff --git a/lib/SimpleSAML/Logger/LoggingHandlerSyslog.php b/lib/SimpleSAML/Logger/LoggingHandlerSyslog.php
index f6d58b1..6b8abef 100644
--- a/lib/SimpleSAML/Logger/LoggingHandlerSyslog.php
+++ b/lib/SimpleSAML/Logger/LoggingHandlerSyslog.php
@@ -27,7 +27,7 @@ class SimpleSAML_Logger_LoggingHandlerSyslog implements SimpleSAML_Logger_Loggin
$processname = $config->getString('logging.processname', 'simpleSAMLphp');
// Setting facility to LOG_USER (only valid in Windows), enable log level rewrite on windows systems.
- if (SimpleSAML_Utilities::isWindowsOS()) {
+ if (SimpleSAML\Utils\System::getOS() === SimpleSAML\Utils\System::WINDOWS) {
$this->isWindows = TRUE;
$facility = LOG_USER;
}
diff --git a/lib/SimpleSAML/Memcache.php b/lib/SimpleSAML/Memcache.php
index 33d4584..791f43a 100644
--- a/lib/SimpleSAML/Memcache.php
+++ b/lib/SimpleSAML/Memcache.php
@@ -403,7 +403,7 @@ class SimpleSAML_Memcache {
throw new Exception('Failed to get memcache server status.');
}
- $stats = SimpleSAML_Utilities::transposeArray($stats);
+ $stats = SimpleSAML\Utils\Arrays::transpose($stats);
$ret = array_merge_recursive($ret, $stats);
}
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
index 3e08619..aa93416 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php
@@ -89,7 +89,7 @@ class SimpleSAML_Metadata_MetaDataStorageHandler {
$config = SimpleSAML_Configuration::getInstance();
assert($config instanceof SimpleSAML_Configuration);
- $baseurl = SimpleSAML_Utilities::selfURLhost() . '/' .
+ $baseurl = \SimpleSAML\Utils\HTTP::getSelfURLHost() . '/' .
$config->getBaseURL();
if ($set == 'saml20-sp-hosted') {
@@ -144,7 +144,7 @@ class SimpleSAML_Metadata_MetaDataStorageHandler {
unset($srcList[$key]);
SimpleSAML_Logger::warning("Dropping metadata entity " .
var_export($key,true) . ", expired " .
- SimpleSAML_Utilities::generateTimestamp($le['expire']) .
+ SimpleSAML\Utils\Time::generateTimestamp($le['expire']) .
".");
}
}
@@ -187,7 +187,7 @@ class SimpleSAML_Metadata_MetaDataStorageHandler {
assert('is_string($set)');
/* First we look for the hostname/path combination. */
- $currenthostwithpath = SimpleSAML_Utilities::getSelfHostWithPath(); // sp.example.org/university
+ $currenthostwithpath = \SimpleSAML\Utils\HTTP::getSelfHostWithPath(); // sp.example.org/university
foreach($this->sources as $source) {
$index = $source->getEntityIdFromHostPath($currenthostwithpath, $set, $type);
@@ -198,7 +198,7 @@ class SimpleSAML_Metadata_MetaDataStorageHandler {
/* Then we look for the hostname. */
- $currenthost = SimpleSAML_Utilities::getSelfHost(); // sp.example.org
+ $currenthost = \SimpleSAML\Utils\HTTP::getSelfHost(); // sp.example.org
if(strpos($currenthost, ":") !== FALSE) {
$currenthostdecomposed = explode(":", $currenthost);
$currenthost = $currenthostdecomposed[0];
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php
index 22688c2..656426b 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php
@@ -116,16 +116,16 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerFlatFile extends SimpleSAML_Meta
private function generateDynamicHostedEntityID($set) {
/* Get the configuration. */
- $baseurl = SimpleSAML_Utilities::getBaseURL();
+ $baseurl = \SimpleSAML\Utils\HTTP::getBaseURL();
if ($set === 'saml20-idp-hosted') {
return $baseurl . 'saml2/idp/metadata.php';
} elseif($set === 'shib13-idp-hosted') {
return $baseurl . 'shib13/idp/metadata.php';
} elseif($set === 'wsfed-sp-hosted') {
- return 'urn:federation:' . SimpleSAML_Utilities::getSelfHost();
+ return 'urn:federation:' . \SimpleSAML\Utils\HTTP::getSelfHost();
} elseif($set === 'adfs-idp-hosted') {
- return 'urn:federation:' . SimpleSAML_Utilities::getSelfHost() . ':idp';
+ return 'urn:federation:' . \SimpleSAML\Utils\HTTP::getSelfHost() . ':idp';
} else {
throw new Exception('Can not generate dynamic EntityID for metadata of this type: [' . $set . ']');
}
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerMDX.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerMDX.php
index 7364a6f..7f61a62 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerMDX.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerMDX.php
@@ -140,8 +140,9 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerMDX extends SimpleSAML_Metadata_
$rawData = file_get_contents($cachefilename);
if (empty($rawData)) {
+ $error = error_get_last();
throw new Exception('Error reading metadata from cache file "' . $cachefilename . '": ' .
- SimpleSAML_Utilities::getLastError());
+ $error['message']);
}
$data = unserialize($rawData);
@@ -252,14 +253,15 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerMDX extends SimpleSAML_Metadata_
SimpleSAML_Logger::debug('MetaData - Handler.MDX: Downloading metadata for "'. $index .'" from [' . $mdx_url . ']' );
try {
- $xmldata = SimpleSAML_Utilities::fetch($mdx_url);
+ $xmldata = \SimpleSAML\Utils\HTTP::fetch($mdx_url);
} catch(Exception $e) {
SimpleSAML_Logger::warning('Fetching metadata for ' . $index . ': ' . $e->getMessage());
}
if (empty($xmldata)) {
+ $error = error_get_last();
throw new Exception('Error downloading metadata for "'. $index .'" from "' . $mdx_url . '": ' .
- SimpleSAML_Utilities::getLastError());
+ $error['message']);
}
$entity = SimpleSAML_Metadata_SAMLParser::parseString($xmldata);
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php
index 3415cd5..fae34c9 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSerialize.php
@@ -164,8 +164,9 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerSerialize extends SimpleSAML_Met
$data = @file_get_contents($filePath);
if ($data === FALSE) {
+ $error = error_get_last();
SimpleSAML_Logger::warning('Error reading file ' . $filePath .
- ': ' . SimpleSAML_Utilities::getLastError());
+ ': ' . $error['message']);
return NULL;
}
@@ -199,8 +200,9 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerSerialize extends SimpleSAML_Met
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 .
- ': ' . SimpleSAML_Utilities::getLastError());
+ ': ' . $error['message']);
return FALSE;
}
}
@@ -211,15 +213,17 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerSerialize extends SimpleSAML_Met
$res = file_put_contents($newPath, $data);
if ($res === FALSE) {
+ $error = error_get_last();
SimpleSAML_Logger::error('Error saving file ' . $newPath .
- ': ' . SimpleSAML_Utilities::getLastError());
+ ': ' . $error['message']);
return FALSE;
}
$res = rename($newPath, $filePath);
if ($res === FALSE) {
+ $error = error_get_last();
SimpleSAML_Logger::error('Error renaming ' . $newPath . ' to ' . $filePath .
- ': ' . SimpleSAML_Utilities::getLastError());
+ ': ' . $error['message']);
return FALSE;
}
@@ -248,8 +252,9 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerSerialize extends SimpleSAML_Met
$res = unlink($filePath);
if ($res === FALSE) {
+ $error = error_get_last();
SimpleSAML_Logger::error('Failed to delete file ' . $filePath .
- ': ' . SimpleSAML_Utilities::getLastError());
+ ': ' . $error['message']);
}
}
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
index 549d920..1fc1160 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
@@ -152,7 +152,7 @@ abstract class SimpleSAML_Metadata_MetaDataStorageSource {
if(!is_array($entry['hint.cidr'])) continue;
foreach ($entry['hint.cidr'] AS $hint_entry) {
- if (SimpleSAML_Utilities::ipCIDRcheck($hint_entry, $ip)) {
+ if (SimpleSAML\Utils\Net::ipCIDRcheck($hint_entry, $ip)) {
if ($type === 'entityid') {
return $entry['entityid'];
} else {
@@ -178,7 +178,7 @@ abstract class SimpleSAML_Metadata_MetaDataStorageSource {
$metadataSet = $this->getMetadataSet($set);
/* Check for hostname. */
- $currenthost = SimpleSAML_Utilities::getSelfHost(); // sp.example.org
+ $currenthost = \SimpleSAML\Utils\HTTP::getSelfHost(); // sp.example.org
if(strpos($currenthost, ":") !== FALSE) {
$currenthostdecomposed = explode(":", $currenthost);
$currenthost = $currenthostdecomposed[0];
diff --git a/lib/SimpleSAML/Metadata/SAMLBuilder.php b/lib/SimpleSAML/Metadata/SAMLBuilder.php
index f684d82..703d28c 100644
--- a/lib/SimpleSAML/Metadata/SAMLBuilder.php
+++ b/lib/SimpleSAML/Metadata/SAMLBuilder.php
@@ -78,7 +78,7 @@ class SimpleSAML_Metadata_SAMLBuilder {
$xml = $this->getEntityDescriptor();
if ($formatted) {
- SimpleSAML_Utilities::formatDOMElement($xml);
+ SimpleSAML\Utils\XML::formatDOMElement($xml);
}
return $xml->ownerDocument->saveXML();
@@ -277,9 +277,9 @@ class SimpleSAML_Metadata_SAMLBuilder {
return;
}
- $orgName = SimpleSAML_Utilities::arrayize($metadata['OrganizationName'], 'en');
- $orgDisplayName = SimpleSAML_Utilities::arrayize($metadata['OrganizationDisplayName'], 'en');
- $orgURL = SimpleSAML_Utilities::arrayize($metadata['OrganizationURL'], 'en');
+ $orgName = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationName'], 'en');
+ $orgDisplayName = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationDisplayName'], 'en');
+ $orgURL = SimpleSAML\Utils\Arrays::arrayize($metadata['OrganizationURL'], 'en');
$this->addOrganization($orgName, $orgDisplayName, $orgURL);
}
@@ -441,6 +441,15 @@ class SimpleSAML_Metadata_SAMLBuilder {
$e = new SAML2_XML_md_SPSSODescriptor();
$e->protocolSupportEnumeration = $protocols;
+ if ($metadata->hasValue('saml20.sign.assertion')) {
+ $e->WantAssertionsSigned = $metadata->getBoolean('saml20.sign.assertion');
+ }
+
+ if ($metadata->hasValue('redirect.validate')) {
+ $e->AuthnRequestsSigned = $metadata->getBoolean('redirect.validate');
+ } elseif ($metadata->hasValue('validate.authnrequest')) {
+ $e->AuthnRequestsSigned = $metadata->getBoolean('validate.authnrequest');
+ }
$this->addExtensions($metadata, $e);
@@ -465,7 +474,7 @@ class SimpleSAML_Metadata_SAMLBuilder {
foreach ($metadata->getArray('contacts', array()) as $contact) {
if (array_key_exists('contactType', $contact) && array_key_exists('emailAddress', $contact)) {
- $this->addContact($contact['contactType'], SimpleSAML_Utils_Config_Metadata::getContact($contact));
+ $this->addContact($contact['contactType'], \SimpleSAML\Utils\Config\Metadata::getContact($contact));
}
}
@@ -511,7 +520,7 @@ class SimpleSAML_Metadata_SAMLBuilder {
foreach ($metadata->getArray('contacts', array()) as $contact) {
if (array_key_exists('contactType', $contact) && array_key_exists('emailAddress', $contact)) {
- $this->addContact($contact['contactType'], SimpleSAML_Utils_Config_Metadata::getContact($contact));
+ $this->addContact($contact['contactType'], \SimpleSAML\Utils\Config\Metadata::getContact($contact));
}
}
@@ -624,7 +633,7 @@ class SimpleSAML_Metadata_SAMLBuilder {
assert('in_array($type, array("technical", "support", "administrative", "billing", "other"), TRUE)');
// TODO: remove this check as soon as getContact() is called always before calling this function.
- $details = SimpleSAML_Utils_Config_Metadata::getContact($details);
+ $details = \SimpleSAML\Utils\Config\Metadata::getContact($details);
$e = new SAML2_XML_md_ContactPerson();
$e->contactType = $type;
diff --git a/lib/SimpleSAML/Metadata/SAMLParser.php b/lib/SimpleSAML/Metadata/SAMLParser.php
index cc5b84e..c141a5c 100644
--- a/lib/SimpleSAML/Metadata/SAMLParser.php
+++ b/lib/SimpleSAML/Metadata/SAMLParser.php
@@ -178,7 +178,7 @@ class SimpleSAML_Metadata_SAMLParser {
public static function parseFile($file) {
$doc = new DOMDocument();
- $data = SimpleSAML_Utilities::fetch($file);
+ $data = \SimpleSAML\Utils\HTTP::fetch($file);
$res = $doc->loadXML($data);
if($res !== TRUE) {
@@ -248,7 +248,7 @@ class SimpleSAML_Metadata_SAMLParser {
if ($file === NULL) throw new Exception('Cannot open file NULL. File name not specified.');
- $data = SimpleSAML_Utilities::fetch($file);
+ $data = \SimpleSAML\Utils\HTTP::fetch($file);
$doc = new DOMDocument();
$res = $doc->loadXML($data);
@@ -297,9 +297,9 @@ class SimpleSAML_Metadata_SAMLParser {
assert('$element instanceof DOMElement');
- if(SimpleSAML_Utilities::isDOMElementOfType($element, 'EntityDescriptor', '@md') === TRUE) {
+ if (SimpleSAML\Utils\XML::isDOMElementOfType($element, 'EntityDescriptor', '@md') === TRUE) {
return self::processDescriptorsElement(new SAML2_XML_md_EntityDescriptor($element));
- } elseif(SimpleSAML_Utilities::isDOMElementOfType($element, 'EntitiesDescriptor', '@md') === TRUE) {
+ } elseif (SimpleSAML\Utils\XML::isDOMElementOfType($element, 'EntitiesDescriptor', '@md') === TRUE) {
return self::processDescriptorsElement(new SAML2_XML_md_EntitiesDescriptor($element));
} else {
throw new Exception('Unexpected root node: [' . $element->namespaceURI . ']:' .
@@ -1016,8 +1016,8 @@ class SimpleSAML_Metadata_SAMLParser {
$name = $attribute->getAttribute('Name');
$values = array_map(
- array('SimpleSAML_Utilities', 'getDOMText'),
- SimpleSAML_Utilities::getDOMChildren($attribute, 'AttributeValue', '@saml2')
+ array('SimpleSAML\Utils\XML', 'getDOMText'),
+ SimpleSAML\Utils\XML::getDOMChildren($attribute, 'AttributeValue', '@saml2')
);
if ($name === 'tags') {
@@ -1293,7 +1293,7 @@ class SimpleSAML_Metadata_SAMLParser {
throw new Exception('Failed to load SAML metadata from empty XML document.');
}
- if(SimpleSAML_Utilities::isDOMElementOfType($ed, 'EntityDescriptor', '@md') === FALSE) {
+ if (SimpleSAML\Utils\XML::isDOMElementOfType($ed, 'EntityDescriptor', '@md') === FALSE) {
throw new Exception('Expected first element in the metadata document to be an EntityDescriptor element.');
}
@@ -1311,7 +1311,7 @@ class SimpleSAML_Metadata_SAMLParser {
public function validateSignature($certificates) {
foreach ($certificates as $cert) {
assert('is_string($cert)');
- $certFile = SimpleSAML_Utilities::resolveCert($cert);
+ $certFile = \SimpleSAML\Utils\Config::getCertPath($cert);
if (!file_exists($certFile)) {
throw new Exception('Could not find certificate file [' . $certFile . '], which is needed to validate signature');
}
diff --git a/lib/SimpleSAML/Metadata/Signer.php b/lib/SimpleSAML/Metadata/Signer.php
index 51c29d3..a53201b 100644
--- a/lib/SimpleSAML/Metadata/Signer.php
+++ b/lib/SimpleSAML/Metadata/Signer.php
@@ -142,13 +142,13 @@ class SimpleSAML_Metadata_Signer {
$keyCertFiles = self::findKeyCert($config, $entityMetadata, $type);
- $keyFile = SimpleSAML_Utilities::resolveCert($keyCertFiles['privatekey']);
+ $keyFile = \SimpleSAML\Utils\Config::getCertPath($keyCertFiles['privatekey']);
if (!file_exists($keyFile)) {
throw new Exception('Could not find private key file [' . $keyFile . '], which is needed to sign the metadata');
}
$keyData = file_get_contents($keyFile);
- $certFile = SimpleSAML_Utilities::resolveCert($keyCertFiles['certificate']);
+ $certFile = \SimpleSAML\Utils\Config::getCertPath($keyCertFiles['certificate']);
if (!file_exists($certFile)) {
throw new Exception('Could not find certificate file [' . $certFile . '], which is needed to sign the metadata');
}
diff --git a/lib/SimpleSAML/Module.php b/lib/SimpleSAML/Module.php
index b5d8f02..45b1ae6 100644
--- a/lib/SimpleSAML/Module.php
+++ b/lib/SimpleSAML/Module.php
@@ -155,9 +155,9 @@ class SimpleSAML_Module {
assert('is_string($resource)');
assert('$resource[0] !== "/"');
- $url = SimpleSAML_Utilities::getBaseURL() . 'module.php/' . $resource;
+ $url = \SimpleSAML\Utils\HTTP::getBaseURL() . 'module.php/' . $resource;
if (!empty($parameters)) {
- $url = SimpleSAML_Utilities::addURLparameter($url, $parameters);
+ $url = \SimpleSAML\Utils\HTTP::addURLParameters($url, $parameters);
}
return $url;
}
diff --git a/lib/SimpleSAML/Session.php b/lib/SimpleSAML/Session.php
index 3f2af27..42f73b8 100644
--- a/lib/SimpleSAML/Session.php
+++ b/lib/SimpleSAML/Session.php
@@ -137,7 +137,7 @@ class SimpleSAML_Session
$sh = SimpleSAML_SessionHandler::getSessionHandler();
$this->sessionId = $sh->newSessionId();
- $this->trackid = SimpleSAML_Utilities::stringToHex(SimpleSAML_Utilities::generateRandomBytes(5));
+ $this->trackid = bin2hex(openssl_random_pseudo_bytes(5));
$this->dirty = true;
@@ -408,7 +408,7 @@ class SimpleSAML_Session
$this->authData[$authority] = $data;
- $this->authToken = SimpleSAML_Utilities::generateID();
+ $this->authToken = SimpleSAML\Utils\Random::generateID();
$sessionHandler = SimpleSAML_SessionHandler::getSessionHandler();
if (!$this->transient && (!empty($data['RememberMe']) || $this->rememberMeExpire) &&
diff --git a/lib/SimpleSAML/SessionHandler.php b/lib/SimpleSAML/SessionHandler.php
index 8ad3a13..9586d56 100644
--- a/lib/SimpleSAML/SessionHandler.php
+++ b/lib/SimpleSAML/SessionHandler.php
@@ -155,7 +155,7 @@ abstract class SimpleSAML_SessionHandler {
$params = $this->getCookieParams();
}
- SimpleSAML_Utilities::setCookie($name, $value, $params);
+ \SimpleSAML\Utils\HTTP::setCookie($name, $value, $params);
}
}
diff --git a/lib/SimpleSAML/SessionHandlerCookie.php b/lib/SimpleSAML/SessionHandlerCookie.php
index 60b033a..9e47a8a 100644
--- a/lib/SimpleSAML/SessionHandlerCookie.php
+++ b/lib/SimpleSAML/SessionHandlerCookie.php
@@ -93,7 +93,7 @@ extends SimpleSAML_SessionHandler {
* A random session id.
*/
private static function createSessionID() {
- return SimpleSAML_Utilities::stringToHex(SimpleSAML_Utilities::generateRandomBytes(16));
+ return bin2hex(openssl_random_pseudo_bytes(16));
}
diff --git a/lib/SimpleSAML/SessionHandlerPHP.php b/lib/SimpleSAML/SessionHandlerPHP.php
index afb62a6..9857d84 100644
--- a/lib/SimpleSAML/SessionHandlerPHP.php
+++ b/lib/SimpleSAML/SessionHandlerPHP.php
@@ -68,7 +68,7 @@ class SimpleSAML_SessionHandlerPHP extends SimpleSAML_SessionHandler {
public function newSessionId() {
$session_cookie_params = session_get_cookie_params();
- if ($session_cookie_params['secure'] && !SimpleSAML_Utilities::isHTTPS()) {
+ if ($session_cookie_params['secure'] && !\SimpleSAML\Utils\HTTP::isHTTPS()) {
throw new SimpleSAML_Error_Exception('Session start with secure cookie not allowed on http.');
}
@@ -77,7 +77,7 @@ class SimpleSAML_SessionHandlerPHP extends SimpleSAML_SessionHandler {
}
/* Generate new (secure) session id. */
- $sessionId = SimpleSAML_Utilities::stringToHex(SimpleSAML_Utilities::generateRandomBytes(16));
+ $sessionId = bin2hex(openssl_random_pseudo_bytes(16));
SimpleSAML_Session::createSession($sessionId);
if (session_id() !== '') {
@@ -105,7 +105,7 @@ class SimpleSAML_SessionHandlerPHP extends SimpleSAML_SessionHandler {
$session_cookie_params = session_get_cookie_params();
- if ($session_cookie_params['secure'] && !SimpleSAML_Utilities::isHTTPS()) {
+ if ($session_cookie_params['secure'] && !\SimpleSAML\Utils\HTTP::isHTTPS()) {
throw new SimpleSAML_Error_Exception('Session start with secure cookie not allowed on http.');
}
diff --git a/lib/SimpleSAML/Stats.php b/lib/SimpleSAML/Stats.php
index ec76a3f..acaf1d8 100644
--- a/lib/SimpleSAML/Stats.php
+++ b/lib/SimpleSAML/Stats.php
@@ -80,7 +80,7 @@ class SimpleSAML_Stats {
/* The ID generation is designed to cluster IDs related in time close together. */
$int_t = (int)$data['time'];
- $hd = SimpleSAML_Utilities::generateRandomBytes(16);
+ $hd = openssl_random_pseudo_bytes(16);
$data['_id'] = sprintf('%016x%s', $int_t, bin2hex($hd));
foreach (self::$outputs as $out) {
diff --git a/lib/SimpleSAML/Store.php b/lib/SimpleSAML/Store.php
index e45d4a7..2ea922d 100644
--- a/lib/SimpleSAML/Store.php
+++ b/lib/SimpleSAML/Store.php
@@ -47,9 +47,6 @@ abstract class SimpleSAML_Store {
self::$instance = new SimpleSAML_Store_SQL();
break;
default:
- if (strpos($storeType, ':') === FALSE) {
- throw new SimpleSAML_Error_Exception('Unknown datastore type: ' . var_export($storeType, TRUE));
- }
/* Datastore from module. */
$className = SimpleSAML_Module::resolveClass($storeType, 'Store', 'SimpleSAML_Store');
self::$instance = new $className();
diff --git a/lib/SimpleSAML/Utilities.php b/lib/SimpleSAML/Utilities.php
index 44a9fbf..5f0e7cf 100644
--- a/lib/SimpleSAML/Utilities.php
+++ b/lib/SimpleSAML/Utilities.php
@@ -29,341 +29,100 @@ class SimpleSAML_Utilities {
/**
- * Will return sp.example.org
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfHost() instead.
*/
public static function getSelfHost() {
-
- $url = self::getBaseURL();
-
- $start = strpos($url,'://') + 3;
- $length = strcspn($url,'/:',$start);
-
- return substr($url, $start, $length);
-
- }
-
- /**
- * Retrieve Host value from $_SERVER environment variables
- */
- private static function getServerHost() {
-
- if (array_key_exists('HTTP_HOST', $_SERVER)) {
- $currenthost = $_SERVER['HTTP_HOST'];
- } elseif (array_key_exists('SERVER_NAME', $_SERVER)) {
- $currenthost = $_SERVER['SERVER_NAME'];
- } else {
- /* Almost certainly not what you want, but ... */
- $currenthost = 'localhost';
- }
-
- if(strstr($currenthost, ":")) {
- $currenthostdecomposed = explode(":", $currenthost);
- $port = array_pop($currenthostdecomposed);
- if (!is_numeric($port)) {
- array_push($currenthostdecomposed, $port);
- }
- $currenthost = implode($currenthostdecomposed, ":");
- }
- return $currenthost;
-
+ return \SimpleSAML\Utils\HTTP::getSelfHost();
}
/**
- * Will return https://sp.example.org[:PORT]
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfURLHost() instead.
*/
public static function selfURLhost() {
-
- $url = self::getBaseURL();
-
- $start = strpos($url,'://') + 3;
- $length = strcspn($url,'/',$start) + $start;
-
- return substr($url, 0, $length);
+ return \SimpleSAML\Utils\HTTP::getSelfURLHost();
}
/**
- * This function checks if we should set a secure cookie.
- *
- * @return TRUE if the cookie should be secure, FALSE otherwise.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::isHTTPS() instead.
*/
public static function isHTTPS() {
-
- $url = self::getBaseURL();
-
- $end = strpos($url,'://');
- $protocol = substr($url, 0, $end);
-
- if ($protocol === 'https') {
- return TRUE;
- } else {
- return FALSE;
- }
-
- }
-
- /**
- * retrieve HTTPS status from $_SERVER environment variables
- */
- private static function getServerHTTPS() {
-
- if(!array_key_exists('HTTPS', $_SERVER)) {
- /* Not an https-request. */
- return FALSE;
- }
-
- if($_SERVER['HTTPS'] === 'off') {
- /* IIS with HTTPS off. */
- return FALSE;
- }
-
- /* Otherwise, HTTPS will be a non-empty string. */
- return $_SERVER['HTTPS'] !== '';
-
+ return \SimpleSAML\Utils\HTTP::isHTTPS();
}
/**
- * Retrieve port number from $_SERVER environment variables
- * return it as a string such as ":80" if different from
- * protocol default port, otherwise returns an empty string
- */
- private static function getServerPort() {
-
- if (isset($_SERVER["SERVER_PORT"])) {
- $portnumber = $_SERVER["SERVER_PORT"];
- } else {
- $portnumber = 80;
- }
- $port = ':' . $portnumber;
-
- if (self::getServerHTTPS()) {
- if ($portnumber == '443') $port = '';
- } else {
- if ($portnumber == '80') $port = '';
- }
-
- return $port;
-
- }
-
- /**
- * Will return https://sp.example.org/universities/ruc/baz/simplesaml/saml2/SSOService.php
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfURLNoQuery() instead.
*/
public static function selfURLNoQuery() {
-
- $selfURLhost = self::selfURLhost();
- $selfURLhost .= $_SERVER['SCRIPT_NAME'];
- if (isset($_SERVER['PATH_INFO'])) {
- $selfURLhost .= $_SERVER['PATH_INFO'];
- }
- return $selfURLhost;
+ return \SimpleSAML\Utils\HTTP::getSelfURLNoQuery();
}
/**
- * Will return sp.example.org/ssp/sp1
- *
- * Please note this function will return the base URL for the current
- * SP, as defined in the global configuration.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfHostWithPath() instead.
*/
public static function getSelfHostWithPath() {
-
- $baseurl = explode("/", self::getBaseURL());
- $elements = array_slice($baseurl, 3 - count($baseurl), count($baseurl) - 4);
- $path = implode("/", $elements);
- $selfhostwithpath = self::getSelfHost();
- return $selfhostwithpath . "/" . $path;
+ return \SimpleSAML\Utils\HTTP::getSelfHostWithPath();
}
-
+
+
/**
- * Will return foo
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getFirstPathElement() instead.
*/
public static function getFirstPathElement($trailingslash = true) {
-
- if (preg_match('|^/(.*?)/|', $_SERVER['SCRIPT_NAME'], $matches)) {
- return ($trailingslash ? '/' : '') . $matches[1];
- }
- return '';
+ return \SimpleSAML\Utils\HTTP::getFirstPathElement($trailingslash);
}
-
- public static function selfURL() {
-
- $selfURLhost = self::selfURLhost();
-
- $requestURI = $_SERVER['REQUEST_URI'];
- if ($requestURI[0] !== '/') {
- /* We probably have a URL of the form: http://server/. */
- if (preg_match('#^https?://[^/]*(/.*)#i', $requestURI, $matches)) {
- $requestURI = $matches[1];
- }
- }
-
- return $selfURLhost . $requestURI;
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getSelfURL() instead.
+ */
+ public static function selfURL() {
+ return \SimpleSAML\Utils\HTTP::getSelfURL();
}
/**
- * Retrieve and return the absolute base URL for the simpleSAMLphp installation.
- *
- * For example: https://idp.example.org/simplesaml/
- *
- * The URL will always end with a '/'.
- *
- * @return string The absolute base URL for the simpleSAMLphp installation.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getBaseURL() instead.
*/
public static function getBaseURL() {
-
- $globalConfig = SimpleSAML_Configuration::getInstance();
- $baseURL = $globalConfig->getString('baseurlpath', 'simplesaml/');
-
- if (preg_match('#^https?://.*/$#D', $baseURL, $matches)) {
- /* full URL in baseurlpath, override local server values */
- return $baseURL;
- } elseif (
- (preg_match('#^/?([^/]?.*/)$#D', $baseURL, $matches)) ||
- (preg_match('#^\*(.*)/$#D', $baseURL, $matches)) ||
- ($baseURL === '')) {
- /* get server values */
-
- if (self::getServerHTTPS()) {
- $protocol = 'https://';
- } else {
- $protocol = 'http://';
- }
-
- $hostname = self::getServerHost();
- $port = self::getServerPort();
- $path = '/' . $globalConfig->getBaseURL();
-
- return $protocol.$hostname.$port.$path;
- } else {
- throw new SimpleSAML_Error_Exception('Invalid value of \'baseurl\' in '.
- 'config.php. Valid format is in the form: '.
- '[(http|https)://(hostname|fqdn)[:port]]/[path/to/simplesaml/]. '.
- 'It must end with a \'/\'.');
- }
-
+ return \SimpleSAML\Utils\HTTP::getBaseURL();
}
/**
- * Add one or more query parameters to the given URL.
- *
- * @param $url The URL the query parameters should be added to.
- * @param $parameter The query parameters which should be added to the url. This should be
- * an associative array. For backwards comaptibility, it can also be a
- * query string representing the new parameters. This will write a warning
- * to the log.
- * @return The URL with the new query parameters.
- */
- public static function addURLparameter($url, $parameter) {
-
- /* For backwards compatibility - allow $parameter to be a string. */
- if(is_string($parameter)) {
- /* Print warning to log. */
- $backtrace = debug_backtrace();
- $where = $backtrace[0]['file'] . ':' . $backtrace[0]['line'];
- SimpleSAML_Logger::warning(
- 'Deprecated use of SimpleSAML_Utilities::addURLparameter at ' . $where .
- '. The parameter "$parameter" should now be an array, but a string was passed.');
-
- $parameter = self::parseQueryString($parameter);
- }
- assert('is_array($parameter)');
-
- $queryStart = strpos($url, '?');
- if($queryStart === FALSE) {
- $oldQuery = array();
- $url .= '?';
- } else {
- $oldQuery = substr($url, $queryStart + 1);
- if($oldQuery === FALSE) {
- $oldQuery = array();
- } else {
- $oldQuery = self::parseQueryString($oldQuery);
- }
- $url = substr($url, 0, $queryStart + 1);
- }
-
- $query = array_merge($oldQuery, $parameter);
- $url .= http_build_query($query, '', '&');
-
- return $url;
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::addURLParameters() instead.
+ */
+ public static function addURLparameter($url, $parameters) {
+ return \SimpleSAML\Utils\HTTP::addURLParameters($url, $parameters);
}
/**
- * 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. An empty string if the $url parameter is empty as
- * defined by the empty() function.
- * @throws SimpleSAML_Error_Exception if the URL is malformed or is not allowed by configuration.
+ * @deprecated This method will be removed in SSP 2.0. Please use \SimpleSAML\Utils\HTTP::checkURLAllowed() instead.
*/
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;
+ return \SimpleSAML\Utils\HTTP::checkURLAllowed($url, $trustedSites);
}
/**
- * 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.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML_Auth_State::parseStateID() instead.
*/
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);
+ return SimpleSAML_Auth_State::parseStateID($stateId);
}
+ /**
+ * @deprecated This method will be removed in SSP 2.0.
+ */
public static function checkDateConditions($start=NULL, $end=NULL) {
$currentTime = time();
-
+
if (!empty($start)) {
$startTime = SAML2_Utils::xsDateTimeToTimestamp($start);
/* Allow for a 10 minute difference in Time */
@@ -381,185 +140,45 @@ class SimpleSAML_Utilities {
}
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Random::generateID() instead.
+ */
public static function generateID() {
- return '_' . self::stringToHex(self::generateRandomBytes(21));
+ return SimpleSAML\Utils\Random::generateID();
}
-
+
/**
- * This function generates a timestamp on the form used by the SAML protocols.
- *
- * @param $instant The time the timestamp should represent.
- * @return The timestamp.
+ * @deprecated This method will be removed in SSP 2.0. Please use \SimpleSAML\Utils\Time::generateTimestamp() instead.
*/
public static function generateTimestamp($instant = NULL) {
- if($instant === NULL) {
- $instant = time();
- }
- return gmdate('Y-m-d\TH:i:s\Z', $instant);
+ return SimpleSAML\Utils\Time::generateTimestamp($instant);
}
/**
- * Interpret a ISO8601 duration value relative to a given timestamp.
- *
- * @param string $duration The duration, as a string.
- * @param int $timestamp The unix timestamp we should apply the duration to. Optional, default
- * to the current time.
- * @return int The new timestamp, after the duration is applied.
+ * @deprecated This method will be removed in SSP 2.0. Please use \SimpleSAML\Utils\Time::parseDuration() instead.
*/
public static function parseDuration($duration, $timestamp = NULL) {
- assert('is_string($duration)');
- assert('is_null($timestamp) || is_int($timestamp)');
-
- /* Parse the duration. We use a very strict pattern. */
- $durationRegEx = '#^(-?)P(?:(?:(?:(\\d+)Y)?(?:(\\d+)M)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)(?:[.,]\d+)?S)?)?)|(?:(\\d+)W))$#D';
- if (!preg_match($durationRegEx, $duration, $matches)) {
- throw new Exception('Invalid ISO 8601 duration: ' . $duration);
- }
-
- $durYears = (empty($matches[2]) ? 0 : (int)$matches[2]);
- $durMonths = (empty($matches[3]) ? 0 : (int)$matches[3]);
- $durDays = (empty($matches[4]) ? 0 : (int)$matches[4]);
- $durHours = (empty($matches[5]) ? 0 : (int)$matches[5]);
- $durMinutes = (empty($matches[6]) ? 0 : (int)$matches[6]);
- $durSeconds = (empty($matches[7]) ? 0 : (int)$matches[7]);
- $durWeeks = (empty($matches[8]) ? 0 : (int)$matches[8]);
-
- if (!empty($matches[1])) {
- /* Negative */
- $durYears = -$durYears;
- $durMonths = -$durMonths;
- $durDays = -$durDays;
- $durHours = -$durHours;
- $durMinutes = -$durMinutes;
- $durSeconds = -$durSeconds;
- $durWeeks = -$durWeeks;
- }
-
- if ($timestamp === NULL) {
- $timestamp = time();
- }
-
- if ($durYears !== 0 || $durMonths !== 0) {
- /* Special handling of months and years, since they aren't a specific interval, but
- * instead depend on the current time.
- */
-
- /* We need the year and month from the timestamp. Unfortunately, PHP doesn't have the
- * gmtime function. Instead we use the gmdate function, and split the result.
- */
- $yearmonth = explode(':', gmdate('Y:n', $timestamp));
- $year = (int)($yearmonth[0]);
- $month = (int)($yearmonth[1]);
-
- /* Remove the year and month from the timestamp. */
- $timestamp -= gmmktime(0, 0, 0, $month, 1, $year);
-
- /* Add years and months, and normalize the numbers afterwards. */
- $year += $durYears;
- $month += $durMonths;
- while ($month > 12) {
- $year += 1;
- $month -= 12;
- }
- while ($month < 1) {
- $year -= 1;
- $month += 12;
- }
-
- /* Add year and month back into timestamp. */
- $timestamp += gmmktime(0, 0, 0, $month, 1, $year);
- }
-
- /* Add the other elements. */
- $timestamp += $durWeeks * 7 * 24 * 60 * 60;
- $timestamp += $durDays * 24 * 60 * 60;
- $timestamp += $durHours * 60 * 60;
- $timestamp += $durMinutes * 60;
- $timestamp += $durSeconds;
-
- return $timestamp;
+ return SimpleSAML\Utils\Time::parseDuration($duration, $timestamp);
}
/**
- * Show and log fatal error message.
- *
- * This function logs a error message to the error log and shows the
- * message to the user. Script execution terminates afterwards.
- *
- * The error code comes from the errors-dictionary. It can optionally include parameters, which
- * will be substituted into the output string.
- *
- * @param string $trackId The trackid of the user, from $session->getTrackID().
- * @param mixed $errorCode Either a string with the error code, or an array with the error code and
- * additional parameters.
- * @param Exception $e The exception which caused the error.
- * @deprecated
+ * @deprecated This method will be removed in SSP 2.0. Please raise a SimpleSAML_Error_Error exception instead.
*/
public static function fatalError($trackId = 'na', $errorCode = null, Exception $e = null) {
-
throw new SimpleSAML_Error_Error($errorCode, $e);
}
/**
- * Check whether an IP address is part of an CIDR.
+ * @deprecated This method will be removed in version 2.0. Use SimpleSAML\Utils\Net::ipCIDRcheck() instead.
*/
static function ipCIDRcheck($cidr, $ip = null) {
- if ($ip == null) $ip = $_SERVER['REMOTE_ADDR'];
- list ($net, $mask) = explode('/', $cidr);
-
- if (strstr($ip, ':') || strstr($net, ':')) {
- // Validate IPv6 with inet_pton, convert to hex with bin2hex
- // then store as a long with hexdec
-
- $ip_pack = inet_pton($ip);
- $net_pack = inet_pton($net);
-
- if ($ip_pack === false || $net_pack === false) {
- // not valid IPv6 address (warning already issued)
- return false;
- }
-
- $ip_ip = str_split(bin2hex($ip_pack),8);
- foreach ($ip_ip as &$value) {
- $value = hexdec($value);
- }
-
- $ip_net = str_split(bin2hex($net_pack),8);
- foreach ($ip_net as &$value) {
- $value = hexdec($value);
- }
- } else {
- $ip_ip[0] = ip2long ($ip);
- $ip_net[0] = ip2long ($net);
- }
-
- for($i = 0; $mask > 0 && $i < sizeof($ip_ip); $i++) {
- if ($mask > 32) {
- $iteration_mask = 32;
- } else {
- $iteration_mask = $mask;
- }
- $mask -= 32;
-
- $ip_mask = ~((1 << (32 - $iteration_mask)) - 1);
-
- $ip_net_mask = $ip_net[$i] & $ip_mask;
- $ip_ip_mask = $ip_ip[$i] & $ip_mask;
-
- if ($ip_ip_mask != $ip_net_mask)
- return false;
- }
- return true;
+ return SimpleSAML\Utils\Net::ipCIDRcheck($cidr, $ip);
}
- /*
- * This is a temporary function, holding the redirect() functionality,
- * meanwhile we are deprecating the it.
- */
private static function _doRedirect($url, $parameters = array()) {
assert('is_string($url)');
assert('!empty($url)');
@@ -617,27 +236,8 @@ class SimpleSAML_Utilities {
/**
- * 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 function 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.
+ * @deprecated 1.12.0 This method 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)');
@@ -653,395 +253,88 @@ class SimpleSAML_Utilities {
}
/**
- * This function redirects to the specified URL without performing any security checks. Please, do NOT use this
- * function with user supplied URLs.
- *
- * 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 function will also generate a simple web page with a clickable link to the target URL.
- *
- * @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.
- *
- * @return void This function never returns.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::redirectTrustedURL() instead.
*/
public static function redirectTrustedURL($url, $parameters = array()) {
- assert('is_string($url)');
- assert('is_array($parameters)');
-
- $url = self::normalizeURL($url);
- self::_doRedirect($url, $parameters);
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($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.
- *
- * 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.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::redirectUntrustedURL() instead.
*/
public static function redirectUntrustedURL($url, $parameters = array()) {
- assert('is_string($url)');
- assert('is_array($parameters)');
-
- $url = self::checkURLAllowed($url);
- self::_doRedirect($url, $parameters);
+ \SimpleSAML\Utils\HTTP::redirectUntrustedURL($url, $parameters);
}
/**
- * This function transposes a two-dimensional array, so that
- * $a['k1']['k2'] becomes $a['k2']['k1'].
- *
- * @param $in Input two-dimensional array.
- * @return The transposed array.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Arrays::transpose() instead.
*/
public static function transposeArray($in) {
- assert('is_array($in)');
-
- $ret = array();
-
- foreach($in as $k1 => $a2) {
- assert('is_array($a2)');
-
- foreach($a2 as $k2 => $v) {
- if(!array_key_exists($k2, $ret)) {
- $ret[$k2] = array();
- }
-
- $ret[$k2][$k1] = $v;
- }
- }
-
- return $ret;
+ return SimpleSAML\Utils\Arrays::transpose($in);
}
/**
- * This function checks if the DOMElement has the correct localName and namespaceURI.
- *
- * We also define the following shortcuts for namespaces:
- * - '@ds': 'http://www.w3.org/2000/09/xmldsig#'
- * - '@md': 'urn:oasis:names:tc:SAML:2.0:metadata'
- * - '@saml1': 'urn:oasis:names:tc:SAML:1.0:assertion'
- * - '@saml1md': 'urn:oasis:names:tc:SAML:profiles:v1metadata'
- * - '@saml1p': 'urn:oasis:names:tc:SAML:1.0:protocol'
- * - '@saml2': 'urn:oasis:names:tc:SAML:2.0:assertion'
- * - '@saml2p': 'urn:oasis:names:tc:SAML:2.0:protocol'
- *
- * @param $element The element we should check.
- * @param $name The localname the element should have.
- * @param $nsURI The namespaceURI the element should have.
- * @return TRUE if both namespace and localname matches, FALSE otherwise.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::isDOMElementOfType() instead.
*/
public static function isDOMElementOfType(DOMNode $element, $name, $nsURI) {
- assert('is_string($name)');
- assert('is_string($nsURI)');
- assert('strlen($nsURI) > 0');
-
- if (!($element instanceof DOMElement)) {
- /* Most likely a comment-node. */
- return FALSE;
- }
-
- /* Check if the namespace is a shortcut, and expand it if it is. */
- if($nsURI[0] == '@') {
-
- /* The defined shortcuts. */
- $shortcuts = array(
- '@ds' => 'http://www.w3.org/2000/09/xmldsig#',
- '@md' => 'urn:oasis:names:tc:SAML:2.0:metadata',
- '@saml1' => 'urn:oasis:names:tc:SAML:1.0:assertion',
- '@saml1md' => 'urn:oasis:names:tc:SAML:profiles:v1metadata',
- '@saml1p' => 'urn:oasis:names:tc:SAML:1.0:protocol',
- '@saml2' => 'urn:oasis:names:tc:SAML:2.0:assertion',
- '@saml2p' => 'urn:oasis:names:tc:SAML:2.0:protocol',
- '@shibmd' => 'urn:mace:shibboleth:metadata:1.0',
- );
-
- /* Check if it is a valid shortcut. */
- if(!array_key_exists($nsURI, $shortcuts)) {
- throw new Exception('Unknown namespace shortcut: ' . $nsURI);
- }
-
- /* Expand the shortcut. */
- $nsURI = $shortcuts[$nsURI];
- }
-
-
- if($element->localName !== $name) {
- return FALSE;
- }
-
- if($element->namespaceURI !== $nsURI) {
- return FALSE;
- }
-
- return TRUE;
+ return SimpleSAML\Utils\XML::isDOMElementOfType($element, $name, $nsURI);
}
/**
- * This function finds direct descendants of a DOM element with the specified
- * localName and namespace. They are returned in an array.
- *
- * This function accepts the same shortcuts for namespaces as the isDOMElementOfType function.
- *
- * @param DOMElement $element The element we should look in.
- * @param string $localName The name the element should have.
- * @param string $namespaceURI The namespace the element should have.
- * @return array Array with the matching elements in the order they are found. An empty array is
- * returned if no elements match.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::getDOMChildren() instead.
*/
public static function getDOMChildren(DOMElement $element, $localName, $namespaceURI) {
- assert('is_string($localName)');
- assert('is_string($namespaceURI)');
-
- $ret = array();
-
- for($i = 0; $i < $element->childNodes->length; $i++) {
- $child = $element->childNodes->item($i);
-
- /* Skip text nodes and comment elements. */
- if($child instanceof DOMText || $child instanceof DOMComment) {
- continue;
- }
-
- if(self::isDOMElementOfType($child, $localName, $namespaceURI) === TRUE) {
- $ret[] = $child;
- }
- }
-
- return $ret;
+ return SimpleSAML\Utils\XML::getDOMChildren($element, $localName, $namespaceURI);
}
/**
- * This function extracts the text from DOMElements which should contain
- * only text content.
- *
- * @param $element The element we should extract text from.
- * @return The text content of the element.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::getDOMText() instead.
*/
public static function getDOMText($element) {
- assert('$element instanceof DOMElement');
-
- $txt = '';
-
- for($i = 0; $i < $element->childNodes->length; $i++) {
- $child = $element->childNodes->item($i);
- if(!($child instanceof DOMText)) {
- throw new Exception($element->localName . ' contained a non-text child node.');
- }
-
- $txt .= $child->wholeText;
- }
-
- $txt = trim($txt);
- return $txt;
+ return SimpleSAML\Utils\XML::getDOMText($element);
}
/**
- * This function parses the Accept-Language http header and returns an associative array with each
- * language and the score for that language.
- *
- * If an language includes a region, then the result will include both the language with the region
- * and the language without the region.
- *
- * The returned array will be in the same order as the input.
- *
- * @return An associative array with each language and the score for that language.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getAcceptLanguage() instead.
*/
public static function getAcceptLanguage() {
-
- if(!array_key_exists('HTTP_ACCEPT_LANGUAGE', $_SERVER)) {
- /* No Accept-Language header - return empty set. */
- return array();
- }
-
- $languages = explode(',', strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']));
-
- $ret = array();
-
- foreach($languages as $l) {
- $opts = explode(';', $l);
-
- $l = trim(array_shift($opts)); /* The language is the first element.*/
-
- $q = 1.0;
-
- /* Iterate over all options, and check for the quality option. */
- foreach($opts as $o) {
- $o = explode('=', $o);
- if(count($o) < 2) {
- /* Skip option with no value. */
- continue;
- }
-
- $name = trim($o[0]);
- $value = trim($o[1]);
-
- if($name === 'q') {
- $q = (float)$value;
- }
- }
-
- /* Remove the old key to ensure that the element is added to the end. */
- unset($ret[$l]);
-
- /* Set the quality in the result. */
- $ret[$l] = $q;
-
- if(strpos($l, '-')) {
- /* The language includes a region part. */
-
- /* Extract the language without the region. */
- $l = explode('-', $l);
- $l = $l[0];
-
- /* Add this language to the result (unless it is defined already). */
- if(!array_key_exists($l, $ret)) {
- $ret[$l] = $q;
- }
- }
- }
-
- return $ret;
+ return \SimpleSAML\Utils\HTTP::getAcceptLanguage();
}
/**
- * This function attempts to validate an XML string against the specified schema.
- *
- * It will parse the string into a DOM document and validate this document against the schema.
- *
- * @param $xml The XML string or document which should be validated.
- * @param $schema The schema which should be used.
- * @return Returns a string with the errors if validation fails. An empty string is
- * returned if validation passes.
- * @deprecated
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::isValid() instead.
*/
public static function validateXML($xml, $schema) {
- assert('is_string($xml) || $xml instanceof DOMDocument');
- assert('is_string($schema)');
-
- SimpleSAML_XML_Errors::begin();
-
- if($xml instanceof DOMDocument) {
- $dom = $xml;
- $res = TRUE;
- } else {
- $dom = new DOMDocument;
- $res = $dom->loadXML($xml);
- }
-
- if($res) {
-
- $config = SimpleSAML_Configuration::getInstance();
- $schemaPath = $config->resolvePath('schemas') . '/';
- $schemaFile = $schemaPath . $schema;
-
- $res = $dom->schemaValidate($schemaFile);
- if($res) {
- SimpleSAML_XML_Errors::end();
- return '';
- }
-
- $errorText = "Schema validation failed on XML string:\n";
- } else {
- $errorText = "Failed to parse XML string for schema validation:\n";
- }
-
- $errors = SimpleSAML_XML_Errors::end();
- $errorText .= SimpleSAML_XML_Errors::formatErrors($errors);
-
- return $errorText;
+ $result = \SimpleSAML\Utils\XML::isValid($xml, $schema);
+ return ($result === true) ? '' : $result;
}
/**
- * This function performs some sanity checks on XML documents, and optionally validates them
- * against their schema. A warning will be printed to the log if validation fails.
- *
- * @param $message The message which should be validated, as a string.
- * @param $type The type of document - can be either 'saml20', 'saml11' or 'saml-meta'.
- * @deprecated
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::checkSAMLMessage() instead.
*/
public static function validateXMLDocument($message, $type) {
- assert('is_string($message)');
- assert($type === 'saml11' || $type === 'saml20' || $type === 'saml-meta');
-
- /* A SAML message should not contain a doctype-declaration. */
- if(strpos($message, '<!DOCTYPE') !== FALSE) {
- throw new Exception('XML contained a doctype declaration.');
- }
-
- $enabled = SimpleSAML_Configuration::getInstance()->getBoolean('debug.validatexml', NULL);
- if($enabled === NULL) {
- /* Fall back to old configuration option. */
- $enabled = SimpleSAML_Configuration::getInstance()->getBoolean('debug.validatesamlmessages', NULL);
- if($enabled === NULL) {
- /* Fall back to even older configuration option. */
- $enabled = SimpleSAML_Configuration::getInstance()->getBoolean('debug.validatesaml2messages', FALSE);
- }
- }
-
- if(!$enabled) {
- return;
- }
-
- switch($type) {
- case 'saml11':
- $result = self::validateXML($message, 'oasis-sstc-saml-schema-protocol-1.1.xsd');
- break;
- case 'saml20':
- $result = self::validateXML($message, 'saml-schema-protocol-2.0.xsd');
- break;
- case 'saml-meta':
- $result = self::validateXML($message, 'saml-schema-metadata-2.0.xsd');
- break;
- default:
- throw new Exception('Invalid message type.');
- }
-
- if($result !== '') {
- SimpleSAML_Logger::warning($result);
- }
+ \SimpleSAML\Utils\XML::checkSAMLMessage($message, $type);
}
/**
- * This function generates a binary string containing random bytes.
- *
- * It is implemented as a wrapper of the openssl_random_pseudo_bytes function,
- * available since PHP 5.3.0.
- *
- * @param int $length The number of random bytes to return.
- * @return string A string of $length random bytes.
+ * @deprecated This method will be removed in SSP 2.0. Please use openssl_random_pseudo_bytes() instead.
*/
public static function generateRandomBytes($length) {
assert('is_int($length)');
- return openssl_random_pseudo_bytes($length);
+ return openssl_random_pseudo_bytes($length);
}
/**
- * This function converts a binary string to hexadecimal characters.
- *
- * @param $bytes Input string.
- * @return String with lowercase hexadecimal characters.
+ * @deprecated This method will be removed in SSP 2.0. Please use bin2hex() instead.
*/
public static function stringToHex($bytes) {
$ret = '';
@@ -1053,262 +346,56 @@ class SimpleSAML_Utilities {
/**
- * Resolve a (possibly) relative path from the given base path.
- *
- * A path which starts with a '/' is assumed to be absolute, all others are assumed to be
- * relative. The default base path is the root of the simpleSAMPphp installation.
- *
- * @param $path The path we should resolve.
- * @param $base The base path, where we should search for $path from. Default value is the root
- * of the simpleSAMLphp installation.
- * @return An absolute path referring to $path.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\System::resolvePath() instead.
*/
public static function resolvePath($path, $base = NULL) {
- if($base === NULL) {
- $config = SimpleSAML_Configuration::getInstance();
- $base = $config->getBaseDir();
- }
-
- /* Remove trailing slashes from $base. */
- while(substr($base, -1) === '/') {
- $base = substr($base, 0, -1);
- }
-
- /* Check for absolute path. */
- if(substr($path, 0, 1) === '/') {
- /* Absolute path. */
- $ret = '/';
- } else {
- /* Path relative to base. */
- $ret = $base;
- }
-
- $path = explode('/', $path);
- foreach($path as $d) {
- if($d === '.') {
- continue;
- } elseif($d === '..') {
- $ret = dirname($ret);
- } else {
- if(substr($ret, -1) !== '/') {
- $ret .= '/';
- }
- $ret .= $d;
- }
- }
-
- return $ret;
+ return \SimpleSAML\Utils\System::resolvePath($path, $base);
}
/**
- * Resolve a (possibly) relative URL relative to a given base URL.
- *
- * This function supports these forms of relative URLs:
- * ^\w+: Absolute URL
- * ^// Same protocol.
- * ^/ Same protocol and host.
- * ^? Same protocol, host and path, replace query string & fragment
- * ^# Same protocol, host, path and query, replace fragment
- * The rest: Relative to the base path.
- *
- * @param $url The relative URL.
- * @param $base The base URL. Defaults to the base URL of this installation of simpleSAMLphp.
- * @return An absolute URL for the given relative URL.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::resolveURL() instead.
*/
public static function resolveURL($url, $base = NULL) {
- if($base === NULL) {
- $base = self::getBaseURL();
- }
-
- if(!preg_match('/^((((\w+:)\/\/[^\/]+)(\/[^?#]*))(?:\?[^#]*)?)(?:#.*)?/', $base, $baseParsed)) {
- throw new Exception('Unable to parse base url: ' . $base);
- }
-
- $baseDir = dirname($baseParsed[5] . 'filename');
- $baseScheme = $baseParsed[4];
- $baseHost = $baseParsed[3];
- $basePath = $baseParsed[2];
- $baseQuery = $baseParsed[1];
-
- if(preg_match('$^\w+:$', $url)) {
- return $url;
- }
-
- if(substr($url, 0, 2) === '//') {
- return $baseScheme . $url;
- }
-
- $firstChar = substr($url, 0, 1);
-
- if($firstChar === '/') {
- return $baseHost . $url;
- }
-
- if($firstChar === '?') {
- return $basePath . $url;
- }
-
- if($firstChar === '#') {
- return $baseQuery . $url;
- }
-
-
- /* We have a relative path. Remove query string/fragment and save it as $tail. */
- $queryPos = strpos($url, '?');
- $fragmentPos = strpos($url, '#');
- if($queryPos !== FALSE || $fragmentPos !== FALSE) {
- if($queryPos === FALSE) {
- $tailPos = $fragmentPos;
- } elseif($fragmentPos === FALSE) {
- $tailPos = $queryPos;
- } elseif($queryPos < $fragmentPos) {
- $tailPos = $queryPos;
- } else {
- $tailPos = $fragmentPos;
- }
-
- $tail = substr($url, $tailPos);
- $dir = substr($url, 0, $tailPos);
- } else {
- $dir = $url;
- $tail = '';
- }
-
- $dir = self::resolvePath($dir, $baseDir);
-
- return $baseHost . $dir . $tail;
+ return \SimpleSAML\Utils\HTTP::resolveURL($url, $base);
}
/**
- * Normalizes a URL to an absolute URL and validate it.
- *
- * In addition to resolving the URL, this function makes sure that it is
- * a link to a http or https site.
- *
- * @param string $url The relative URL.
- * @return string An absolute URL for the given relative URL.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::normalizeURL() instead.
*/
public static function normalizeURL($url) {
- assert('is_string($url)');
-
- $url = SimpleSAML_Utilities::resolveURL($url, SimpleSAML_Utilities::selfURL());
-
- /* Verify that the URL is to a http or https site. */
- if (!preg_match('@^https?://@i', $url)) {
- throw new SimpleSAML_Error_Exception('Invalid URL: ' . $url);
- }
-
- return $url;
+ return \SimpleSAML\Utils\HTTP::normalizeURL($url);
}
/**
- * Parse a query string into an array.
- *
- * This function parses a query string into an array, similar to the way the builtin
- * 'parse_str' works, except it doesn't handle arrays, and it doesn't do "magic quotes".
- *
- * Query parameters without values will be set to an empty string.
- *
- * @param $query_string The query string which should be parsed.
- * @return The query string as an associative array.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::parseQueryString() instead.
*/
public static function parseQueryString($query_string) {
- assert('is_string($query_string)');
-
- $res = array();
- foreach(explode('&', $query_string) as $param) {
- $param = explode('=', $param);
- $name = urldecode($param[0]);
- if(count($param) === 1) {
- $value = '';
- } else {
- $value = urldecode($param[1]);
- }
-
- $res[$name] = $value;
- }
-
- return $res;
+ return \SimpleSAML\Utils\HTTP::parseQueryString($query_string);
}
/**
- * Parse and validate an array with attributes.
- *
- * This function takes in an associative array with attributes, and parses and validates
- * this array. On success, it will return a normalized array, where each attribute name
- * is an index to an array of one or more strings. On failure an exception will be thrown.
- * This exception will contain an message describing what is wrong.
- *
- * @param array $attributes The attributes we should parse and validate.
- * @return array The parsed attributes.
+ * @deprecated This method will be removed in SSP 2.0. Please use
+ * SimpleSAML\Utils\Arrays::normalizeAttributesArray() instead.
*/
public static function parseAttributes($attributes) {
-
- if (!is_array($attributes)) {
- throw new Exception('Attributes was not an array. Was: ' . var_export($attributes, TRUE));
- }
-
- $newAttrs = array();
- foreach ($attributes as $name => $values) {
- if (!is_string($name)) {
- throw new Exception('Invalid attribute name: ' . var_export($name, TRUE));
- }
-
- if (!is_array($values)) {
- $values = array($values);
- }
-
- foreach ($values as $value) {
- if (!is_string($value)) {
- throw new Exception('Invalid attribute value for attribute ' . $name .
- ': ' . var_export($value, TRUE));
- }
- }
-
- $newAttrs[$name] = $values;
- }
-
- return $newAttrs;
+ return SimpleSAML\Utils\Arrays::normalizeAttributesArray($attributes);
}
/**
- * Retrieve secret salt.
- *
- * This function retrieves the value which is configured as the secret salt. It will
- * check that the value exists and is set to a non-default value. If it isn't, an
- * exception will be thrown.
- *
- * The secret salt can be used as a component in hash functions, to make it difficult to
- * test all possible values in order to retrieve the original value. It can also be used
- * as a simple method for signing data, by hashing the data together with the salt.
- *
- * @return string The secret salt.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Config::getSecretSalt() instead.
*/
public static function getSecretSalt() {
-
- $secretSalt = SimpleSAML_Configuration::getInstance()->getString('secretsalt');
- if ($secretSalt === 'defaultsecretsalt') {
- throw new Exception('The "secretsalt" configuration option must be set to a secret' .
- ' value.');
- }
-
- return $secretSalt;
+ return SimpleSAML\Utils\Config::getSecretSalt();
}
/**
- * Retrieve last error message.
- *
- * This function retrieves the last error message. If no error has occurred,
- * '[No error message found]' will be returned. If the required function isn't available,
- * '[Cannot get error message]' will be returned.
- *
- * @return string Last error message.
+ * @deprecated This method will be removed in SSP 2.0. Please call error_get_last() directly.
*/
public static function getLastError() {
@@ -1326,396 +413,99 @@ class SimpleSAML_Utilities {
/**
- * Resolves a path that may be relative to the cert-directory.
- *
- * @param string $path The (possibly relative) path to the file.
- * @return string The file path.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Config::getCertPath() instead.
*/
public static function resolveCert($path) {
- assert('is_string($path)');
-
- $globalConfig = SimpleSAML_Configuration::getInstance();
- $base = $globalConfig->getPathValue('certdir', 'cert/');
- return SimpleSAML_Utilities::resolvePath($path, $base);
+ return \SimpleSAML\Utils\Config::getCertPath($path);
}
/**
- * Get public key or certificate from metadata.
- *
- * This function implements a function to retrieve the public key or certificate from
- * a metadata array.
- *
- * It will search for the following elements in the metadata:
- * 'certData' The certificate as a base64-encoded string.
- * 'certificate' A file with a certificate or public key in PEM-format.
- * 'certFingerprint' The fingerprint of the certificate. Can be a single fingerprint,
- * or an array of multiple valid fingerprints.
- *
- * This function will return an array with these elements:
- * 'PEM' The public key/certificate in PEM-encoding.
- * 'certData' The certificate data, base64 encoded, on a single line. (Only
- * present if this is a certificate.)
- * 'certFingerprint' Array of valid certificate fingerprints. (Only present
- * if this is a certificate.)
- *
- * @param SimpleSAML_Configuration $metadata The metadata.
- * @param bool $required Whether the private key is required. If this is TRUE, a
- * missing key will cause an exception. Default is FALSE.
- * @param string $prefix The prefix which should be used when reading from the metadata
- * array. Defaults to ''.
- * @return array|NULL Public key or certificate data, or NULL if no public key or
- * certificate was found.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Crypto::loadPublicKey() instead.
*/
public static function loadPublicKey(SimpleSAML_Configuration $metadata, $required = FALSE, $prefix = '') {
- assert('is_bool($required)');
- assert('is_string($prefix)');
-
- $keys = $metadata->getPublicKeys(NULL, FALSE, $prefix);
- if ($keys !== NULL) {
- foreach ($keys as $key) {
- if ($key['type'] !== 'X509Certificate') {
- continue;
- }
- if ($key['signing'] !== TRUE) {
- continue;
- }
- $certData = $key['X509Certificate'];
- $pem = "-----BEGIN CERTIFICATE-----\n" .
- chunk_split($certData, 64) .
- "-----END CERTIFICATE-----\n";
- $certFingerprint = strtolower(sha1(base64_decode($certData)));
-
- return array(
- 'certData' => $certData,
- 'PEM' => $pem,
- 'certFingerprint' => array($certFingerprint),
- );
- }
- /* No valid key found. */
- } elseif ($metadata->hasValue($prefix . 'certFingerprint')) {
- /* We only have a fingerprint available. */
- $fps = $metadata->getArrayizeString($prefix . 'certFingerprint');
-
- /* Normalize fingerprint(s) - lowercase and no colons. */
- foreach($fps as &$fp) {
- assert('is_string($fp)');
- $fp = strtolower(str_replace(':', '', $fp));
- }
-
- /* We can't build a full certificate from a fingerprint, and may as well
- * return an array with only the fingerprint(s) immediately.
- */
- return array('certFingerprint' => $fps);
- }
-
- /* No public key/certificate available. */
- if ($required) {
- throw new Exception('No public key / certificate found in metadata.');
- } else {
- return NULL;
- }
+ return SimpleSAML\Utils\Crypto::loadPublicKey($metadata, $required, $prefix);
}
/**
- * Load private key from metadata.
- *
- * This function loads a private key from a metadata array. It searches for the
- * following elements:
- * 'privatekey' Name of a private key file in the cert-directory.
- * 'privatekey_pass' Password for the private key.
- *
- * It returns and array with the following elements:
- * 'PEM' Data for the private key, in PEM-format
- * 'password' Password for the private key.
- *
- * @param SimpleSAML_Configuration $metadata The metadata array the private key should be loaded from.
- * @param bool $required Whether the private key is required. If this is TRUE, a
- * missing key will cause an exception. Default is FALSE.
- * @param string $prefix The prefix which should be used when reading from the metadata
- * array. Defaults to ''.
- * @return array|NULL Extracted private key, or NULL if no private key is present.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Crypto::loadPrivateKey() instead.
*/
public static function loadPrivateKey(SimpleSAML_Configuration $metadata, $required = FALSE, $prefix = '') {
- assert('is_bool($required)');
- assert('is_string($prefix)');
-
- $file = $metadata->getString($prefix . 'privatekey', NULL);
- if ($file === NULL) {
- /* No private key found. */
- if ($required) {
- throw new Exception('No private key found in metadata.');
- } else {
- return NULL;
- }
- }
-
- $file = SimpleSAML_Utilities::resolveCert($file);
- $data = @file_get_contents($file);
- if ($data === FALSE) {
- throw new Exception('Unable to load private key from file "' . $file . '"');
- }
-
- $ret = array(
- 'PEM' => $data,
- );
-
- if ($metadata->hasValue($prefix . 'privatekey_pass')) {
- $ret['password'] = $metadata->getString($prefix . 'privatekey_pass');
- }
-
- return $ret;
+ return SimpleSAML\Utils\Crypto::loadPrivateKey($metadata, $required, $prefix);
}
/**
- * Format a DOM element.
- *
- * This function takes in a DOM element, and inserts whitespace to make it more
- * readable. Note that whitespace added previously will be removed.
- *
- * @param DOMElement $root The root element which should be formatted.
- * @param string $indentBase The indentation this element should be assumed to
- * have. Default is an empty string.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::formatDOMElement() instead.
*/
public static function formatDOMElement(DOMElement $root, $indentBase = '') {
- assert(is_string($indentBase));
-
- /* Check what this element contains. */
- $fullText = ''; /* All text in this element. */
- $textNodes = array(); /* Text nodes which should be deleted. */
- $childNodes = array(); /* Other child nodes. */
- for ($i = 0; $i < $root->childNodes->length; $i++) {
- $child = $root->childNodes->item($i);
-
- if($child instanceof DOMText) {
- $textNodes[] = $child;
- $fullText .= $child->wholeText;
-
- } elseif ($child instanceof DOMComment || $child instanceof DOMElement) {
- $childNodes[] = $child;
-
- } else {
- /* Unknown node type. We don't know how to format this. */
- return;
- }
- }
-
- $fullText = trim($fullText);
- if (strlen($fullText) > 0) {
- /* We contain text. */
- $hasText = TRUE;
- } else {
- $hasText = FALSE;
- }
-
- $hasChildNode = (count($childNodes) > 0);
-
- if ($hasText && $hasChildNode) {
- /* Element contains both text and child nodes - we don't know how to format this one. */
- return;
- }
-
- /* Remove text nodes. */
- foreach ($textNodes as $node) {
- $root->removeChild($node);
- }
-
- if ($hasText) {
- /* Only text - add a single text node to the element with the full text. */
- $root->appendChild(new DOMText($fullText));
- return;
-
- }
-
- if (!$hasChildNode) {
- /* Empty node. Nothing to do. */
- return;
- }
-
- /* Element contains only child nodes - add indentation before each one, and
- * format child elements.
- */
- $childIndentation = $indentBase . ' ';
- foreach ($childNodes as $node) {
- /* Add indentation before node. */
- $root->insertBefore(new DOMText("\n" . $childIndentation), $node);
-
- /* Format child elements. */
- if ($node instanceof DOMElement) {
- self::formatDOMElement($node, $childIndentation);
- }
- }
-
- /* Add indentation before closing tag. */
- $root->appendChild(new DOMText("\n" . $indentBase));
+ SimpleSAML\Utils\XML::formatDOMElement($root, $indentBase);
}
/**
- * Format an XML string.
- *
- * This function formats an XML string using the formatDOMElement function.
- *
- * @param string $xml XML string which should be formatted.
- * @param string $indentBase Optional indentation which should be applied to all
- * the output. Optional, defaults to ''.
- * @return string Formatted string.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::formatXMLString() instead.
*/
public static function formatXMLString($xml, $indentBase = '') {
- assert('is_string($xml)');
- assert('is_string($indentBase)');
-
- $doc = new DOMDocument();
- if (!$doc->loadXML($xml)) {
- throw new Exception('Error parsing XML string.');
- }
-
- $root = $doc->firstChild;
- self::formatDOMElement($root);
-
- return $doc->saveXML($root);
+ return SimpleSAML\Utils\XML::formatXMLString($xml, $indentBase);
}
- /*
- * Input is single value or array, returns an array.
+ /**
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Arrays::arrayize() instead.
*/
public static function arrayize($data, $index = 0) {
- if (is_array($data)) {
- return $data;
- } else {
- return array($index => $data);
- }
+ return SimpleSAML\Utils\Arrays::arrayize($data, $index);
}
/**
- * Check whether the current user is a admin user.
- *
- * @return bool TRUE if the current user is a admin user, FALSE if not.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Auth::isAdmin() instead.
*/
public static function isAdmin() {
-
- $session = SimpleSAML_Session::getSessionFromRequest();
-
- return $session->isValid('admin') || $session->isValid('login-admin');
+ return SimpleSAML\Utils\Auth::isAdmin();
}
/**
- * Retrieve a admin login URL.
- *
- * @param string|NULL $returnTo The URL the user should arrive on after admin authentication.
- * @return string A URL which can be used for admin authentication.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Auth::getAdminLoginURL instead();
*/
public static function getAdminLoginURL($returnTo = NULL) {
- assert('is_string($returnTo) || is_null($returnTo)');
-
- if ($returnTo === NULL) {
- $returnTo = SimpleSAML_Utilities::selfURL();
- }
-
- return SimpleSAML_Module::getModuleURL('core/login-admin.php', array('ReturnTo' => $returnTo));
+ return SimpleSAML\Utils\Auth::getAdminLoginURL($returnTo);
}
/**
- * Require admin access for current page.
- *
- * This is a helper-function for limiting a page to admin access. It will redirect
- * the user to a login page if the current user doesn't have admin access.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Auth::requireAdmin() instead.
*/
public static function requireAdmin() {
-
- if (self::isAdmin()) {
- return;
- }
-
- /* Not authenticated as admin user. Start authentication. */
-
- if (SimpleSAML_Auth_Source::getById('admin') !== NULL) {
- $as = new SimpleSAML_Auth_Simple('admin');
- $as->login();
- } else {
- throw new Exception('Cannot find "admin" auth source, and admin privileges are required.');
- }
+ \SimpleSAML\Utils\Auth::requireAdmin();
}
/**
- * Do a POST redirect to a page.
- *
- * This function never returns.
- *
- * @param string $destination The destination URL.
- * @param array $post An array of name-value pairs which will be posted.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::submitPOSTData() instead.
*/
public static function postRedirect($destination, $post) {
- assert('is_string($destination)');
- assert('is_array($post)');
-
- $config = SimpleSAML_Configuration::getInstance();
- $httpRedirect = $config->getBoolean('enable.http_post', FALSE);
-
- if ($httpRedirect && preg_match("#^http:#", $destination) && self::isHTTPS()) {
- $url = self::createHttpPostRedirectLink($destination, $post);
- self::redirect($url);
- assert('FALSE');
- }
-
- $p = new SimpleSAML_XHTML_Template($config, 'post.php');
- $p->data['destination'] = $destination;
- $p->data['post'] = $post;
- $p->show();
- exit(0);
+ \SimpleSAML\Utils\HTTP::submitPOSTData($destination, $post);
}
/**
- * Create a link which will POST data.
- *
- * @param string $destination The destination URL.
- * @param array $post The name-value pairs which will be posted to the destination.
- * @return string A URL which can be accessed to post the data.
+ * @deprecated This method will be removed in SSP 2.0. PLease use SimpleSAML\Utils\HTTP::getPOSTRedirectURL() instead.
*/
public static function createPostRedirectLink($destination, $post) {
- assert('is_string($destination)');
- assert('is_array($post)');
-
- $config = SimpleSAML_Configuration::getInstance();
- $httpRedirect = $config->getBoolean('enable.http_post', FALSE);
-
- if ($httpRedirect && preg_match("#^http:#", $destination) && self::isHTTPS()) {
- $url = self::createHttpPostRedirectLink($destination, $post);
- } else {
- $postId = SimpleSAML_Utilities::generateID();
- $postData = array(
- 'post' => $post,
- 'url' => $destination,
- );
-
- $session = SimpleSAML_Session::getSessionFromRequest();
- $session->setData('core_postdatalink', $postId, $postData);
-
- $url = SimpleSAML_Module::getModuleURL('core/postredirect.php', array('RedirId' => $postId));
- }
-
- return $url;
+ return \SimpleSAML\Utils\HTTP::getPOSTRedirectURL($destination, $post);
}
/**
- * Create a link which will POST data to HTTP in a secure way.
- *
- * @param string $destination The destination URL.
- * @param array $post The name-value pairs which will be posted to the destination.
- * @return string A URL which can be accessed to post the data.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::getPOSTRedirectURL() instead.
*/
public static function createHttpPostRedirectLink($destination, $post) {
assert('is_string($destination)');
assert('is_array($post)');
- $postId = SimpleSAML_Utilities::generateID();
+ $postId = SimpleSAML\Utils\Random::generateID();
$postData = array(
'post' => $post,
'url' => $destination,
@@ -1724,7 +514,7 @@ class SimpleSAML_Utilities {
$session = SimpleSAML_Session::getSessionFromRequest();
$session->setData('core_postdatalink', $postId, $postData);
- $redirInfo = base64_encode(self::aesEncrypt($session->getSessionId() . ':' . $postId));
+ $redirInfo = base64_encode(SimpleSAML\Utils\Crypto::aesEncrypt($session->getSessionId() . ':' . $postId));
$url = SimpleSAML_Module::getModuleURL('core/postredirect.php', array('RedirInfo' => $redirInfo));
$url = preg_replace("#^https:#", "http:", $url);
@@ -1734,181 +524,22 @@ class SimpleSAML_Utilities {
/**
- * Validate a certificate against a CA file, by using the builtin
- * openssl_x509_checkpurpose function
- *
- * @param string $certificate The certificate, in PEM format.
- * @param string $caFile File with trusted certificates, in PEM-format.
- * @return boolean|string TRUE on success, or a string with error messages if it failed.
- * @deprecated
- */
- private static function validateCABuiltIn($certificate, $caFile) {
- assert('is_string($certificate)');
- assert('is_string($caFile)');
-
- /* Clear openssl errors. */
- while(openssl_error_string() !== FALSE);
-
- $res = openssl_x509_checkpurpose($certificate, X509_PURPOSE_ANY, array($caFile));
-
- $errors = '';
- /* Log errors. */
- while( ($error = openssl_error_string()) !== FALSE) {
- $errors .= ' [' . $error . ']';
- }
-
- if($res !== TRUE) {
- return $errors;
- }
-
- return TRUE;
- }
-
-
- /**
- * Validate the certificate used to sign the XML against a CA file, by using the "openssl verify" command.
- *
- * This function uses the openssl verify command to verify a certificate, to work around limitations
- * on the openssl_x509_checkpurpose function. That function will not work on certificates without a purpose
- * set.
- *
- * @param string $certificate The certificate, in PEM format.
- * @param string $caFile File with trusted certificates, in PEM-format.
- * @return boolean|string TRUE on success, a string with error messages on failure.
- * @deprecated
- */
- private static function validateCAExec($certificate, $caFile) {
- assert('is_string($certificate)');
- assert('is_string($caFile)');
-
- $command = array(
- 'openssl', 'verify',
- '-CAfile', $caFile,
- '-purpose', 'any',
- );
-
- $cmdline = '';
- foreach($command as $c) {
- $cmdline .= escapeshellarg($c) . ' ';
- }
-
- $cmdline .= '2>&1';
- $descSpec = array(
- 0 => array('pipe', 'r'),
- 1 => array('pipe', 'w'),
- );
- $process = proc_open($cmdline, $descSpec, $pipes);
- if (!is_resource($process)) {
- throw new Exception('Failed to execute verification command: ' . $cmdline);
- }
-
- if (fwrite($pipes[0], $certificate) === FALSE) {
- throw new Exception('Failed to write certificate for verification.');
- }
- fclose($pipes[0]);
-
- $out = '';
- while (!feof($pipes[1])) {
- $line = trim(fgets($pipes[1]));
- if(strlen($line) > 0) {
- $out .= ' [' . $line . ']';
- }
- }
- fclose($pipes[1]);
-
- $status = proc_close($process);
- if ($status !== 0 || $out !== ' [stdin: OK]') {
- return $out;
- }
-
- return TRUE;
- }
-
-
- /**
- * Validate the certificate used to sign the XML against a CA file.
- *
- * This function throws an exception if unable to validate against the given CA file.
- *
- * @param string $certificate The certificate, in PEM format.
- * @param string $caFile File with trusted certificates, in PEM-format.
- * @deprecated
+ * @deprecated This method will be removed in SSP 2.0.
*/
public static function validateCA($certificate, $caFile) {
- assert('is_string($certificate)');
- assert('is_string($caFile)');
-
- if (!file_exists($caFile)) {
- throw new Exception('Could not load CA file: ' . $caFile);
- }
-
- SimpleSAML_Logger::debug('Validating certificate against CA file: ' . var_export($caFile, TRUE));
-
- $resBuiltin = self::validateCABuiltIn($certificate, $caFile);
- if ($resBuiltin !== TRUE) {
- SimpleSAML_Logger::debug('Failed to validate with internal function: ' . var_export($resBuiltin, TRUE));
-
- $resExternal = self::validateCAExec($certificate, $caFile);
- if ($resExternal !== TRUE) {
- SimpleSAML_Logger::debug('Failed to validate with external function: ' . var_export($resExternal, TRUE));
- throw new Exception('Could not verify certificate against CA file "'
- . $caFile . '". Internal result:' . $resBuiltin .
- ' External result:' . $resExternal);
- }
- }
-
- SimpleSAML_Logger::debug('Successfully validated certificate.');
+ SimpleSAML_XML_Validator::validateCertificate($certificate, $caFile);
}
/**
- * Initialize the timezone.
- *
- * This function should be called before any calls to date().
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Time::initTimezone() instead.
*/
public static function initTimezone() {
- static $initialized = FALSE;
-
- if ($initialized) {
- return;
- }
-
- $initialized = TRUE;
-
- $globalConfig = SimpleSAML_Configuration::getInstance();
-
- $timezone = $globalConfig->getString('timezone', NULL);
- if ($timezone !== NULL) {
- if (!date_default_timezone_set($timezone)) {
- throw new SimpleSAML_Error_Exception('Invalid timezone set in the \'timezone\'-option in config.php.');
- }
- return;
- }
-
- /* We don't have a timezone configured. */
-
- /*
- * The date_default_timezone_get()-function is likely to cause a warning.
- * Since we have a custom error handler which logs the errors with a backtrace,
- * this error will be logged even if we prefix the function call with '@'.
- * Instead we temporarily replace the error handler.
- */
- function ignoreError() {
- /* Don't do anything with this error. */
- return TRUE;
- }
- set_error_handler('ignoreError');
- $serverTimezone = date_default_timezone_get();
- restore_error_handler();
-
- /* Set the timezone to the default. */
- date_default_timezone_set($serverTimezone);
+ \SimpleSAML\Utils\Time::initTimezone();
}
/**
- * Disable the loading of external entities in XML documents to prevent local and
- * remote file inclusion attacks. This is in most cases already disabled by default
- * in system libraries, but to be safe we explicitly disable it also.
+ * @deprecated This method will be removed in SSP 2.0. Please use libxml_disable_entity_loader() instead.
*/
public static function disableXMLEntityLoader() {
/* Function only present in PHP >= 5.2.11 while we support 5.2+ */
@@ -1918,90 +549,23 @@ class SimpleSAML_Utilities {
}
/**
- * Atomically write a file.
- *
- * This is a helper function for safely writing file data atomically.
- * It does this by writing the file data to a temporary file, and then
- * renaming this to the correct name.
- *
- * @param string $filename The name of the file.
- * @param string $data The data we should write to the file.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\System::writeFile() instead.
*/
public static function writeFile($filename, $data, $mode=0600) {
- assert('is_string($filename)');
- assert('is_string($data)');
- assert('is_numeric($mode)');
-
- $tmpFile = $filename . '.new.' . getmypid() . '.' . php_uname('n');
-
- $res = @file_put_contents($tmpFile, $data);
- if ($res === FALSE) {
- throw new SimpleSAML_Error_Exception('Error saving file ' . $tmpFile .
- ': ' . SimpleSAML_Utilities::getLastError());
- }
-
- if (!self::isWindowsOS()) {
- $res = chmod($tmpFile, $mode);
- if ($res === FALSE) {
- unlink($tmpFile);
- throw new SimpleSAML_Error_Exception('Error changing file mode ' . $tmpFile .
- ': ' . SimpleSAML_Utilities::getLastError());
- }
- }
-
- $res = rename($tmpFile, $filename);
- if ($res === FALSE) {
- unlink($tmpFile);
- throw new SimpleSAML_Error_Exception('Error renaming ' . $tmpFile . ' to ' .
- $filename . ': ' . SimpleSAML_Utilities::getLastError());
- }
+ \SimpleSAML\Utils\System::writeFile($filename, $data, $mode);
}
/**
- * Get temp directory path.
- *
- * This function retrieves the path to a directory where
- * temporary files can be saved.
- *
- * @return string Path to temp directory, without a trailing '/'.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\System::getTempDir instead.
*/
public static function getTempDir() {
-
- $globalConfig = SimpleSAML_Configuration::getInstance();
-
- $tempDir = $globalConfig->getString('tempdir', '/tmp/simplesaml');
-
- while (substr($tempDir, -1) === '/') {
- $tempDir = substr($tempDir, 0, -1);
- }
-
- if (!is_dir($tempDir)) {
- $ret = mkdir($tempDir, 0700, TRUE);
- if (!$ret) {
- throw new SimpleSAML_Error_Exception('Error creating temp dir ' .
- var_export($tempDir, TRUE) . ': ' . SimpleSAML_Utilities::getLastError());
- }
- } elseif (function_exists('posix_getuid')) {
-
- /* Check that the owner of the temp diretory is the current user. */
- $stat = lstat($tempDir);
- if ($stat['uid'] !== posix_getuid()) {
- throw new SimpleSAML_Error_Exception('Temp directory (' . var_export($tempDir, TRUE) .
- ') not owned by current user.');
- }
- }
-
- return $tempDir;
+ return SimpleSAML\Utils\System::getTempDir();
}
/**
- * Disable reporting of the given log levels.
- *
- * Every call to this function must be followed by a call to popErrorMask();
- *
- * @param int $mask The log levels that should be masked.
+ * @deprecated This method will be removed in SSP 2.0.
*/
public static function maskErrors($mask) {
assert('is_int($mask)');
@@ -2016,12 +580,9 @@ class SimpleSAML_Utilities {
/**
- * Pop an error mask.
- *
- * This function restores the previous error mask.
+ * @deprecated This method will be removed in SSP 2.0.
*/
public static function popErrorMask() {
-
$lastMask = array_pop(self::$logLevelStack);
error_reporting($lastMask[0]);
self::$logMask = $lastMask[1];
@@ -2029,344 +590,66 @@ class SimpleSAML_Utilities {
/**
- * Find the default endpoint in an endpoint array.
- *
- * @param array $endpoints Array with endpoints.
- * @param array $bindings Array with acceptable bindings. Can be NULL if any binding is allowed.
- * @return array|NULL The default endpoint, or NULL if no acceptable endpoints are used.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Config\Metadata::getDefaultEndpoint() instead.
*/
public static function getDefaultEndpoint(array $endpoints, array $bindings = NULL) {
-
- $firstNotFalse = NULL;
- $firstAllowed = NULL;
-
- /* Look through the endpoint list for acceptable endpoints. */
- foreach ($endpoints as $i => $ep) {
- if ($bindings !== NULL && !in_array($ep['Binding'], $bindings, TRUE)) {
- /* Unsupported binding. Skip it. */
- continue;
- }
-
- if (array_key_exists('isDefault', $ep)) {
- if ($ep['isDefault'] === TRUE) {
- /* This is the first endpoitn with isDefault set to TRUE. */
- return $ep;
- }
- /* isDefault is set to FALSE, but the endpoint is still useable as a last resort. */
- if ($firstAllowed === NULL) {
- /* This is the first endpoint that we can use. */
- $firstAllowed = $ep;
- }
- } else {
- if ($firstNotFalse === NULL) {
- /* This is the first endpoint without isDefault set. */
- $firstNotFalse = $ep;
- }
- }
- }
-
- if ($firstNotFalse !== NULL) {
- /* We have an endpoint without isDefault set to FALSE. */
- return $firstNotFalse;
- }
-
- /*
- * $firstAllowed either contains the first endpoint we can use, or it
- * contains NULL if we cannot use any of the endpoints. Either way we
- * return the value of it.
- */
- return $firstAllowed;
+ return \SimpleSAML\Utils\Config\Metadata::getDefaultEndpoint($endpoints, $bindings);
}
/**
- * Check for session cookie, and show missing-cookie page if it is missing.
- *
- * @param string|NULL $retryURL The URL the user should access to retry the operation.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::checkSessionCookie() instead.
*/
public static function checkCookie($retryURL = NULL) {
- assert('is_string($retryURL) || is_null($retryURL)');
-
- $session = SimpleSAML_Session::getSessionFromRequest();
- if ($session->hasSessionCookie()) {
- return;
- }
-
- /* We didn't have a session cookie. Redirect to the no-cookie page. */
-
- $url = SimpleSAML_Module::getModuleURL('core/no_cookie.php');
- if ($retryURL !== NULL) {
- $url = self::addURLParameter($url, array('retryURL' => $retryURL));
- }
- self::redirectTrustedURL($url);
+ \SimpleSAML\Utils\HTTP::checkSessionCookie($retryURL);
}
/**
- * Helper function to log messages that we send or receive.
- *
- * @param string|DOMElement $message The message, as an XML string or an XML element.
- * @param string $type Whether this message is sent or received, encrypted or decrypted.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\XML::debugSAMLMessage() instead.
*/
public static function debugMessage($message, $type) {
- assert('is_string($message) || $message instanceof DOMElement');
-
- $globalConfig = SimpleSAML_Configuration::getInstance();
- if (!$globalConfig->getBoolean('debug', FALSE)) {
- /* Message debug disabled. */
- return;
- }
-
- if ($message instanceof DOMElement) {
- $message = $message->ownerDocument->saveXML($message);
- }
-
- switch ($type) {
- case 'in':
- SimpleSAML_Logger::debug('Received message:');
- break;
- case 'out':
- SimpleSAML_Logger::debug('Sending message:');
- break;
- case 'decrypt':
- SimpleSAML_Logger::debug('Decrypted message:');
- break;
- case 'encrypt':
- SimpleSAML_Logger::debug('Encrypted message:');
- break;
- default:
- assert(FALSE);
- }
-
- $str = self::formatXMLString($message);
- foreach (explode("\n", $str) as $line) {
- SimpleSAML_Logger::debug($line);
- }
+ \SimpleSAML\Utils\XML::debugSAMLMessage($message, $type);
}
/**
- * Helper function to retrieve a file or URL with proxy support.
- *
- * An exception will be thrown if we are unable to retrieve the data.
- *
- * @param string $path The path or URL we should fetch.
- * @param array $context Extra context options. This parameter is optional.
- * @param boolean $getHeaders Whether to also return response headers. Optional.
- * @return mixed array if $getHeaders is set, string otherwise
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::fetch() instead.
*/
public static function fetch($path, $context = array(), $getHeaders = FALSE) {
- assert('is_string($path)');
-
- $config = SimpleSAML_Configuration::getInstance();
-
- $proxy = $config->getString('proxy', NULL);
- if ($proxy !== NULL) {
- if (!isset($context['http']['proxy'])) {
- $context['http']['proxy'] = $proxy;
- }
- if (!isset($context['http']['request_fulluri'])) {
- $context['http']['request_fulluri'] = TRUE;
- }
- // If the remote endpoint over HTTPS uses the SNI extension
- // (Server Name Indication RFC 4366), the proxy could
- // introduce a mismatch between the names in the
- // Host: HTTP header and the SNI_server_name in TLS
- // negotiation (thanks to Cristiano Valli @ GARR-IDEM
- // to have pointed this problem).
- // See: https://bugs.php.net/bug.php?id=63519
- // These controls will force the same value for both fields.
- // Marco Ferrante (marco@csita.unige.it), Nov 2012
- if (preg_match('#^https#i', $path)
- && defined('OPENSSL_TLSEXT_SERVER_NAME')
- && OPENSSL_TLSEXT_SERVER_NAME) {
- // Extract the hostname
- $hostname = parse_url($path, PHP_URL_HOST);
- if (!empty($hostname)) {
- $context['ssl'] = array(
- 'SNI_server_name' => $hostname,
- 'SNI_enabled' => TRUE,
- );
- }
- else {
- SimpleSAML_Logger::warning('Invalid URL format or local URL used through a proxy');
- }
- }
- }
-
- $context = stream_context_create($context);
-
- $data = file_get_contents($path, FALSE, $context);
- if ($data === FALSE) {
- throw new SimpleSAML_Error_Exception('Error fetching ' . var_export($path, TRUE) . ':' . self::getLastError());
- }
-
- // Data and headers.
- if ($getHeaders) {
-
- if (isset($http_response_header)) {
- $headers = array();
- foreach($http_response_header as $h) {
- if(preg_match('@^HTTP/1\.[01]\s+\d{3}\s+@', $h)) {
- $headers = array(); // reset
- $headers[0] = $h;
- continue;
- }
- $bits = explode(':', $h, 2);
- if(count($bits) === 2) {
- $headers[strtolower($bits[0])] = trim($bits[1]);
- }
- }
- } else {
- /* No HTTP headers - probably a different protocol, e.g. file. */
- $headers = NULL;
- }
-
- return array($data, $headers);
- }
-
- return $data;
+ return \SimpleSAML\Utils\HTTP::fetch($path, $context, $getHeaders);
}
/**
- * Function to AES encrypt data.
- *
- * @param string $clear Data to encrypt.
- * @return array The encrypted data and IV.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Crypto::aesEncrypt() instead.
*/
public static function aesEncrypt($clear) {
- assert('is_string($clear)');
-
- if (!function_exists("mcrypt_encrypt")) {
- throw new Exception("aesEncrypt needs mcrypt php module.");
- }
-
- $enc = MCRYPT_RIJNDAEL_256;
- $mode = MCRYPT_MODE_CBC;
-
- $blockSize = mcrypt_get_block_size($enc, $mode);
- $ivSize = mcrypt_get_iv_size($enc, $mode);
- $keySize = mcrypt_get_key_size($enc, $mode);
-
- $key = hash('sha256', self::getSecretSalt(), TRUE);
- $key = substr($key, 0, $keySize);
-
- $len = strlen($clear);
- $numpad = $blockSize - ($len % $blockSize);
- $clear = str_pad($clear, $len + $numpad, chr($numpad));
-
- $iv = self::generateRandomBytes($ivSize);
-
- $data = mcrypt_encrypt($enc, $key, $clear, $mode, $iv);
-
- return $iv . $data;
+ return SimpleSAML\Utils\Crypto::aesEncrypt($clear);
}
/**
- * Function to AES decrypt data.
- *
- * @param $data Encrypted data.
- * @param $iv IV of encrypted data.
- * @return string The decrypted data.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\Crypto::aesDecrypt() instead.
*/
public static function aesDecrypt($encData) {
- assert('is_string($encData)');
-
- if (!function_exists("mcrypt_encrypt")) {
- throw new Exception("aesDecrypt needs mcrypt php module.");
- }
-
- $enc = MCRYPT_RIJNDAEL_256;
- $mode = MCRYPT_MODE_CBC;
-
- $ivSize = mcrypt_get_iv_size($enc, $mode);
- $keySize = mcrypt_get_key_size($enc, $mode);
-
- $key = hash('sha256', self::getSecretSalt(), TRUE);
- $key = substr($key, 0, $keySize);
-
- $iv = substr($encData, 0, $ivSize);
- $data = substr($encData, $ivSize);
-
- $clear = mcrypt_decrypt($enc, $key, $data, $mode, $iv);
-
- $len = strlen($clear);
- $numpad = ord($clear[$len - 1]);
- $clear = substr($clear, 0, $len - $numpad);
-
- return $clear;
+ return SimpleSAML\Utils\Crypto::aesDecrypt($encData);
}
/**
- * This function checks if we are running on Windows OS.
- *
- * @return TRUE if we are on Windows OS, FALSE otherwise.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\System::getOS() instead.
*/
public static function isWindowsOS() {
- return substr(strtoupper(PHP_OS),0,3) == 'WIN';
+ return SimpleSAML\Utils\System::getOS() === SimpleSAML\Utils\System::WINDOWS;
}
/**
- * Set a cookie.
- *
- * @param string $name The name of the session cookie.
- * @param string|NULL $value The value of the cookie. Set to NULL to delete the cookie.
- * @param array|NULL $params Cookie parameters.
- * @param bool $throw Whether to throw exception if setcookie fails.
+ * @deprecated This method will be removed in SSP 2.0. Please use SimpleSAML\Utils\HTTP::setCookie() instead.
*/
public static function setCookie($name, $value, array $params = NULL, $throw = TRUE) {
- assert('is_string($name)');
- assert('is_string($value) || is_null($value)');
-
- $default_params = array(
- 'lifetime' => 0,
- 'expire' => NULL,
- 'path' => '/',
- 'domain' => NULL,
- 'secure' => FALSE,
- 'httponly' => TRUE,
- 'raw' => FALSE,
- );
-
- if ($params !== NULL) {
- $params = array_merge($default_params, $params);
- } else {
- $params = $default_params;
- }
-
- // Do not set secure cookie if not on HTTPS
- if ($params['secure'] && !self::isHTTPS()) {
- SimpleSAML_Logger::warning('Setting secure cookie on http not allowed.');
- return;
- }
-
- if ($value === NULL) {
- $expire = time() - 365*24*60*60;
- } elseif (isset($params['expire'])) {
- $expire = $params['expire'];
- } elseif ($params['lifetime'] === 0) {
- $expire = 0;
- } else {
- $expire = time() + $params['lifetime'];
- }
-
- if ($params['raw']) {
- $success = setrawcookie($name, $value, $expire, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
- } else {
- $success = setcookie($name, $value, $expire, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
- }
-
- if (!$success) {
- if ($throw) {
- throw new SimpleSAML_Error_Exception('Error setting cookie - headers already sent.');
- } else {
- SimpleSAML_Logger::warning('Error setting cookie - headers already sent.');
- }
- }
+ \SimpleSAML\Utils\HTTP::setCookie($name, $value, $params, $throw);
}
}
diff --git a/lib/SimpleSAML/Utils/Arrays.php b/lib/SimpleSAML/Utils/Arrays.php
new file mode 100644
index 0000000..a620a11
--- /dev/null
+++ b/lib/SimpleSAML/Utils/Arrays.php
@@ -0,0 +1,104 @@
+<?php
+namespace SimpleSAML\Utils;
+
+/**
+ * Array-related utility methods.
+ *
+ * @package SimpleSAMLphp
+ */
+class Arrays
+{
+
+ /**
+ * Put a non-array variable into an array.
+ *
+ * @param array $data The data to place into an array.
+ * @param mixed $index The index or key of the array where to place the data. Defaults to 0.
+ *
+ * @return array An array with one element containing $data, with key $index, or $data itself if it's already an
+ * array.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function arrayize($data, $index = 0)
+ {
+ return (is_array($data)) ? $data : array($index => $data);
+ }
+
+ /**
+ * Validate and normalize an array with attributes.
+ *
+ * This function takes in an associative array with attributes, and parses and validates
+ * this array. On success, it will return a normalized array, where each attribute name
+ * is an index to an array of one or more strings. On failure an exception will be thrown.
+ * This exception will contain an message describing what is wrong.
+ *
+ * @param array $attributes The array containing attributes that we should validate and normalize.
+ *
+ * @return array The normalized attributes array.
+ * @throws \InvalidArgumentException If input is not an array, array keys are not strings or attribute values are
+ * not strings.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function normalizeAttributesArray($attributes)
+ {
+
+ if (!is_array($attributes)) {
+ throw new \InvalidArgumentException('Attributes was not an array. Was: '.print_r($attributes, true).'".');
+ }
+
+ $newAttrs = array();
+ foreach ($attributes as $name => $values) {
+ if (!is_string($name)) {
+ throw new \InvalidArgumentException('Invalid attribute name: "'.print_r($name, true).'".');
+ }
+
+ $values = self::arrayize($values);
+
+ foreach ($values as $value) {
+ if (!is_string($value)) {
+ throw new \InvalidArgumentException('Invalid attribute value for attribute '.$name.
+ ': "'.print_r($value, true).'".');
+ }
+ }
+
+ $newAttrs[$name] = $values;
+ }
+
+ return $newAttrs;
+ }
+
+ /**
+ * This function transposes a two-dimensional array, so that $a['k1']['k2'] becomes $a['k2']['k1'].
+ *
+ * @param array $array The two-dimensional array to transpose.
+ *
+ * @return mixed The transposed array, or false if $array is not a valid two-dimensional array.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ */
+ public static function transpose($array)
+ {
+ if (!is_array($array)) {
+ return false;
+ }
+
+ $ret = array();
+ foreach ($array as $k1 => $a2) {
+ if (!is_array($a2)) {
+ return false;
+ }
+
+ foreach ($a2 as $k2 => $v) {
+ if (!array_key_exists($k2, $ret)) {
+ $ret[$k2] = array();
+ }
+ $ret[$k2][$k1] = $v;
+ }
+ }
+ return $ret;
+ }
+} \ No newline at end of file
diff --git a/lib/SimpleSAML/Utils/Auth.php b/lib/SimpleSAML/Utils/Auth.php
new file mode 100644
index 0000000..089f94e
--- /dev/null
+++ b/lib/SimpleSAML/Utils/Auth.php
@@ -0,0 +1,72 @@
+<?php
+namespace SimpleSAML\Utils;
+
+/**
+ * Auth-related utility methods.
+ *
+ * @package SimpleSAMLphp
+ */
+class Auth
+{
+
+ /**
+ * Retrieve a admin login URL.
+ *
+ * @param string|NULL $returnTo The URL the user should arrive on after admin authentication. Defaults to null.
+ *
+ * @return string A URL which can be used for admin authentication.
+ * @throws \InvalidArgumentException If $returnTo is neither a string nor null.
+ */
+ public static function getAdminLoginURL($returnTo = null)
+ {
+ if (!(is_string($returnTo) || is_null($returnTo))) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ if ($returnTo === null) {
+ $returnTo = \SimpleSAML\Utils\HTTP::getSelfURL();
+ }
+
+ return \SimpleSAML_Module::getModuleURL('core/login-admin.php', array('ReturnTo' => $returnTo));
+ }
+
+ /**
+ * Check whether the current user is admin.
+ *
+ * @return boolean True if the current user is an admin user, false otherwise.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function isAdmin()
+ {
+ $session = \SimpleSAML_Session::getSessionFromRequest();
+ return $session->isValid('admin') || $session->isValid('login-admin');
+ }
+
+ /**
+ * Require admin access to the current page.
+ *
+ * This is a helper function for limiting a page to those with administrative access. It will redirect the user to
+ * a login page if the current user doesn't have admin access.
+ *
+ * @return void This function will only return if the user is admin.
+ * @throws \SimpleSAML_Error_Exception If no "admin" authentication source was configured.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function requireAdmin()
+ {
+ if (self::isAdmin()) {
+ return;
+ }
+
+ // not authenticated as admin user, start authentication
+ if (\SimpleSAML_Auth_Source::getById('admin') !== null) {
+ $as = new \SimpleSAML_Auth_Simple('admin');
+ $as->login();
+ } else {
+ throw new \SimpleSAML_Error_Exception('Cannot find "admin" auth source, and admin privileges are required.');
+ }
+ }
+} \ No newline at end of file
diff --git a/lib/SimpleSAML/Utils/Config.php b/lib/SimpleSAML/Utils/Config.php
new file mode 100644
index 0000000..e0c3f57
--- /dev/null
+++ b/lib/SimpleSAML/Utils/Config.php
@@ -0,0 +1,58 @@
+<?php
+namespace SimpleSAML\Utils;
+
+/**
+ * Utility class for SimpleSAMLphp configuration management and manipulation.
+ *
+ * @package SimpleSAMLphp
+ */
+class Config
+{
+
+ /**
+ * Resolves a path that may be relative to the cert-directory.
+ *
+ * @param string $path The (possibly relative) path to the file.
+ *
+ * @return string The file path.
+ * @throws \InvalidArgumentException If $path is not a string.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function getCertPath($path)
+ {
+ if (!is_string($path)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $globalConfig = \SimpleSAML_Configuration::getInstance();
+ $base = $globalConfig->getPathValue('certdir', 'cert/');
+ return System::resolvePath($path, $base);
+ }
+
+
+ /**
+ * Retrieve the secret salt.
+ *
+ * This function retrieves the value which is configured as the secret salt. It will check that the value exists
+ * and is set to a non-default value. If it isn't, an exception will be thrown.
+ *
+ * The secret salt can be used as a component in hash functions, to make it difficult to test all possible values
+ * in order to retrieve the original value. It can also be used as a simple method for signing data, by hashing the
+ * data together with the salt.
+ *
+ * @return string The secret salt.
+ * @throws \InvalidArgumentException If the secret salt hasn't been configured.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function getSecretSalt()
+ {
+ $secretSalt = \SimpleSAML_Configuration::getInstance()->getString('secretsalt');
+ if ($secretSalt === 'defaultsecretsalt') {
+ throw new \InvalidArgumentException('The "secretsalt" configuration option must be set to a secret value.');
+ }
+
+ return $secretSalt;
+ }
+} \ No newline at end of file
diff --git a/lib/SimpleSAML/Utils/Config/Metadata.php b/lib/SimpleSAML/Utils/Config/Metadata.php
index d85abe4..82c47ba 100644
--- a/lib/SimpleSAML/Utils/Config/Metadata.php
+++ b/lib/SimpleSAML/Utils/Config/Metadata.php
@@ -1,11 +1,13 @@
<?php
+namespace SimpleSAML\Utils\Config;
+
/**
* Class with utilities to fetch different configuration objects from metadata configuration arrays.
*
* @package SimpleSAMLphp
* @author Jaime Pérez Crespo, UNINETT AS <jaime.perez@uninett.no>
*/
-class SimpleSAML_Utils_Config_Metadata
+class Metadata
{
/**
@@ -13,7 +15,12 @@ class SimpleSAML_Utils_Config_Metadata
* @see "Metadata for the OASIS Security Assertion Markup Language (SAML) V2.0", section 2.3.2.2.
*/
public static $VALID_CONTACT_OPTIONS = array(
- 'contactType', 'emailAddress', 'givenName', 'surName', 'telephoneNumber', 'company',
+ 'contactType',
+ 'emailAddress',
+ 'givenName',
+ 'surName',
+ 'telephoneNumber',
+ 'company',
);
@@ -22,7 +29,11 @@ class SimpleSAML_Utils_Config_Metadata
* @see "Metadata for the OASIS Security Assertion Markup Language (SAML) V2.0", section 2.3.2.2.
*/
public static $VALID_CONTACT_TYPES = array(
- 'technical', 'support', 'administrative', 'billing', 'other',
+ 'technical',
+ 'support',
+ 'administrative',
+ 'billing',
+ 'other',
);
@@ -57,24 +68,28 @@ class SimpleSAML_Utils_Config_Metadata
* otherwise it will just return the name as "givenName" in the resulting array.
*
* @param array $contact The contact to parse and sanitize.
+ *
* @return array An array holding valid contact configuration options. If a key 'name' was part of the input array,
* it will try to decompose the name into its parts, and place the parts into givenName and surName, if those are
* missing.
- * @throws InvalidArgumentException if the contact does not conform to valid configuration rules for contacts.
+ * @throws \InvalidArgumentException If $contact is neither a string nor null, or the contact does not conform to
+ * valid configuration rules for contacts.
*/
public static function getContact($contact)
{
- assert('is_array($contact) || is_null($contact)');
+ if (!(is_array($contact) || is_null($contact))) {
+ throw new \InvalidArgumentException('Invalid input parameters');
+ }
// check the type
if (!isset($contact['contactType']) || !in_array($contact['contactType'], self::$VALID_CONTACT_TYPES, true)) {
$types = join(', ', array_map(
- function($t) {
+ function ($t) {
return '"'.$t.'"';
},
self::$VALID_CONTACT_TYPES
));
- throw new InvalidArgumentException('"contactType" is mandatory and must be one of '. $types.".");
+ throw new \InvalidArgumentException('"contactType" is mandatory and must be one of '.$types.".");
}
// try to fill in givenName and surName from name
@@ -100,34 +115,38 @@ class SimpleSAML_Utils_Config_Metadata
// check givenName
if (isset($contact['givenName']) && (
empty($contact['givenName']) || !is_string($contact['givenName'])
- )) {
- throw new InvalidArgumentException('"givenName" must be a string and cannot be empty.');
+ )
+ ) {
+ throw new \InvalidArgumentException('"givenName" must be a string and cannot be empty.');
}
// check surName
if (isset($contact['surName']) && (
empty($contact['surName']) || !is_string($contact['surName'])
- )) {
- throw new InvalidArgumentException('"surName" must be a string and cannot be empty.');
+ )
+ ) {
+ throw new \InvalidArgumentException('"surName" must be a string and cannot be empty.');
}
// check company
if (isset($contact['company']) && (
empty($contact['company']) || !is_string($contact['company'])
- )) {
- throw new InvalidArgumentException('"company" must be a string and cannot be empty.');
+ )
+ ) {
+ throw new \InvalidArgumentException('"company" must be a string and cannot be empty.');
}
// check emailAddress
if (isset($contact['emailAddress'])) {
if (empty($contact['emailAddress']) ||
- !(is_string($contact['emailAddress']) || is_array($contact['emailAddress']))) {
- throw new InvalidArgumentException('"emailAddress" must be a string or an array and cannot be empty.');
+ !(is_string($contact['emailAddress']) || is_array($contact['emailAddress']))
+ ) {
+ throw new \InvalidArgumentException('"emailAddress" must be a string or an array and cannot be empty.');
}
if (is_array($contact['emailAddress'])) {
foreach ($contact['emailAddress'] as $address) {
if (!is_string($address) || empty($address)) {
- throw new InvalidArgumentException('Email addresses must be a string and cannot be empty.');
+ throw new \InvalidArgumentException('Email addresses must be a string and cannot be empty.');
}
}
}
@@ -136,13 +155,14 @@ class SimpleSAML_Utils_Config_Metadata
// check telephoneNumber
if (isset($contact['telephoneNumber'])) {
if (empty($contact['telephoneNumber']) ||
- !(is_string($contact['telephoneNumber']) || is_array($contact['telephoneNumber']))) {
- throw new InvalidArgumentException('"telephoneNumber" must be a string or an array and cannot be empty.');
+ !(is_string($contact['telephoneNumber']) || is_array($contact['telephoneNumber']))
+ ) {
+ throw new \InvalidArgumentException('"telephoneNumber" must be a string or an array and cannot be empty.');
}
if (is_array($contact['telephoneNumber'])) {
foreach ($contact['telephoneNumber'] as $address) {
if (!is_string($address) || empty($address)) {
- throw new InvalidArgumentException('Telephone numbers must be a string and cannot be empty.');
+ throw new \InvalidArgumentException('Telephone numbers must be a string and cannot be empty.');
}
}
}
@@ -152,4 +172,55 @@ class SimpleSAML_Utils_Config_Metadata
return array_intersect_key($contact, array_flip(self::$VALID_CONTACT_OPTIONS));
}
+
+ /**
+ * Find the default endpoint in an endpoint array.
+ *
+ * @param array $endpoints An array with endpoints.
+ * @param array $bindings An array with acceptable bindings. Can be null if any binding is allowed.
+ *
+ * @return array|NULL The default endpoint, or null if no acceptable endpoints are used.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function getDefaultEndpoint(array $endpoints, array $bindings = null)
+ {
+ $firstNotFalse = null;
+ $firstAllowed = null;
+
+ // look through the endpoint list for acceptable endpoints
+ foreach ($endpoints as $i => $ep) {
+ if ($bindings !== null && !in_array($ep['Binding'], $bindings, true)) {
+ // unsupported binding, skip it
+ continue;
+ }
+
+ if (array_key_exists('isDefault', $ep)) {
+ if ($ep['isDefault'] === true) {
+ // this is the first endpoint with isDefault set to true
+ return $ep;
+ }
+ // isDefault is set to false, but the endpoint is still usable as a last resort
+ if ($firstAllowed === null) {
+ // this is the first endpoint that we can use
+ $firstAllowed = $ep;
+ }
+ } else {
+ if ($firstNotFalse === null) {
+ // this is the first endpoint without isDefault set
+ $firstNotFalse = $ep;
+ }
+ }
+ }
+
+ if ($firstNotFalse !== null) {
+ // we have an endpoint without isDefault set to false
+ return $firstNotFalse;
+ }
+
+ /* $firstAllowed either contains the first endpoint we can use, or it contains null if we cannot use any of the
+ * endpoints. Either way we return its value.
+ */
+ return $firstAllowed;
+ }
}
diff --git a/lib/SimpleSAML/Utils/Crypto.php b/lib/SimpleSAML/Utils/Crypto.php
index 1bf2dd2..e09bbfe 100644
--- a/lib/SimpleSAML/Utils/Crypto.php
+++ b/lib/SimpleSAML/Utils/Crypto.php
@@ -1,142 +1,334 @@
<?php
+namespace SimpleSAML\Utils;
+
/**
- * A class for crypto related functions
+ * A class for cryptography-related functions
*
- * @author Dyonisius Visser, TERENA. <visser@terena.org>
- * @package simpleSAMLphp
+ * @package SimpleSAMLphp
*/
-class SimpleSAML_Utils_Crypto {
-
- /**
- * This function generates a password hash
- * @param $password The unencrypted password
- * @param $algo The hashing algorithm, capitals, optionally prepended with 'S' (salted)
- * @param $salt Optional salt
- */
- public static function pwHash($password, $algo, $salt = NULL) {
- assert('is_string($algo)');
- assert('is_string($password)');
-
- if(in_array(strtolower($algo), hash_algos())) {
- $php_algo = strtolower($algo); // 'sha256' etc
- // LDAP compatibility
- return '{' . preg_replace('/^SHA1$/', 'SHA', $algo) . '}'
- .base64_encode(hash($php_algo, $password, TRUE));
- }
-
- // Salt
- if(!$salt) {
- // Default 8 byte salt, but 4 byte for LDAP SHA1 hashes
- $bytes = ($algo == 'SSHA1') ? 4 : 8;
- $salt = SimpleSAML_Utilities::generateRandomBytes($bytes);
- }
-
- if($algo[0] == 'S' && in_array(substr(strtolower($algo),1), hash_algos())) {
- $php_algo = substr(strtolower($algo),1); // 'sha256' etc
- // Salted hash, with LDAP compatibility
- return '{' . preg_replace('/^SSHA1$/', 'SSHA', $algo) . '}' .
- base64_encode(hash($php_algo, $password.$salt, TRUE) . $salt);
- }
-
- throw new Exception('Hashing algoritm \'' . strtolower($algo) . '\' not supported');
-
- }
-
-
- /**
- * This function checks if a password is valid
- * @param $crypted Password as appears in password file, optionally prepended with algorithm
- * @param $clear Password to check
- */
- public static function pwValid($crypted, $clear) {
- assert('is_string($crypted)');
- assert('is_string($clear)');
-
- // Match algorithm string ('{SSHA256}', '{MD5}')
- if(preg_match('/^{(.*?)}(.*)$/', $crypted, $matches)) {
-
- // LDAP compatibility
- $algo = preg_replace('/^(S?SHA)$/', '${1}1', $matches[1]);
-
- $cryptedpw = $matches[2];
-
- if(in_array(strtolower($algo), hash_algos())) {
- // Unsalted hash
- return ( $crypted == self::pwHash($clear, $algo) );
- }
-
- if($algo[0] == 'S' && in_array(substr(strtolower($algo),1), hash_algos())) {
- $php_algo = substr(strtolower($algo),1);
- // Salted hash
- $hash_length = strlen(hash($php_algo, 'whatever', TRUE));
- $salt = substr(base64_decode($cryptedpw), $hash_length);
- return ( $crypted == self::pwHash($clear, $algo, $salt) );
- }
-
- throw new Exception('Hashing algoritm \'' . strtolower($algo) . '\' not supported');
-
- } else {
- return $crypted === $clear;
- }
- }
-
- /**
- * This function generates an Apache 'apr1' password hash, which uses a modified
- * version of MD5: http://httpd.apache.org/docs/2.2/misc/password_encryptions.html
- * @param $password The unencrypted password
- * @param $salt Optional salt
- */
- public static function apr1Md5Hash($password, $salt = NULL) {
- assert('is_string($password)');
-
- $chars = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
- if(!$salt) {
- $salt = substr(str_shuffle($allowed_chars), 0, 8);
- }
-
- $len = strlen($password);
- $text = $password.'$apr1$'.$salt;
- $bin = pack("H32", md5($password.$salt.$password));
- for($i = $len; $i > 0; $i -= 16) { $text .= substr($bin, 0, min(16, $i)); }
- for($i = $len; $i > 0; $i >>= 1) { $text .= ($i & 1) ? chr(0) : $password{0}; }
- $bin = pack("H32", md5($text));
- for($i = 0; $i < 1000; $i++) {
- $new = ($i & 1) ? $password : $bin;
- if ($i % 3) $new .= $salt;
- if ($i % 7) $new .= $password;
- $new .= ($i & 1) ? $bin : $password;
- $bin = pack("H32", md5($new));
- }
- $tmp= '';
- for ($i = 0; $i < 5; $i++) {
- $k = $i + 6;
- $j = $i + 12;
- if ($j == 16) $j = 5;
- $tmp = $bin[$i].$bin[$k].$bin[$j].$tmp;
- }
- $tmp = chr(0).chr(0).$bin[11].$tmp;
- $tmp = strtr(
- strrev(substr(base64_encode($tmp), 2)),
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
- $chars
- );
- return "$"."apr1"."$".$salt."$".$tmp;
- }
-
-
- /**
- * This function verifies an Apache 'apr1' password hash
- */
- public static function apr1Md5Valid($crypted, $clear) {
- assert('is_string($crypted)');
- assert('is_string($clear)');
- $pattern = '/^\$apr1\$([A-Za-z0-9\.\/]{8})\$([A-Za-z0-9\.\/]{22})$/';
-
- if(preg_match($pattern, $crypted, $matches)) {
- $salt = $matches[1];
- return ( $crypted == self::apr1Md5Hash($clear, $salt) );
- }
- return FALSE;
- }
+class Crypto
+{
+
+ /**
+ * Decrypt data using AES and the system-wide secret salt as key.
+ *
+ * @param string $ciphertext The encrypted data to decrypt.
+ *
+ * @return string The decrypted data.
+ * @htorws \InvalidArgumentException If $ciphertext is not a string.
+ * @throws \SimpleSAML_Error_Exception If the mcrypt module is not loaded.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function aesDecrypt($ciphertext)
+ {
+ if (!is_string($ciphertext)) {
+ throw new \InvalidArgumentException('Input parameter "$ciphertext" must be a string.');
+ }
+ if (!function_exists("mcrypt_encrypt")) {
+ throw new \SimpleSAML_Error_Exception("The mcrypt PHP module is not loaded.");
+ }
+
+ $enc = MCRYPT_RIJNDAEL_256;
+ $mode = MCRYPT_MODE_CBC;
+
+ $ivSize = mcrypt_get_iv_size($enc, $mode);
+ $keySize = mcrypt_get_key_size($enc, $mode);
+
+ $key = hash('sha256', Config::getSecretSalt(), true);
+ $key = substr($key, 0, $keySize);
+
+ $iv = substr($ciphertext, 0, $ivSize);
+ $data = substr($ciphertext, $ivSize);
+
+ $clear = mcrypt_decrypt($enc, $key, $data, $mode, $iv);
+
+ $len = strlen($clear);
+ $numpad = ord($clear[$len - 1]);
+ $clear = substr($clear, 0, $len - $numpad);
+
+ return $clear;
+ }
+
+
+ /**
+ * Encrypt data using AES and the system-wide secret salt as key.
+ *
+ * @param string $data The data to encrypt.
+ *
+ * @return string The encrypted data and IV.
+ * @throws \InvalidArgumentException If $data is not a string.
+ * @throws \SimpleSAML_Error_Exception If the mcrypt module is not loaded.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function aesEncrypt($data)
+ {
+ if (!is_string($data)) {
+ throw new \InvalidArgumentException('Input parameter "$data" must be a string.');
+ }
+ if (!function_exists("mcrypt_encrypt")) {
+ throw new \SimpleSAML_Error_Exception('The mcrypt PHP module is not loaded.');
+ }
+
+ $enc = MCRYPT_RIJNDAEL_256;
+ $mode = MCRYPT_MODE_CBC;
+
+ $blockSize = mcrypt_get_block_size($enc, $mode);
+ $ivSize = mcrypt_get_iv_size($enc, $mode);
+ $keySize = mcrypt_get_key_size($enc, $mode);
+
+ $key = hash('sha256', Config::getSecretSalt(), true);
+ $key = substr($key, 0, $keySize);
+
+ $len = strlen($data);
+ $numpad = $blockSize - ($len % $blockSize);
+ $data = str_pad($data, $len + $numpad, chr($numpad));
+
+ $iv = openssl_random_pseudo_bytes($ivSize);
+
+ $data = mcrypt_encrypt($enc, $key, $data, $mode, $iv);
+
+ return $iv.$data;
+ }
+
+
+ /**
+ * Load a private key from metadata.
+ *
+ * This function loads a private key from a metadata array. It looks for the following elements:
+ * - 'privatekey': Name of a private key file in the cert-directory.
+ * - 'privatekey_pass': Password for the private key.
+ *
+ * It returns and array with the following elements:
+ * - 'PEM': Data for the private key, in PEM-format.
+ * - 'password': Password for the private key.
+ *
+ * @param \SimpleSAML_Configuration $metadata The metadata array the private key should be loaded from.
+ * @param bool $required Whether the private key is required. If this is true, a
+ * missing key will cause an exception. Defaults to false.
+ * @param string $prefix The prefix which should be used when reading from the metadata
+ * array. Defaults to ''.
+ *
+ * @return array|NULL Extracted private key, or NULL if no private key is present.
+ * @throws \InvalidArgumentException If $required is not boolean or $prefix is not a string.
+ * @throws \SimpleSAML_Error_Exception If no private key is found in the metadata, or it was not possible to load
+ * it.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function loadPrivateKey(\SimpleSAML_Configuration $metadata, $required = false, $prefix = '')
+ {
+ if (!is_bool($required) || !is_string($prefix)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $file = $metadata->getString($prefix.'privatekey', null);
+ if ($file === null) {
+ // no private key found
+ if ($required) {
+ throw new \SimpleSAML_Error_Exception('No private key found in metadata.');
+ } else {
+ return null;
+ }
+ }
+
+ $file = Config::getCertPath($file);
+ $data = @file_get_contents($file);
+ if ($data === false) {
+ throw new \SimpleSAML_Error_Exception('Unable to load private key from file "'.$file.'"');
+ }
+
+ $ret = array(
+ 'PEM' => $data,
+ );
+
+ if ($metadata->hasValue($prefix.'privatekey_pass')) {
+ $ret['password'] = $metadata->getString($prefix.'privatekey_pass');
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * Get public key or certificate from metadata.
+ *
+ * This function implements a function to retrieve the public key or certificate from a metadata array.
+ *
+ * It will search for the following elements in the metadata:
+ * - 'certData': The certificate as a base64-encoded string.
+ * - 'certificate': A file with a certificate or public key in PEM-format.
+ * - 'certFingerprint': The fingerprint of the certificate. Can be a single fingerprint, or an array of multiple
+ * valid fingerprints.
+ *
+ * This function will return an array with these elements:
+ * - 'PEM': The public key/certificate in PEM-encoding.
+ * - 'certData': The certificate data, base64 encoded, on a single line. (Only present if this is a certificate.)
+ * - 'certFingerprint': Array of valid certificate fingerprints. (Only present if this is a certificate.)
+ *
+ * @param \SimpleSAML_Configuration $metadata The metadata.
+ * @param bool $required Whether the private key is required. If this is TRUE, a missing key
+ * will cause an exception. Default is FALSE.
+ * @param string $prefix The prefix which should be used when reading from the metadata array.
+ * Defaults to ''.
+ *
+ * @return array|NULL Public key or certificate data, or NULL if no public key or certificate was found.
+ * @throws \InvalidArgumentException If $metadata is not an instance of \SimpleSAML_Configuration, $required is not
+ * boolean or $prefix is not a string.
+ * @throws \SimpleSAML_Error_Exception If no private key is found in the metadata, or it was not possible to load
+ * it.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Lasse Birnbaum Jensen
+ */
+ public static function loadPublicKey(\SimpleSAML_Configuration $metadata, $required = false, $prefix = '')
+ {
+ if (!is_bool($required) || !is_string($prefix)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $keys = $metadata->getPublicKeys(null, false, $prefix);
+ if ($keys !== null) {
+ foreach ($keys as $key) {
+ if ($key['type'] !== 'X509Certificate') {
+ continue;
+ }
+ if ($key['signing'] !== true) {
+ continue;
+ }
+ $certData = $key['X509Certificate'];
+ $pem = "-----BEGIN CERTIFICATE-----\n".
+ chunk_split($certData, 64).
+ "-----END CERTIFICATE-----\n";
+ $certFingerprint = strtolower(sha1(base64_decode($certData)));
+
+ return array(
+ 'certData' => $certData,
+ 'PEM' => $pem,
+ 'certFingerprint' => array($certFingerprint),
+ );
+ }
+ // no valid key found
+ } elseif ($metadata->hasValue($prefix.'certFingerprint')) {
+ // we only have a fingerprint available
+ $fps = $metadata->getArrayizeString($prefix.'certFingerprint');
+
+ // normalize fingerprint(s) - lowercase and no colons
+ foreach ($fps as &$fp) {
+ assert('is_string($fp)');
+ $fp = strtolower(str_replace(':', '', $fp));
+ }
+
+ // We can't build a full certificate from a fingerprint, and may as well return an array with only the
+ //fingerprint(s) immediately.
+ return array('certFingerprint' => $fps);
+ }
+
+ // no public key/certificate available
+ if ($required) {
+ throw new \SimpleSAML_Error_Exception('No public key / certificate found in metadata.');
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * This function hashes a password with a given algorithm.
+ *
+ * @param string $password The password to hash.
+ * @param string $algorithm The hashing algorithm, uppercase, optionally prepended with 'S' (salted). See
+ * hash_algos() for a complete list of hashing algorithms.
+ * @param string $salt An optional salt to use.
+ *
+ * @return string The hashed password.
+ * @throws \InvalidArgumentException If the input parameters are not strings.
+ * @throws \SimpleSAML_Error_Exception If the algorithm specified is not supported.
+ *
+ * @see hash_algos()
+ *
+ * @author Dyonisius Visser, TERENA <visser@terena.org>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function pwHash($password, $algorithm, $salt = null)
+ {
+ if (!is_string($algorithm) || !is_string($password)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ // hash w/o salt
+ if (in_array(strtolower($algorithm), hash_algos())) {
+ $alg_str = '{'.str_replace('SHA1', 'SHA', $algorithm).'}'; // LDAP compatibility
+ $hash = hash(strtolower($algorithm), $password, true);
+ return $alg_str.base64_encode($hash);
+ }
+
+ // hash w/ salt
+ if (!$salt) { // no salt provided, generate one
+ // default 8 byte salt, but 4 byte for LDAP SHA1 hashes
+ $bytes = ($algorithm == 'SSHA1') ? 4 : 8;
+ $salt = openssl_random_pseudo_bytes($bytes);
+ }
+
+ if ($algorithm[0] == 'S' && in_array(substr(strtolower($algorithm), 1), hash_algos())) {
+ $alg = substr(strtolower($algorithm), 1); // 'sha256' etc
+ $alg_str = '{'.str_replace('SSHA1', 'SSHA', $algorithm).'}'; // LDAP compatibility
+ $hash = hash($alg, $password.$salt, true);
+ return $alg_str.base64_encode($hash.$salt);
+ }
+
+ throw new \SimpleSAML_Error_Exception('Hashing algorithm \''.strtolower($algorithm).'\' is not supported');
+ }
+
+
+ /**
+ * This function checks if a password is valid
+ *
+ * @param string $hash The password as it appears in password file, optionally prepended with algorithm.
+ * @param string $password The password to check in clear.
+ *
+ * @return boolean True if the hash corresponds with the given password, false otherwise.
+ * @throws \InvalidArgumentException If the input parameters are not strings.
+ * @throws \SimpleSAML_Error_Exception If the algorithm specified is not supported.
+ *
+ * @author Dyonisius Visser, TERENA <visser@terena.org>
+ */
+ public static function pwValid($hash, $password)
+ {
+ if (!is_string($hash) || !is_string($password)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ // match algorithm string (e.g. '{SSHA256}', '{MD5}')
+ if (preg_match('/^{(.*?)}(.*)$/', $hash, $matches)) {
+
+ // LDAP compatibility
+ $alg = preg_replace('/^(S?SHA)$/', '${1}1', $matches[1]);
+
+ // hash w/o salt
+ if (in_array(strtolower($alg), hash_algos())) {
+ return $hash === self::pwHash($password, $alg);
+ }
+
+ // hash w/ salt
+ if ($alg[0] === 'S' && in_array(substr(strtolower($alg), 1), hash_algos())) {
+ $php_alg = substr(strtolower($alg), 1);
+
+ // get hash length of this algorithm to learn how long the salt is
+ $hash_length = strlen(hash($php_alg, '', true));
+ $salt = substr(base64_decode($matches[2]), $hash_length);
+ return ($hash === self::pwHash($password, $alg, $salt));
+ }
+ } else {
+ return $hash === $password;
+ }
+
+ throw new \SimpleSAML_Error_Exception('Hashing algorithm \''.strtolower($alg).'\' is not supported');
+ }
}
diff --git a/lib/SimpleSAML/Utils/HTTP.php b/lib/SimpleSAML/Utils/HTTP.php
new file mode 100644
index 0000000..7518bfa
--- /dev/null
+++ b/lib/SimpleSAML/Utils/HTTP.php
@@ -0,0 +1,1027 @@
+<?php
+namespace SimpleSAML\Utils;
+
+
+/**
+ * HTTP-related utility methods.
+ *
+ * @package SimpleSAMLphp
+ */
+class HTTP
+{
+
+ /**
+ * Obtain a URL where we can redirect to securely post a form with the given data to a specific destination.
+ *
+ * @param string $destination The destination URL.
+ * @param array $data An associative array containing the data to be posted to $destination.
+ *
+ * @return string A URL which allows to securely post a form to $destination.
+ *
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ private static function getSecurePOSTRedirectURL($destination, $data)
+ {
+ $session = \SimpleSAML_Session::getSessionFromRequest();
+ $id = self::savePOSTData($session, $destination, $data);
+
+ // encrypt the session ID and the random ID
+ $info = base64_encode(Crypto::aesEncrypt($session->getSessionId().':'.$id));
+
+ $url = \SimpleSAML_Module::getModuleURL('core/postredirect.php', array('RedirInfo' => $info));
+ return preg_replace('#^https:#', 'http:', $url);
+ }
+
+
+ /**
+ * Retrieve Host value from $_SERVER environment variables.
+ *
+ * @return string The current host name, including the port if needed. It will use localhost when unable to
+ * determine the current host.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ private static function getServerHost()
+ {
+ if (array_key_exists('HTTP_HOST', $_SERVER)) {
+ $current = $_SERVER['HTTP_HOST'];
+ } elseif (array_key_exists('SERVER_NAME', $_SERVER)) {
+ $current = $_SERVER['SERVER_NAME'];
+ } else {
+ // almost certainly not what you want, but...
+ $current = 'localhost';
+ }
+
+ if (strstr($current, ":")) {
+ $decomposed = explode(":", $current);
+ $port = array_pop($decomposed);
+ if (!is_numeric($port)) {
+ array_push($decomposed, $port);
+ }
+ $current = implode($decomposed, ":");
+ }
+ return $current;
+ }
+
+
+ /**
+ * Retrieve HTTPS status from $_SERVER environment variables.
+ *
+ * @return boolean True if the request was performed through HTTPS, false otherwise.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ private static function getServerHTTPS()
+ {
+ if (!array_key_exists('HTTPS', $_SERVER)) {
+ // not an https-request
+ return false;
+ }
+
+ if ($_SERVER['HTTPS'] === 'off') {
+ // IIS with HTTPS off
+ return false;
+ }
+
+ // otherwise, HTTPS will be a non-empty string
+ return $_SERVER['HTTPS'] !== '';
+ }
+
+
+ /**
+ * Retrieve the port number from $_SERVER environment variables.
+ *
+ * @return string The port number prepended by a colon, if it is different than the default port for the protocol
+ * (80 for HTTP, 443 for HTTPS), or an empty string otherwise.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ private static function getServerPort()
+ {
+ $port = (isset($_SERVER['SERVER_PORT'])) ? $_SERVER['SERVER_PORT'] : '80';
+ if (self::getServerHTTPS()) {
+ if ($port !== '443') {
+ return ':'.$port;
+ }
+ } else {
+ if ($port !== '80') {
+ return ':'.$port;
+ }
+ }
+ return '';
+ }
+
+
+ /**
+ * 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 function 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.
+ *
+ * @return void This function never returns.
+ * @throws \InvalidArgumentException If $url is not a string or is empty, or $parameters is not an array.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Mads Freek Petersen
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ private static function redirect($url, $parameters = array())
+ {
+ if (!is_string($url) || empty($url) || !is_array($parameters)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+ if (!empty($parameters)) {
+ $url = self::addURLParameters($url, $parameters);
+ }
+
+ /* 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' &&
+ $_SERVER['REQUEST_METHOD'] === 'POST'
+ ) {
+ $code = 303;
+ } else {
+ $code = 302;
+ }
+
+ if (strlen($url) > 2048) {
+ \SimpleSAML_Logger::warning('Redirecting to a URL longer than 2048 bytes.');
+ }
+
+ // set the location header
+ header('Location: '.$url, true, $code);
+
+ // disable caching of this response
+ header('Pragma: no-cache');
+ header('Cache-Control: no-cache, must-revalidate');
+
+ // show a minimal web page with a clickable link to the URL
+ echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";
+ echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"';
+ echo ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'."\n";
+ echo '<html xmlns="http://www.w3.org/1999/xhtml">'."\n";
+ echo " <head>\n";
+ echo ' <meta http-equiv="content-type" content="text/html; charset=utf-8">'."\n";
+ echo " <title>Redirect</title>\n";
+ echo " </head>\n";
+ echo " <body>\n";
+ echo " <h1>Redirect</h1>\n";
+ echo ' <p>You were redirected to: <a id="redirlink" href="'.htmlspecialchars($url).'">';
+ echo htmlspecialchars($url)."</a>\n";
+ echo ' <script type="text/javascript">document.getElementById("redirlink").focus();</script>'."\n";
+ echo " </p>\n";
+ echo " </body>\n";
+ echo '</html>';
+
+ // end script execution
+ exit;
+ }
+
+
+ /**
+ * Save the given HTTP POST data and the destination where it should be posted to a given session.
+ *
+ * @param \SimpleSAML_Session $session The session where to temporarily store the data.
+ * @param string $destination The destination URL where the form should be posted.
+ * @param array $data An associative array with the data to be posted to $destination.
+ *
+ * @return string A random identifier that can be used to retrieve the data from the current session.
+ *
+ * @author Andjelko Horvat
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ private static function savePOSTData(\SimpleSAML_Session $session, $destination, $data)
+ {
+ // generate a random ID to avoid replay attacks
+ $id = Random::generateID();
+ $postData = array(
+ 'post' => $data,
+ 'url' => $destination,
+ );
+
+ // save the post data to the session, tied to the random ID
+ $session->setData('core_postdatalink', $id, $postData);
+
+ return $id;
+ }
+
+
+ /**
+ * Add one or more query parameters to the given URL.
+ *
+ * @param string $url The URL the query parameters should be added to.
+ * @param array $parameters The query parameters which should be added to the url. This should be an associative
+ * array.
+ *
+ * @return string The URL with the new query parameters.
+ * @throws \InvalidArgumentException If $url is not a string or $parameters is not an array.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function addURLParameters($url, $parameters)
+ {
+ if (!is_string($url) || !is_array($parameters)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $queryStart = strpos($url, '?');
+ if ($queryStart === false) {
+ $oldQuery = array();
+ $url .= '?';
+ } else {
+ $oldQuery = substr($url, $queryStart + 1);
+ if ($oldQuery === false) {
+ $oldQuery = array();
+ } else {
+ $oldQuery = self::parseQueryString($oldQuery);
+ }
+ $url = substr($url, 0, $queryStart + 1);
+ }
+
+ $query = array_merge($oldQuery, $parameters);
+ $url .= http_build_query($query, '', '&');
+
+ return $url;
+ }
+
+
+ /**
+ * Check for session cookie, and show missing-cookie page if it is missing.
+ *
+ * @param string|NULL $retryURL The URL the user should access to retry the operation. Defaults to null.
+ *
+ * @return void If there is a session cookie, nothing will be returned. Otherwise, the user will be redirected to a
+ * page telling about the missing cookie.
+ * @throws \InvalidArgumentException If $retryURL is neither a string nor null.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function checkSessionCookie($retryURL = null)
+ {
+ if (!is_string($retryURL) || !is_null($retryURL)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $session = \SimpleSAML_Session::getSessionFromRequest();
+ if ($session->hasSessionCookie()) {
+ return;
+ }
+
+ // we didn't have a session cookie. Redirect to the no-cookie page
+
+ $url = \SimpleSAML_Module::getModuleURL('core/no_cookie.php');
+ if ($retryURL !== null) {
+ $url = self::addURLParameters($url, array('retryURL' => $retryURL));
+ }
+ self::redirectTrustedURL($url);
+ }
+
+
+ /**
+ * 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. An empty string if the $url parameter is empty as
+ * defined by the empty() function.
+ * @throws \InvalidArgumentException If the URL is malformed.
+ * @throws \SimpleSAML_Error_Exception If the URL is not allowed by configuration.
+ *
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ 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);
+ // TODO: remove this before 2.0
+ 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;
+ }
+
+
+ /**
+ * Helper function to retrieve a file or URL with proxy support.
+ *
+ * An exception will be thrown if we are unable to retrieve the data.
+ *
+ * @param string $url The path or URL we should fetch.
+ * @param array $context Extra context options. This parameter is optional.
+ * @param boolean $getHeaders Whether to also return response headers. Optional.
+ *
+ * @return mixed array if $getHeaders is set, string otherwise
+ * @throws \InvalidArgumentException If the input parameters are invalid.
+ * @throws \SimpleSAML_Error_Exception If the file or URL cannot be retrieved.
+ *
+ * @author Andjelko Horvat
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Marco Ferrante, University of Genova <marco@csita.unige.it>
+ */
+ public static function fetch($url, $context = array(), $getHeaders = false)
+ {
+ if (!is_string($url)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $config = \SimpleSAML_Configuration::getInstance();
+
+ $proxy = $config->getString('proxy', null);
+ if ($proxy !== null) {
+ if (!isset($context['http']['proxy'])) {
+ $context['http']['proxy'] = $proxy;
+ }
+ if (!isset($context['http']['request_fulluri'])) {
+ $context['http']['request_fulluri'] = true;
+ }
+ /*
+ * If the remote endpoint over HTTPS uses the SNI extension (Server Name Indication RFC 4366), the proxy
+ * could introduce a mismatch between the names in the Host: HTTP header and the SNI_server_name in TLS
+ * negotiation (thanks to Cristiano Valli @ GARR-IDEM to have pointed this problem).
+ * See: https://bugs.php.net/bug.php?id=63519
+ * These controls will force the same value for both fields.
+ * Marco Ferrante (marco@csita.unige.it), Nov 2012
+ */
+ if (preg_match('#^https#i', $url)
+ && defined('OPENSSL_TLSEXT_SERVER_NAME')
+ && OPENSSL_TLSEXT_SERVER_NAME
+ ) {
+ // extract the hostname
+ $hostname = parse_url($url, PHP_URL_HOST);
+ if (!empty($hostname)) {
+ $context['ssl'] = array(
+ 'SNI_server_name' => $hostname,
+ 'SNI_enabled' => true,
+ );
+ } else {
+ \SimpleSAML_Logger::warning('Invalid URL format or local URL used through a proxy');
+ }
+ }
+ }
+
+ $context = stream_context_create($context);
+ $data = file_get_contents($url, false, $context);
+ if ($data === false) {
+ $error = error_get_last();
+ throw new \SimpleSAML_Error_Exception('Error fetching '.var_export($url, true).':'.$error['message']);
+ }
+
+ // data and headers.
+ if ($getHeaders) {
+ if (isset($http_response_header)) {
+ $headers = array();
+ foreach ($http_response_header as $h) {
+ if (preg_match('@^HTTP/1\.[01]\s+\d{3}\s+@', $h)) {
+ $headers = array(); // reset
+ $headers[0] = $h;
+ continue;
+ }
+ $bits = explode(':', $h, 2);
+ if (count($bits) === 2) {
+ $headers[strtolower($bits[0])] = trim($bits[1]);
+ }
+ }
+ } else {
+ // no HTTP headers, probably a different protocol, e.g. file
+ $headers = null;
+ }
+ return array($data, $headers);
+ }
+
+ return $data;
+ }
+
+
+ /**
+ * This function parses the Accept-Language HTTP header and returns an associative array with each language and the
+ * score for that language. If a language includes a region, then the result will include both the language with
+ * the region and the language without the region.
+ *
+ * The returned array will be in the same order as the input.
+ *
+ * @return array An associative array with each language and the score for that language.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function getAcceptLanguage()
+ {
+ if (!array_key_exists('HTTP_ACCEPT_LANGUAGE', $_SERVER)) {
+ // no Accept-Language header, return an empty set
+ return array();
+ }
+
+ $languages = explode(',', strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']));
+
+ $ret = array();
+
+ foreach ($languages as $l) {
+ $opts = explode(';', $l);
+
+ $l = trim(array_shift($opts)); // the language is the first element
+
+ $q = 1.0;
+
+ // iterate over all options, and check for the quality option
+ foreach ($opts as $o) {
+ $o = explode('=', $o);
+ if (count($o) < 2) {
+ // skip option with no value
+ continue;
+ }
+
+ $name = trim($o[0]);
+ $value = trim($o[1]);
+
+ if ($name === 'q') {
+ $q = (float) $value;
+ }
+ }
+
+ // remove the old key to ensure that the element is added to the end
+ unset($ret[$l]);
+
+ // set the quality in the result
+ $ret[$l] = $q;
+
+ if (strpos($l, '-')) {
+ // the language includes a region part
+
+ // extract the language without the region
+ $l = explode('-', $l);
+ $l = $l[0];
+
+ // add this language to the result (unless it is defined already)
+ if (!array_key_exists($l, $ret)) {
+ $ret[$l] = $q;
+ }
+ }
+ }
+ return $ret;
+ }
+
+
+ /**
+ * Retrieve the base URL of the SimpleSAMLphp installation. The URL will always end with a '/'. For example:
+ * https://idp.example.org/simplesaml/
+ *
+ * @return string The absolute base URL for the simpleSAMLphp installation.
+ * @throws \SimpleSAML_Error_Exception If 'baseurlpath' has an invalid format.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function getBaseURL()
+ {
+ $globalConfig = \SimpleSAML_Configuration::getInstance();
+ $baseURL = $globalConfig->getString('baseurlpath', 'simplesaml/');
+
+ if (preg_match('#^https?://.*/$#D', $baseURL, $matches)) {
+ // full URL in baseurlpath, override local server values
+ return $baseURL;
+ } elseif (
+ (preg_match('#^/?([^/]?.*/)$#D', $baseURL, $matches)) ||
+ (preg_match('#^\*(.*)/$#D', $baseURL, $matches)) ||
+ ($baseURL === '')
+ ) {
+ // get server values
+ $protocol = 'http';
+ $protocol .= (self::getServerHTTPS()) ? 's' : '';
+ $protocol .= '://';
+
+ $hostname = self::getServerHost();
+ $port = self::getServerPort();
+ $path = '/'.$globalConfig->getBaseURL();
+
+ return $protocol.$hostname.$port.$path;
+ } else {
+ throw new \SimpleSAML_Error_Exception('Invalid value for \'baseurlpath\' in '.
+ 'config.php. Valid format is in the form: '.
+ '[(http|https)://(hostname|fqdn)[:port]]/[path/to/simplesaml/]. '.
+ 'It must end with a \'/\'.');
+ }
+ }
+
+
+ /**
+ * Retrieve the first element of the URL path.
+ *
+ * @param boolean $trailingslash Whether to add a trailing slash to the element or not. Defaults to true.
+ *
+ * @return string The first element of the URL path, with an optional, trailing slash.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ */
+ public static function getFirstPathElement($trailingslash = true)
+ {
+ if (preg_match('|^/(.*?)/|', $_SERVER['SCRIPT_NAME'], $matches)) {
+ return ($trailingslash ? '/' : '').$matches[1];
+ }
+ return '';
+ }
+
+
+ /**
+ * Create a link which will POST data.
+ *
+ * @param string $destination The destination URL.
+ * @param array $data The name-value pairs which will be posted to the destination.
+ *
+ * @return string A URL which can be accessed to post the data.
+ * @throws \InvalidArgumentException If $destination is not a string or $data is not an array.
+ *
+ * @author Andjelko Horvat
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function getPOSTRedirectURL($destination, $data)
+ {
+ if (!is_string($destination) || !is_array($data)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $config = \SimpleSAML_Configuration::getInstance();
+ $allowed = $config->getBoolean('enable.http_post', false);
+
+ if ($allowed && preg_match("#^http:#", $destination) && self::isHTTPS()) {
+ // we need to post the data to HTTP
+ $url = self::getSecurePOSTRedirectURL($destination, $data);
+ } else { // post the data directly
+ $session = \SimpleSAML_Session::getSessionFromRequest();
+ $id = self::savePOSTData($session, $destination, $data);
+ $url = \SimpleSAML_Module::getModuleURL('core/postredirect.php', array('RedirId' => $id));
+ }
+
+ return $url;
+ }
+
+
+ /**
+ * Retrieve our own host.
+ *
+ * @return string The current host (with non-default ports included).
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function getSelfHost()
+ {
+ $url = self::getBaseURL();
+
+ $start = strpos($url, '://') + 3;
+ $length = strcspn($url, '/:', $start);
+
+ return substr($url, $start, $length);
+ }
+
+
+ /**
+ * Retrieve our own host together with the URL path. Please note this function will return the base URL for the
+ * current SP, as defined in the global configuration.
+ *
+ * @return string The current host (with non-default ports included) plus the URL path.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function getSelfHostWithPath()
+ {
+ $baseurl = explode("/", self::getBaseURL());
+ $elements = array_slice($baseurl, 3 - count($baseurl), count($baseurl) - 4);
+ $path = implode("/", $elements);
+ return self::getSelfHost()."/".$path;
+ }
+
+
+ /**
+ * Retrieve the current, complete URL.
+ *
+ * @return string The current URL, including query parameters.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function getSelfURL()
+ {
+ $url = self::getSelfURLHost();
+ $requestURI = $_SERVER['REQUEST_URI'];
+ if ($requestURI[0] !== '/') {
+ // we probably have a URL of the form: http://server/
+ if (preg_match('#^https?://[^/]*(/.*)#i', $requestURI, $matches)) {
+ $requestURI = $matches[1];
+ }
+ }
+ return $url.$requestURI;
+ }
+
+
+ /**
+ * Retrieve a URL containing the protocol, the current host and optionally, the port number.
+ *
+ * @return string The current URL without a URL path or query parameters.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function getSelfURLHost()
+ {
+ $url = self::getBaseURL();
+ $start = strpos($url, '://') + 3;
+ $length = strcspn($url, '/', $start) + $start;
+ return substr($url, 0, $length);
+ }
+
+
+ /**
+ * Retrieve the current URL without the query parameters.
+ *
+ * @return string The current URL, not including query parameters.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ */
+ public static function getSelfURLNoQuery()
+ {
+ $url = self::getSelfURLHost();
+ $url .= $_SERVER['SCRIPT_NAME'];
+ if (isset($_SERVER['PATH_INFO'])) {
+ $url .= $_SERVER['PATH_INFO'];
+ }
+ return $url;
+ }
+
+
+ /**
+ * This function checks if we are using HTTPS as protocol.
+ *
+ * @return boolean True if the HTTPS is used, false otherwise.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function isHTTPS()
+ {
+ return strpos(self::getBaseURL(), 'https://') === 0;
+ }
+
+
+ /**
+ * Normalizes a URL to an absolute URL and validate it. In addition to resolving the URL, this function makes sure
+ * that it is a link to an http or https site.
+ *
+ * @param string $url The relative URL.
+ *
+ * @return string An absolute URL for the given relative URL.
+ * @throws \InvalidArgumentException If $url is not a string or a valid URL.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function normalizeURL($url)
+ {
+ if (!is_string($url)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $url = self::resolveURL($url, self::getSelfURL());
+
+ // verify that the URL is to a http or https site
+ if (!preg_match('@^https?://@i', $url)) {
+ throw new \InvalidArgumentException('Invalid URL: '.$url);
+ }
+
+ return $url;
+ }
+
+
+ /**
+ * Parse a query string into an array.
+ *
+ * This function parses a query string into an array, similar to the way the builtin 'parse_str' works, except it
+ * doesn't handle arrays, and it doesn't do "magic quotes".
+ *
+ * Query parameters without values will be set to an empty string.
+ *
+ * @param string $query_string The query string which should be parsed.
+ *
+ * @return array The query string as an associative array.
+ * @throws \InvalidArgumentException If $query_string is not a string.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function parseQueryString($query_string)
+ {
+ if (!is_string($query_string)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $res = array();
+ foreach (explode('&', $query_string) as $param) {
+ $param = explode('=', $param);
+ $name = urldecode($param[0]);
+ if (count($param) === 1) {
+ $value = '';
+ } else {
+ $value = urldecode($param[1]);
+ }
+ $res[$name] = $value;
+ }
+ return $res;
+ }
+
+
+ /**
+ * This function redirects to the specified URL without performing any security checks. Please, do NOT use this
+ * function with user supplied URLs.
+ *
+ * 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 function will also generate a simple web page with a clickable link to the target URL.
+ *
+ * @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.
+ *
+ * @return void This function never returns.
+ * @throws \InvalidArgumentException If $url is not a string or $parameters is not an array.
+ *
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function redirectTrustedURL($url, $parameters = array())
+ {
+ if (!is_string($url) || !is_array($parameters)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $url = self::normalizeURL($url);
+ self::redirect($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.
+ *
+ * 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.
+ *
+ * @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.
+ *
+ * @return void This function never returns.
+ * @throws \InvalidArgumentException If $url is not a string or $parameters is not an array.
+ *
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function redirectUntrustedURL($url, $parameters = array())
+ {
+ if (!is_string($url) || !is_array($parameters)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $url = self::checkURLAllowed($url);
+ self::redirect($url, $parameters);
+ }
+
+
+ /**
+ * Resolve a (possibly relative) URL relative to a given base URL.
+ *
+ * This function supports these forms of relative URLs:
+ * - ^\w+: Absolute URL. E.g. "http://www.example.com:port/path?query#fragment".
+ * - ^// Same protocol. E.g. "//www.example.com:port/path?query#fragment".
+ * - ^/ Same protocol and host. E.g. "/path?query#fragment".
+ * - ^? Same protocol, host and path, replace query string & fragment. E.g. "?query#fragment".
+ * - ^# Same protocol, host, path and query, replace fragment. E.g. "#fragment".
+ * - The rest: Relative to the base path.
+ *
+ * @param string $url The relative URL.
+ * @param string $base The base URL. Defaults to the base URL of this installation of SimpleSAMLphp.
+ *
+ * @return string An absolute URL for the given relative URL.
+ * @throws \InvalidArgumentException If the base URL cannot be parsed into a valid URL, or the given parameters
+ * are not strings.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function resolveURL($url, $base = null)
+ {
+ if ($base === null) {
+ $base = self::getBaseURL();
+ }
+
+ if (!is_string($url) || !is_string($base)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ if (!preg_match('/^((((\w+:)\/\/[^\/]+)(\/[^?#]*))(?:\?[^#]*)?)(?:#.*)?/', $base, $baseParsed)) {
+ throw new \InvalidArgumentException('Unable to parse base url: '.$base);
+ }
+
+ $baseDir = dirname($baseParsed[5].'filename');
+ $baseScheme = $baseParsed[4];
+ $baseHost = $baseParsed[3];
+ $basePath = $baseParsed[2];
+ $baseQuery = $baseParsed[1];
+
+ if (preg_match('$^\w+:$', $url)) {
+ return $url;
+ }
+
+ if (substr($url, 0, 2) === '//') {
+ return $baseScheme.$url;
+ }
+
+ $firstChar = substr($url, 0, 1);
+ if ($firstChar === '/') {
+ return $baseHost.$url;
+ }
+ if ($firstChar === '?') {
+ return $basePath.$url;
+ }
+ if ($firstChar === '#') {
+ return $baseQuery.$url;
+ }
+
+ // we have a relative path. Remove query string/fragment and save it as $tail
+ $queryPos = strpos($url, '?');
+ $fragmentPos = strpos($url, '#');
+ if ($queryPos !== false || $fragmentPos !== false) {
+ if ($queryPos === false) {
+ $tailPos = $fragmentPos;
+ } elseif ($fragmentPos === false) {
+ $tailPos = $queryPos;
+ } elseif ($queryPos < $fragmentPos) {
+ $tailPos = $queryPos;
+ } else {
+ $tailPos = $fragmentPos;
+ }
+
+ $tail = substr($url, $tailPos);
+ $dir = substr($url, 0, $tailPos);
+ } else {
+ $dir = $url;
+ $tail = '';
+ }
+
+ $dir = System::resolvePath($dir, $baseDir);
+
+ return $baseHost.$dir.$tail;
+ }
+
+
+ /**
+ * Set a cookie.
+ *
+ * @param string $name The name of the cookie.
+ * @param string|NULL $value The value of the cookie. Set to NULL to delete the cookie.
+ * @param array|NULL $params Cookie parameters.
+ * @param bool $throw Whether to throw exception if setcookie() fails.
+ *
+ * @throws \InvalidArgumentException If any parameter has an incorrect type.
+ * @throws \SimpleSAML_Error_Exception If the headers were already sent and the cookie cannot be set.
+ *
+ * @author Andjelko Horvat
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function setCookie($name, $value, $params = null, $throw = true)
+ {
+ if (!(is_string($name) && // $name must be a string
+ (is_string($value) || is_null($value)) && // $value can be a string or null
+ (is_array($params) || is_null($params)) && // $params can be an array or null
+ is_bool($throw)) // $throw must be boolean
+ ) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $default_params = array(
+ 'lifetime' => 0,
+ 'expire' => null,
+ 'path' => '/',
+ 'domain' => null,
+ 'secure' => false,
+ 'httponly' => true,
+ 'raw' => false,
+ );
+
+ if ($params !== null) {
+ $params = array_merge($default_params, $params);
+ } else {
+ $params = $default_params;
+ }
+
+ // Do not set secure cookie if not on HTTPS
+ if ($params['secure'] && !self::isHTTPS()) {
+ \SimpleSAML_Logger::warning('Setting secure cookie on plain HTTP is not allowed.');
+ return;
+ }
+
+ if ($value === null) {
+ $expire = time() - 365 * 24 * 60 * 60;
+ } elseif (isset($params['expire'])) {
+ $expire = $params['expire'];
+ } elseif ($params['lifetime'] === 0) {
+ $expire = 0;
+ } else {
+ $expire = time() + $params['lifetime'];
+ }
+
+ if ($params['raw']) {
+ $success = setrawcookie($name, $value, $expire, $params['path'], $params['domain'], $params['secure'],
+ $params['httponly']);
+ } else {
+ $success = setcookie($name, $value, $expire, $params['path'], $params['domain'], $params['secure'],
+ $params['httponly']);
+ }
+
+ if (!$success) {
+ if ($throw) {
+ throw new \SimpleSAML_Error_Exception('Error setting cookie: headers already sent.');
+ } else {
+ \SimpleSAML_Logger::warning('Error setting cookie: headers already sent.');
+ }
+ }
+ }
+
+
+ /**
+ * Submit a POST form to a specific destination.
+ *
+ * This function never returns.
+ *
+ * @param string $destination The destination URL.
+ * @param array $data An associative array with the data to be posted to $destination.
+ *
+ * @throws \InvalidArgumentException If $destination is not a string or $data is not an array.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Andjelko Horvat
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function submitPOSTData($destination, $data)
+ {
+ if (!is_string($destination) || !is_array($data)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $config = \SimpleSAML_Configuration::getInstance();
+ $allowed = $config->getBoolean('enable.http_post', false);
+
+ if ($allowed && preg_match("#^http:#", $destination) && self::isHTTPS()) {
+ // we need to post the data to HTTP
+ self::redirect(self::getSecurePOSTRedirectURL($destination, $data));
+ }
+
+ $p = new \SimpleSAML_XHTML_Template($config, 'post.php');
+ $p->data['destination'] = $destination;
+ $p->data['post'] = $data;
+ $p->show();
+ exit(0);
+ }
+} \ No newline at end of file
diff --git a/lib/SimpleSAML/Utils/Net.php b/lib/SimpleSAML/Utils/Net.php
new file mode 100644
index 0000000..22082b7
--- /dev/null
+++ b/lib/SimpleSAML/Utils/Net.php
@@ -0,0 +1,82 @@
+<?php
+namespace SimpleSAML\Utils;
+
+/**
+ * Net-related utility methods.
+ *
+ * @package SimpleSAMLphp
+ */
+class Net
+{
+
+ /**
+ * Check whether an IP address is part of a CIDR.
+ *
+ * @param string $cidr The network CIDR address.
+ * @param string $ip The IP address to check. Optional. Current remote address will be used if none specified. Do
+ * not rely on default parameter if running behind load balancers.
+ *
+ * @return boolean True if the IP address belongs to the specified CIDR, false otherwise.
+ *
+ * @author Andreas Ã…kre Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Brook Schofield, TERENA
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ static function ipCIDRcheck($cidr, $ip = null)
+ {
+ if ($ip === null) {
+ $ip = $_SERVER['REMOTE_ADDR'];
+ }
+ if (strpos($cidr, '/') === false) {
+ return false;
+ }
+
+ list ($net, $mask) = explode('/', $cidr);
+
+ if (strstr($ip, ':') || strstr($net, ':')) {
+ // Validate IPv6 with inet_pton, convert to hex with bin2hex
+ // then store as a long with hexdec
+
+ $ip_pack = inet_pton($ip);
+ $net_pack = inet_pton($net);
+
+ if ($ip_pack === false || $net_pack === false) {
+ // not valid IPv6 address (warning already issued)
+ return false;
+ }
+
+ $ip_ip = str_split(bin2hex($ip_pack), 8);
+ foreach ($ip_ip as &$value) {
+ $value = hexdec($value);
+ }
+
+ $ip_net = str_split(bin2hex($net_pack), 8);
+ foreach ($ip_net as &$value) {
+ $value = hexdec($value);
+ }
+ } else {
+ $ip_ip[0] = ip2long($ip);
+ $ip_net[0] = ip2long($net);
+ }
+
+ for ($i = 0; $mask > 0 && $i < sizeof($ip_ip); $i++) {
+ if ($mask > 32) {
+ $iteration_mask = 32;
+ } else {
+ $iteration_mask = $mask;
+ }
+ $mask -= 32;
+
+ $ip_mask = ~((1 << (32 - $iteration_mask)) - 1);
+
+ $ip_net_mask = $ip_net[$i] & $ip_mask;
+ $ip_ip_mask = $ip_ip[$i] & $ip_mask;
+
+ if ($ip_ip_mask != $ip_net_mask) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/lib/SimpleSAML/Utils/Random.php b/lib/SimpleSAML/Utils/Random.php
new file mode 100644
index 0000000..fc87dcf
--- /dev/null
+++ b/lib/SimpleSAML/Utils/Random.php
@@ -0,0 +1,25 @@
+<?php
+namespace SimpleSAML\Utils;
+
+/**
+ * Utility class for random data generation and manipulation.
+ *
+ * @package SimpleSAMLphp
+ */
+class Random
+{
+
+ /**
+ * Generate a random identifier, 22 bytes long.
+ *
+ * @return string A 22-bytes long string with a random, hex string.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function generateID()
+ {
+ return '_'.bin2hex(openssl_random_pseudo_bytes(21));
+ }
+} \ No newline at end of file
diff --git a/lib/SimpleSAML/Utils/System.php b/lib/SimpleSAML/Utils/System.php
new file mode 100644
index 0000000..8889251
--- /dev/null
+++ b/lib/SimpleSAML/Utils/System.php
@@ -0,0 +1,199 @@
+<?php
+namespace SimpleSAML\Utils;
+
+/**
+ * System-related utility methods.
+ *
+ * @package SimpleSAMLphp
+ */
+class System
+{
+
+ const WINDOWS = 1;
+ const LINUX = 2;
+ const OSX = 3;
+ const HPUX = 4;
+ const UNIX = 5;
+ const BSD = 6;
+ const IRIX = 7;
+ const SUNOS = 8;
+
+
+ /**
+ * This function returns the Operating System we are running on.
+ *
+ * @return mixed A predefined constant identifying the OS we are running on. False if we are unable to determine it.
+ *
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function getOS()
+ {
+ if (stristr(PHP_OS, 'LINUX')) {
+ return self::LINUX;
+ }
+ if (stristr(PHP_OS, 'WIN')) {
+ return self::WINDOWS;
+ }
+ if (stristr(PHP_OS, 'DARWIN')) {
+ return self::OSX;
+ }
+ if (stristr(PHP_OS, 'BSD')) {
+ return self::BSD;
+ }
+ if (stristr(PHP_OS, 'UNIX')) {
+ return self::UNIX;
+ }
+ if (stristr(PHP_OS, 'HP-UX')) {
+ return self::HPUX;
+ }
+ if (stristr(PHP_OS, 'IRIX')) {
+ return self::IRIX;
+ }
+ if (stristr(PHP_OS, 'SUNOS')) {
+ return self::SUNOS;
+ }
+ return false;
+ }
+
+
+ /**
+ * This function retrieves the path to a directory where temporary files can be saved.
+ *
+ * @return string Path to a temporary directory, without a trailing directory separator.
+ * @throws \SimpleSAML_Error_Exception If the temporary directory cannot be created or it exists and does not belong
+ * to the current user.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function getTempDir()
+ {
+ $globalConfig = \SimpleSAML_Configuration::getInstance();
+
+ $tempDir = rtrim($globalConfig->getString('tempdir', sys_get_temp_dir().DIRECTORY_SEPARATOR.'simplesaml'),
+ DIRECTORY_SEPARATOR);
+
+ if (!is_dir($tempDir)) {
+ if (!mkdir($tempDir, 0700, true)) {
+ $error = error_get_last();
+ throw new \SimpleSAML_Error_Exception('Error creating temporary directory "'.$tempDir.
+ '": '.$error['message']);
+ }
+ } elseif (function_exists('posix_getuid')) {
+ // check that the owner of the temp directory is the current user
+ $stat = lstat($tempDir);
+ if ($stat['uid'] !== posix_getuid()) {
+ throw new \SimpleSAML_Error_Exception('Temporary directory "'.$tempDir.
+ '" does not belong to the current user.');
+ }
+ }
+
+ return $tempDir;
+ }
+
+
+ /**
+ * Resolve a (possibly) relative path from the given base path.
+ *
+ * A path which starts with a '/' is assumed to be absolute, all others are assumed to be
+ * relative. The default base path is the root of the SimpleSAMLphp installation.
+ *
+ * @param string $path The path we should resolve.
+ * @param string|null $base The base path, where we should search for $path from. Default value is the root of the
+ * SimpleSAMLphp installation.
+ *
+ * @return string An absolute path referring to $path.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function resolvePath($path, $base = null)
+ {
+ if ($base === null) {
+ $config = \SimpleSAML_Configuration::getInstance();
+ $base = $config->getBaseDir();
+ }
+
+ // remove trailing slashes from $base
+ while (substr($base, -1) === '/') {
+ $base = substr($base, 0, -1);
+ }
+
+ // check for absolute path
+ if (substr($path, 0, 1) === '/') {
+ // absolute path. */
+ $ret = '/';
+ } else {
+ // path relative to base
+ $ret = $base;
+ }
+
+ $path = explode('/', $path);
+ foreach ($path as $d) {
+ if ($d === '.') {
+ continue;
+ } elseif ($d === '..') {
+ $ret = dirname($ret);
+ } else {
+ if (substr($ret, -1) !== '/') {
+ $ret .= '/';
+ }
+ $ret .= $d;
+ }
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * Atomically write a file.
+ *
+ * This is a helper function for writing data atomically to a file. It does this by writing the file data to a
+ * temporary file, then renaming it to the required file name.
+ *
+ * @param string $filename The path to the file we want to write to.
+ * @param string $data The data we should write to the file.
+ * @param int $mode The permissions to apply to the file. Defaults to 0600.
+ *
+ * @throws \InvalidArgumentException If any of the input parameters doesn't have the proper types.
+ * @throws \SimpleSAML_Error_Exception If the file cannot be saved, permissions cannot be changed or it is not
+ * possible to write to the target file.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Andjelko Horvat
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function writeFile($filename, $data, $mode = 0600)
+ {
+ if (!is_string($filename) || !is_string($data) || !is_numeric($mode)) {
+ throw new \InvalidArgumentException('Invalid input parameters');
+ }
+
+ $tmpFile = self::getTempDir().DIRECTORY_SEPARATOR.rand();
+
+ $res = @file_put_contents($tmpFile, $data);
+ if ($res === false) {
+ $error = error_get_last();
+ throw new \SimpleSAML_Error_Exception('Error saving file "'.$tmpFile.
+ '": '.$error['message']);
+ }
+
+ if (self::getOS() !== self::WINDOWS) {
+ if (!chmod($tmpFile, $mode)) {
+ unlink($tmpFile);
+ $error = error_get_last();
+ throw new \SimpleSAML_Error_Exception('Error changing file mode of "'.$tmpFile.
+ '": '.$error['message']);
+ }
+ }
+
+ if (!rename($tmpFile, $filename)) {
+ unlink($tmpFile);
+ $error = error_get_last();
+ throw new \SimpleSAML_Error_Exception('Error moving "'.$tmpFile.'" to "'.
+ $filename.'": '.$error['message']);
+ }
+ }
+}
diff --git a/lib/SimpleSAML/Utils/Time.php b/lib/SimpleSAML/Utils/Time.php
new file mode 100644
index 0000000..9898f8b
--- /dev/null
+++ b/lib/SimpleSAML/Utils/Time.php
@@ -0,0 +1,162 @@
+<?php
+/**
+ * Time-related utility methods.
+ *
+ * @package SimpleSAMLphp
+ */
+
+namespace SimpleSAML\Utils;
+
+
+class Time
+{
+
+ /**
+ * This function generates a timestamp on the form used by the SAML protocols.
+ *
+ * @param int $instant The time the timestamp should represent. Defaults to current time.
+ *
+ * @return string The timestamp.
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function generateTimestamp($instant = null)
+ {
+ if ($instant === null) {
+ $instant = time();
+ }
+ return gmdate('Y-m-d\TH:i:s\Z', $instant);
+ }
+
+
+ /**
+ * Initialize the timezone.
+ *
+ * This function should be called before any calls to date().
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function initTimezone()
+ {
+ static $initialized = false;
+
+ if ($initialized) {
+ return;
+ }
+
+ $initialized = true;
+
+ $globalConfig = \SimpleSAML_Configuration::getInstance();
+
+ $timezone = $globalConfig->getString('timezone', null);
+ if ($timezone !== null) {
+ if (!date_default_timezone_set($timezone)) {
+ throw new \SimpleSAML_Error_Exception('Invalid timezone set in the "timezone" option in config.php.');
+ }
+ return;
+ }
+ // we don't have a timezone configured
+
+ /*
+ * The date_default_timezone_get() function is likely to cause a warning.
+ * Since we have a custom error handler which logs the errors with a backtrace,
+ * this error will be logged even if we prefix the function call with '@'.
+ * Instead we temporarily replace the error handler.
+ */
+ set_error_handler(function () {
+ return true;
+ });
+ $serverTimezone = date_default_timezone_get();
+ restore_error_handler();
+
+ // set the timezone to the default
+ date_default_timezone_set($serverTimezone);
+ }
+
+
+ /**
+ * Interpret a ISO8601 duration value relative to a given timestamp.
+ *
+ * @param string $duration The duration, as a string.
+ * @param int $timestamp The unix timestamp we should apply the duration to. Optional, default to the current
+ * time.
+ *
+ * @return int The new timestamp, after the duration is applied.
+ * @throws \InvalidArgumentException If $duration is not a valid ISO 8601 duration or if the input parameters do
+ * not have the right data types.
+ */
+ public static function parseDuration($duration, $timestamp = null)
+ {
+ if (!(is_string($duration) && (is_int($timestamp) || is_null($timestamp)))) {
+ throw new \InvalidArgumentException('Invalid input parameters');
+ }
+
+ // parse the duration. We use a very strict pattern
+ $durationRegEx = '#^(-?)P(?:(?:(?:(\\d+)Y)?(?:(\\d+)M)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)(?:[.,]\d+)?S)?)?)|(?:(\\d+)W))$#D';
+ if (!preg_match($durationRegEx, $duration, $matches)) {
+ throw new \InvalidArgumentException('Invalid ISO 8601 duration: '.$duration);
+ }
+
+ $durYears = (empty($matches[2]) ? 0 : (int) $matches[2]);
+ $durMonths = (empty($matches[3]) ? 0 : (int) $matches[3]);
+ $durDays = (empty($matches[4]) ? 0 : (int) $matches[4]);
+ $durHours = (empty($matches[5]) ? 0 : (int) $matches[5]);
+ $durMinutes = (empty($matches[6]) ? 0 : (int) $matches[6]);
+ $durSeconds = (empty($matches[7]) ? 0 : (int) $matches[7]);
+ $durWeeks = (empty($matches[8]) ? 0 : (int) $matches[8]);
+
+ if (!empty($matches[1])) {
+ // negative
+ $durYears = -$durYears;
+ $durMonths = -$durMonths;
+ $durDays = -$durDays;
+ $durHours = -$durHours;
+ $durMinutes = -$durMinutes;
+ $durSeconds = -$durSeconds;
+ $durWeeks = -$durWeeks;
+ }
+
+ if ($timestamp === null) {
+ $timestamp = time();
+ }
+
+ if ($durYears !== 0 || $durMonths !== 0) {
+ /* Special handling of months and years, since they aren't a specific interval, but
+ * instead depend on the current time.
+ */
+
+ /* We need the year and month from the timestamp. Unfortunately, PHP doesn't have the
+ * gmtime function. Instead we use the gmdate function, and split the result.
+ */
+ $yearmonth = explode(':', gmdate('Y:n', $timestamp));
+ $year = (int) ($yearmonth[0]);
+ $month = (int) ($yearmonth[1]);
+
+ // remove the year and month from the timestamp
+ $timestamp -= gmmktime(0, 0, 0, $month, 1, $year);
+
+ // add years and months, and normalize the numbers afterwards
+ $year += $durYears;
+ $month += $durMonths;
+ while ($month > 12) {
+ $year += 1;
+ $month -= 12;
+ }
+ while ($month < 1) {
+ $year -= 1;
+ $month += 12;
+ }
+
+ // add year and month back into timestamp
+ $timestamp += gmmktime(0, 0, 0, $month, 1, $year);
+ }
+
+ // add the other elements
+ $timestamp += $durWeeks * 7 * 24 * 60 * 60;
+ $timestamp += $durDays * 24 * 60 * 60;
+ $timestamp += $durHours * 60 * 60;
+ $timestamp += $durMinutes * 60;
+ $timestamp += $durSeconds;
+
+ return $timestamp;
+ }
+} \ No newline at end of file
diff --git a/lib/SimpleSAML/Utils/XML.php b/lib/SimpleSAML/Utils/XML.php
new file mode 100644
index 0000000..bd09a31
--- /dev/null
+++ b/lib/SimpleSAML/Utils/XML.php
@@ -0,0 +1,428 @@
+<?php
+/**
+ * Utility class for XML and DOM manipulation.
+ *
+ * @package SimpleSAMLphp
+ */
+
+namespace SimpleSAML\Utils;
+
+
+class XML
+{
+
+ /**
+ * This function performs some sanity checks on XML documents, and optionally validates them against their schema
+ * if the 'debug.validatexml' option is enabled. A warning will be printed to the log if validation fails.
+ *
+ * @param string $message The SAML document we want to check.
+ * @param string $type The type of document. Can be one of:
+ * - 'saml20'
+ * - 'saml11'
+ * - 'saml-meta'
+ *
+ * @throws \InvalidArgumentException If $message is not a string or $type is not a string containing one of the
+ * values allowed.
+ * @throws \SimpleSAML_Error_Exception If $message contains a doctype declaration.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ * @author Jaime Perez, UNINETT AS <jaime.perez@uninett.no>
+ */
+ public static function checkSAMLMessage($message, $type)
+ {
+ $allowed_types = array('saml20', 'saml11', 'saml-meta');
+ if (!(is_string($message) && in_array($type, $allowed_types))) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ // a SAML message should not contain a doctype-declaration
+ if (strpos($message, '<!DOCTYPE') !== false) {
+ throw new \SimpleSAML_Error_Exception('XML contained a doctype declaration.');
+ }
+
+ $enabled = \SimpleSAML_Configuration::getInstance()->getBoolean('debug.validatexml', null);
+ if (!$enabled) {
+ return;
+ }
+
+ $result = true;
+ switch ($type) {
+ case 'saml11':
+ $result = self::isValid($message, 'oasis-sstc-saml-schema-protocol-1.1.xsd');
+ break;
+ case 'saml20':
+ $result = self::isValid($message, 'saml-schema-protocol-2.0.xsd');
+ break;
+ case 'saml-meta':
+ $result = self::isValid($message, 'saml-schema-metadata-2.0.xsd');
+ }
+ if ($result !== true) {
+ \SimpleSAML_Logger::warning($result);
+ }
+ }
+
+
+ /**
+ * Helper function to log SAML messages that we send or receive.
+ *
+ * @param string|\DOMElement $message The message, as an string containing the XML or an XML element.
+ * @param string $type Whether this message is sent or received, encrypted or decrypted. The following
+ * values are supported:
+ * - 'in': for messages received.
+ * - 'out': for outgoing messages.
+ * - 'decrypt': for decrypted messages.
+ * - 'encrypt': for encrypted messages.
+ *
+ * @throws \InvalidArgumentException If $type is not a string or $message is neither a string nor a \DOMElement.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function debugSAMLMessage($message, $type)
+ {
+ if (!(is_string($type) && (is_string($message) || $message instanceof \DOMElement))) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $globalConfig = \SimpleSAML_Configuration::getInstance();
+ if (!$globalConfig->getBoolean('debug', false)) {
+ // message debug disabled
+ return;
+ }
+
+ if ($message instanceof \DOMElement) {
+ $message = $message->ownerDocument->saveXML($message);
+ }
+
+ switch ($type) {
+ case 'in':
+ \SimpleSAML_Logger::debug('Received message:');
+ break;
+ case 'out':
+ \SimpleSAML_Logger::debug('Sending message:');
+ break;
+ case 'decrypt':
+ \SimpleSAML_Logger::debug('Decrypted message:');
+ break;
+ case 'encrypt':
+ \SimpleSAML_Logger::debug('Encrypted message:');
+ break;
+ default:
+ assert(false);
+ }
+
+ $str = self::formatXMLString($message);
+ foreach (explode("\n", $str) as $line) {
+ \SimpleSAML_Logger::debug($line);
+ }
+ }
+
+
+ /**
+ * Format a DOM element.
+ *
+ * This function takes in a DOM element, and inserts whitespace to make it more readable. Note that whitespace
+ * added previously will be removed.
+ *
+ * @param \DOMElement $root The root element which should be formatted.
+ * @param string $indentBase The indentation this element should be assumed to have. Defaults to an empty
+ * string.
+ *
+ * @throws \InvalidArgumentException If $root is not a DOMElement or $indentBase is not a string.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function formatDOMElement(\DOMElement $root, $indentBase = '')
+ {
+ if (!is_string($indentBase)) {
+ throw new \InvalidArgumentException('Invalid input parameters');
+ }
+
+ // check what this element contains
+ $fullText = ''; // all text in this element
+ $textNodes = array(); // text nodes which should be deleted
+ $childNodes = array(); // other child nodes
+ for ($i = 0; $i < $root->childNodes->length; $i++) {
+ $child = $root->childNodes->item($i);
+
+ if ($child instanceof \DOMText) {
+ $textNodes[] = $child;
+ $fullText .= $child->wholeText;
+ } elseif ($child instanceof \DOMComment || $child instanceof \DOMElement) {
+ $childNodes[] = $child;
+ } else {
+ // unknown node type. We don't know how to format this
+ return;
+ }
+ }
+
+ $fullText = trim($fullText);
+ if (strlen($fullText) > 0) {
+ // we contain textelf
+ $hasText = true;
+ } else {
+ $hasText = false;
+ }
+
+ $hasChildNode = (count($childNodes) > 0);
+
+ if ($hasText && $hasChildNode) {
+ // element contains both text and child nodes - we don't know how to format this one
+ return;
+ }
+
+ // remove text nodes
+ foreach ($textNodes as $node) {
+ $root->removeChild($node);
+ }
+
+ if ($hasText) {
+ // only text - add a single text node to the element with the full text
+ $root->appendChild(new \DOMText($fullText));
+ return;
+ }
+
+ if (!$hasChildNode) {
+ // empty node. Nothing to do
+ return;
+ }
+
+ /* Element contains only child nodes - add indentation before each one, and
+ * format child elements.
+ */
+ $childIndentation = $indentBase.' ';
+ foreach ($childNodes as $node) {
+ // add indentation before node
+ $root->insertBefore(new \DOMText("\n".$childIndentation), $node);
+
+ // format child elements
+ if ($node instanceof \DOMElement) {
+ self::formatDOMElement($node, $childIndentation);
+ }
+ }
+
+ // add indentation before closing tag
+ $root->appendChild(new \DOMText("\n".$indentBase));
+ }
+
+
+ /**
+ * Format an XML string.
+ *
+ * This function formats an XML string using the formatDOMElement() function.
+ *
+ * @param string $xml An XML string which should be formatted.
+ * @param string $indentBase Optional indentation which should be applied to all the output. Optional, defaults
+ * to ''.
+ *
+ * @return string The formatted string.
+ * @throws \InvalidArgumentException If the parameters are not strings.
+ * @throws \DOMException If the input does not parse correctly as an XML string.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function formatXMLString($xml, $indentBase = '')
+ {
+ if (!is_string($xml) || !is_string($indentBase)) {
+ throw new \InvalidArgumentException('Invalid input parameters');
+ }
+
+ $doc = new \DOMDocument();
+ if (!$doc->loadXML($xml)) {
+ throw new \DOMException('Error parsing XML string.');
+ }
+
+ $root = $doc->firstChild;
+ self::formatDOMElement($root, $indentBase);
+
+ return $doc->saveXML($root);
+ }
+
+
+ /**
+ * This function finds direct descendants of a DOM element with the specified
+ * localName and namespace. They are returned in an array.
+ *
+ * This function accepts the same shortcuts for namespaces as the isDOMElementOfType function.
+ *
+ * @param \DOMElement $element The element we should look in.
+ * @param string $localName The name the element should have.
+ * @param string $namespaceURI The namespace the element should have.
+ *
+ * @return array Array with the matching elements in the order they are found. An empty array is
+ * returned if no elements match.
+ * @throws \InvalidArgumentException If $element is not an instance of DOMElement, $localName is not a string or
+ * $namespaceURI is not a string.
+ */
+ public static function getDOMChildren(\DOMElement $element, $localName, $namespaceURI)
+ {
+ if (!($element instanceof \DOMElement) || !is_string($localName) || !is_string($namespaceURI)) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ $ret = array();
+
+ for ($i = 0; $i < $element->childNodes->length; $i++) {
+ $child = $element->childNodes->item($i);
+
+ // skip text nodes and comment elements
+ if ($child instanceof \DOMText || $child instanceof \DOMComment) {
+ continue;
+ }
+
+ if (self::isDOMElementOfType($child, $localName, $namespaceURI) === true) {
+ $ret[] = $child;
+ }
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * This function extracts the text from DOMElements which should contain only text content.
+ *
+ * @param \DOMElement $element The element we should extract text from.
+ *
+ * @return string The text content of the element.
+ * @throws \InvalidArgumentException If $element is not an instance of DOMElement.
+ * @throws \SimpleSAML_Error_Exception If the element contains a non-text child node.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function getDOMText(\DOMElement $element)
+ {
+ if (!($element instanceof \DOMElement)) {
+ throw new \InvalidArgumentException('Invalid input parameters');
+ }
+
+ $txt = '';
+
+ for ($i = 0; $i < $element->childNodes->length; $i++) {
+ $child = $element->childNodes->item($i);
+ if (!($child instanceof \DOMText)) {
+ throw new \SimpleSAML_Error_Exception($element->localName.' contained a non-text child node.');
+ }
+
+ $txt .= $child->wholeText;
+ }
+
+ $txt = trim($txt);
+ return $txt;
+ }
+
+
+ /**
+ * This function checks if the DOMElement has the correct localName and namespaceURI.
+ *
+ * We also define the following shortcuts for namespaces:
+ * - '@ds': 'http://www.w3.org/2000/09/xmldsig#'
+ * - '@md': 'urn:oasis:names:tc:SAML:2.0:metadata'
+ * - '@saml1': 'urn:oasis:names:tc:SAML:1.0:assertion'
+ * - '@saml1md': 'urn:oasis:names:tc:SAML:profiles:v1metadata'
+ * - '@saml1p': 'urn:oasis:names:tc:SAML:1.0:protocol'
+ * - '@saml2': 'urn:oasis:names:tc:SAML:2.0:assertion'
+ * - '@saml2p': 'urn:oasis:names:tc:SAML:2.0:protocol'
+ *
+ * @param \DOMNode $element The element we should check.
+ * @param string $name The local name the element should have.
+ * @param string $nsURI The namespaceURI the element should have.
+ *
+ * @return boolean True if both namespace and local name matches, false otherwise.
+ * @throws \InvalidArgumentException If the namespace shortcut is unknown.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function isDOMElementOfType(\DOMNode $element, $name, $nsURI)
+ {
+ if (!($element instanceof \DOMElement) || !is_string($name) || !is_string($nsURI) || strlen($nsURI) === 0) {
+ // most likely a comment-node
+ return false;
+ }
+
+ // check if the namespace is a shortcut, and expand it if it is
+ if ($nsURI[0] === '@') {
+ // the defined shortcuts
+ $shortcuts = array(
+ '@ds' => 'http://www.w3.org/2000/09/xmldsig#',
+ '@md' => 'urn:oasis:names:tc:SAML:2.0:metadata',
+ '@saml1' => 'urn:oasis:names:tc:SAML:1.0:assertion',
+ '@saml1md' => 'urn:oasis:names:tc:SAML:profiles:v1metadata',
+ '@saml1p' => 'urn:oasis:names:tc:SAML:1.0:protocol',
+ '@saml2' => 'urn:oasis:names:tc:SAML:2.0:assertion',
+ '@saml2p' => 'urn:oasis:names:tc:SAML:2.0:protocol',
+ '@shibmd' => 'urn:mace:shibboleth:metadata:1.0',
+ );
+
+ // check if it is a valid shortcut
+ if (!array_key_exists($nsURI, $shortcuts)) {
+ throw new \InvalidArgumentException('Unknown namespace shortcut: '.$nsURI);
+ }
+
+ // expand the shortcut
+ $nsURI = $shortcuts[$nsURI];
+ }
+ if ($element->localName !== $name) {
+ return false;
+ }
+ if ($element->namespaceURI !== $nsURI) {
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * This function attempts to validate an XML string against the specified schema. It will parse the string into a
+ * DOM document and validate this document against the schema.
+ *
+ * Note that this function returns values that are evaluated as a logical true, both when validation works and when
+ * it doesn't. Please use strict comparisons to check the values returned.
+ *
+ * @param string|\DOMDocument $xml The XML string or document which should be validated.
+ * @param string $schema The filename of the schema that should be used to validate the document.
+ *
+ * @return boolean|string Returns a string with errors found if validation fails. True if validation passes ok.
+ * @throws \InvalidArgumentException If $schema is not a string, or $xml is neither a string nor a \DOMDocument.
+ *
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ public static function isValid($xml, $schema)
+ {
+ if (!(is_string($schema) && (is_string($xml) || $xml instanceof \DOMDocument))) {
+ throw new \InvalidArgumentException('Invalid input parameters.');
+ }
+
+ \SimpleSAML_XML_Errors::begin();
+
+ if ($xml instanceof \DOMDocument) {
+ $dom = $xml;
+ $res = true;
+ } else {
+ $dom = new \DOMDocument;
+ $res = $dom->loadXML($xml);
+ }
+
+ if ($res) {
+
+ $config = \SimpleSAML_Configuration::getInstance();
+ $schemaPath = $config->resolvePath('schemas').'/';
+ $schemaFile = $schemaPath.$schema;
+
+ $res = $dom->schemaValidate($schemaFile);
+ if ($res) {
+ \SimpleSAML_XML_Errors::end();
+ return true;
+ }
+
+ $errorText = "Schema validation failed on XML string:\n";
+ } else {
+ $errorText = "Failed to parse XML string for schema validation:\n";
+ }
+
+ $errors = \SimpleSAML_XML_Errors::end();
+ $errorText .= \SimpleSAML_XML_Errors::formatErrors($errors);
+
+ return $errorText;
+ }
+}
diff --git a/lib/SimpleSAML/XHTML/EMail.php b/lib/SimpleSAML/XHTML/EMail.php
index 67989b1..5b69962 100644
--- a/lib/SimpleSAML/XHTML/EMail.php
+++ b/lib/SimpleSAML/XHTML/EMail.php
@@ -65,7 +65,7 @@ pre {
if ($this->subject == NULL) throw new Exception('EMail field [subject] is required and not set.');
if ($this->body == NULL) throw new Exception('EMail field [body] is required and not set.');
- $random_hash = SimpleSAML_Utilities::stringToHex(SimpleSAML_Utilities::generateRandomBytes(16));
+ $random_hash = bin2hex(openssl_random_pseudo_bytes(16));
if (isset($this->from))
$this->headers[]= 'From: ' . $this->from;
diff --git a/lib/SimpleSAML/XHTML/IdPDisco.php b/lib/SimpleSAML/XHTML/IdPDisco.php
index 87e7db3..8b084f3 100644
--- a/lib/SimpleSAML/XHTML/IdPDisco.php
+++ b/lib/SimpleSAML/XHTML/IdPDisco.php
@@ -123,7 +123,7 @@ class SimpleSAML_XHTML_IdPDisco {
if(!array_key_exists('return', $_GET)) {
throw new Exception('Missing parameter: return');
} else {
- $this->returnURL = SimpleSAML_Utilities::checkURLAllowed($_GET['return']);
+ $this->returnURL = \SimpleSAML\Utils\HTTP::checkURLAllowed($_GET['return']);
}
$this->isPassive = FALSE;
@@ -197,7 +197,7 @@ class SimpleSAML_XHTML_IdPDisco {
'httponly' => FALSE,
);
- SimpleSAML_Utilities::setCookie($prefixedName, $value, $params, FALSE);
+ \SimpleSAML\Utils\HTTP::setCookie($prefixedName, $value, $params, FALSE);
}
@@ -462,8 +462,7 @@ class SimpleSAML_XHTML_IdPDisco {
$extDiscoveryStorage = $this->config->getString('idpdisco.extDiscoveryStorage', NULL);
if ($extDiscoveryStorage !== NULL) {
$this->log('Choice made [' . $idp . '] (Forwarding to external discovery storage)');
- SimpleSAML_Utilities::redirectTrustedURL($extDiscoveryStorage, array(
-// $this->returnIdParam => $idp,
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($extDiscoveryStorage, array(
'entityID' => $this->spEntityId,
'IdPentityID' => $idp,
'returnIDParam' => $this->returnIdParam,
@@ -473,7 +472,7 @@ class SimpleSAML_XHTML_IdPDisco {
} else {
$this->log('Choice made [' . $idp . '] (Redirecting the user back. returnIDParam=' . $this->returnIdParam . ')');
- SimpleSAML_Utilities::redirectTrustedURL($this->returnURL, array($this->returnIdParam => $idp));
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($this->returnURL, array($this->returnIdParam => $idp));
}
return;
@@ -481,7 +480,7 @@ class SimpleSAML_XHTML_IdPDisco {
if ($this->isPassive) {
$this->log('Choice not made. (Redirecting the user back without answer)');
- SimpleSAML_Utilities::redirectTrustedURL($this->returnURL);
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($this->returnURL);
return;
}
@@ -495,12 +494,12 @@ class SimpleSAML_XHTML_IdPDisco {
$idpList = array_intersect_key($idpList, array_fill_keys($idpintersection, NULL));
}
- $idpintersection = array_values($idpintersection);
-
- if(sizeof($idpintersection) == 1) {
- $this->log('Choice made [' . $idpintersection[0] . '] (Redirecting the user back. returnIDParam=' . $this->returnIdParam . ')');
- SimpleSAML_Utilities::redirectTrustedURL($this->returnURL, array($this->returnIdParam => $idpintersection[0]));
- }
+ $idpintersection = array_values($idpintersection);
+
+ if(sizeof($idpintersection) == 1) {
+ $this->log('Choice made [' . $idpintersection[0] . '] (Redirecting the user back. returnIDParam=' . $this->returnIdParam . ')');
+ \SimpleSAML\Utils\HTTP::redirectTrustedURL($this->returnURL, array($this->returnIdParam => $idpintersection[0]));
+ }
/*
* Make use of an XHTML template to present the select IdP choice to the user.
@@ -523,7 +522,7 @@ class SimpleSAML_XHTML_IdPDisco {
$t->data['return'] = $this->returnURL;
$t->data['returnIDParam'] = $this->returnIdParam;
$t->data['entityID'] = $this->spEntityId;
- $t->data['urlpattern'] = htmlspecialchars(SimpleSAML_Utilities::selfURLNoQuery());
+ $t->data['urlpattern'] = htmlspecialchars(\SimpleSAML\Utils\HTTP::getSelfURLNoQuery());
$t->data['rememberenabled'] = $this->config->getBoolean('idpdisco.enableremember', FALSE);
$t->show();
}
diff --git a/lib/SimpleSAML/XHTML/Template.php b/lib/SimpleSAML/XHTML/Template.php
index 4186eb9..68275de 100644
--- a/lib/SimpleSAML/XHTML/Template.php
+++ b/lib/SimpleSAML/XHTML/Template.php
@@ -141,7 +141,7 @@ class SimpleSAML_XHTML_Template {
* languages in the header were available.
*/
private function getHTTPLanguage() {
- $languageScore = SimpleSAML_Utilities::getAcceptLanguage();
+ $languageScore = \SimpleSAML\Utils\HTTP::getAcceptLanguage();
/* For now we only use the default language map. We may use a configurable language map
* in the future.
@@ -413,7 +413,6 @@ class SimpleSAML_XHTML_Template {
$translated = $this->getTranslation($tagData);
-# if (!empty($replacements)){ echo('<pre> [' . $tag . ']'); print_r($replacements); exit; }
foreach ($replacements as $k => $v) {
/* try to translate if no replacement is given */
if ($v == NULL) $v = $this->t($k);
@@ -712,7 +711,7 @@ class SimpleSAML_XHTML_Template {
'httponly' => FALSE,
);
- SimpleSAML_Utilities::setCookie($name, $language, $params, FALSE);
+ \SimpleSAML\Utils\HTTP::setCookie($name, $language, $params, FALSE);
}
}
diff --git a/lib/SimpleSAML/XML/Parser.php b/lib/SimpleSAML/XML/Parser.php
index b309666..d73472d 100644
--- a/lib/SimpleSAML/XML/Parser.php
+++ b/lib/SimpleSAML/XML/Parser.php
@@ -10,11 +10,8 @@ class SimpleSAML_XML_Parser {
var $simplexml = null;
-
- function __construct($xml) {
- #parent::construct($xml);
+ function __construct($xml) {;
$this->simplexml = new SimpleXMLElement($xml);
-
$this->simplexml->registerXPathNamespace('saml2', 'urn:oasis:names:tc:SAML:2.0:assertion');
$this->simplexml->registerXPathNamespace('saml2meta', 'urn:oasis:names:tc:SAML:2.0:metadata');
$this->simplexml->registerXPathNamespace('ds', 'http://www.w3.org/2000/09/xmldsig#');
diff --git a/lib/SimpleSAML/XML/Shib13/AuthnRequest.php b/lib/SimpleSAML/XML/Shib13/AuthnRequest.php
index 0d91446..0424245 100644
--- a/lib/SimpleSAML/XML/Shib13/AuthnRequest.php
+++ b/lib/SimpleSAML/XML/Shib13/AuthnRequest.php
@@ -4,7 +4,7 @@
* The Shibboleth 1.3 Authentication Request. Not part of SAML 1.1,
* but an extension using query paramters no XML.
*
- * @author Andreas Åkre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
+ * @author Andreas Ã…kre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
* @package simpleSAMLphp
*/
class SimpleSAML_XML_Shib13_AuthnRequest {
diff --git a/lib/SimpleSAML/XML/Shib13/AuthnResponse.php b/lib/SimpleSAML/XML/Shib13/AuthnResponse.php
index 12e6c6d..d228d81 100644
--- a/lib/SimpleSAML/XML/Shib13/AuthnResponse.php
+++ b/lib/SimpleSAML/XML/Shib13/AuthnResponse.php
@@ -3,7 +3,7 @@
/**
* A Shibboleth 1.3 authentication response.
*
- * @author Andreas Åkre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
+ * @author Andreas Ã…kre Solberg, UNINETT AS. <andreas.solberg@uninett.no>
* @package simpleSAMLphp
*/
class SimpleSAML_XML_Shib13_AuthnResponse {
@@ -106,7 +106,7 @@ class SimpleSAML_XML_Shib13_AuthnResponse {
$this->validator->validateFingerprint($certFingerprints);
} elseif ($md->hasValue('caFile')) {
/* Validate against CA. */
- $this->validator->validateCA(SimpleSAML_Utilities::resolveCert($md->getString('caFile')));
+ $this->validator->validateCA(\SimpleSAML\Utils\Config::getCertPath($md->getString('caFile')));
} else {
throw new SimpleSAML_Error_Exception('Missing certificate in Shibboleth 1.3 IdP Remote metadata for identity provider [' . $issuer . '].');
}
@@ -115,7 +115,7 @@ class SimpleSAML_XML_Shib13_AuthnResponse {
}
- /* Checks if the given node is validated by the signatore on this response.
+ /* Checks if the given node is validated by the signature on this response.
*
* Returns:
* TRUE if the node is validated or FALSE if not.
@@ -212,7 +212,7 @@ class SimpleSAML_XML_Shib13_AuthnResponse {
$end = $condition->getAttribute('NotOnOrAfter');
if ($start && $end) {
- if (! SimpleSAML_Utilities::checkDateConditions($start, $end)) {
+ if (!self::checkDateConditions($start, $end)) {
error_log('Date check failed ... (from ' . $start . ' to ' . $end . ')');
continue;
}
@@ -304,16 +304,16 @@ class SimpleSAML_XML_Shib13_AuthnResponse {
$scopedAttributes = array();
}
- $id = SimpleSAML_Utilities::generateID();
+ $id = SimpleSAML\Utils\Random::generateID();
- $issueInstant = SimpleSAML_Utilities::generateTimestamp();
+ $issueInstant = SimpleSAML\Utils\Time::generateTimestamp();
// 30 seconds timeskew back in time to allow differing clocks.
- $notBefore = SimpleSAML_Utilities::generateTimestamp(time() - 30);
+ $notBefore = SimpleSAML\Utils\Time::generateTimestamp(time() - 30);
- $assertionExpire = SimpleSAML_Utilities::generateTimestamp(time() + 60 * 5);# 5 minutes
- $assertionid = SimpleSAML_Utilities::generateID();
+ $assertionExpire = SimpleSAML\Utils\Time::generateTimestamp(time() + 60 * 5);# 5 minutes
+ $assertionid = SimpleSAML\Utils\Random::generateID();
$spEntityId = $sp->getString('entityid');
@@ -321,7 +321,7 @@ class SimpleSAML_XML_Shib13_AuthnResponse {
$base64 = $sp->getBoolean('base64attributes', FALSE);
$namequalifier = $sp->getString('NameQualifier', $spEntityId);
- $nameid = SimpleSAML_Utilities::generateID();
+ $nameid = SimpleSAML\Utils\Random::generateID();
$subjectNode =
'<Subject>' .
'<NameIdentifier' .
@@ -427,5 +427,42 @@ class SimpleSAML_XML_Shib13_AuthnResponse {
return $attr;
}
+ /**
+ * Check if we are currently between the given date & time conditions.
+ *
+ * Note that this function allows a 10-minute leap from the initial time as marked by $start.
+ *
+ * @param string|null $start A SAML2 timestamp marking the start of the period to check. Defaults to null, in which
+ * case there's no limitations in the past.
+ * @param string|null $end A SAML2 timestamp marking the end of the period to check. Defaults to null, in which
+ * case there's no limitations in the future.
+ *
+ * @return bool True if the current time belongs to the period specified by $start and $end. False otherwise.
+ *
+ * @see \SAML2_Utils::xsDateTimeToTimestamp.
+ *
+ * @author Andreas Solberg, UNINETT AS <andreas.solberg@uninett.no>
+ * @author Olav Morken, UNINETT AS <olav.morken@uninett.no>
+ */
+ protected static function checkDateConditions($start = null, $end = null)
+ {
+ $currentTime = time();
+
+ 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 = \SAML2_Utils::xsDateTimeToTimestamp($end);
+ if (($endTime < 0) || ($endTime <= $currentTime)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
}
diff --git a/lib/SimpleSAML/XML/Signer.php b/lib/SimpleSAML/XML/Signer.php
index 15bf719..d855358 100644
--- a/lib/SimpleSAML/XML/Signer.php
+++ b/lib/SimpleSAML/XML/Signer.php
@@ -117,7 +117,7 @@ class SimpleSAML_XML_Signer {
assert('is_string($file)');
assert('is_string($pass) || is_null($pass)');
- $keyFile = SimpleSAML_Utilities::resolveCert($file);
+ $keyFile = \SimpleSAML\Utils\Config::getCertPath($file);
if (!file_exists($keyFile)) {
throw new Exception('Could not find private key file "' . $keyFile . '".');
}
@@ -167,7 +167,7 @@ class SimpleSAML_XML_Signer {
public function loadCertificate($file) {
assert('is_string($file)');
- $certFile = SimpleSAML_Utilities::resolveCert($file);
+ $certFile = \SimpleSAML\Utils\Config::getCertPath($file);
if (!file_exists($certFile)) {
throw new Exception('Could not find certificate file "' . $certFile . '".');
}
@@ -202,7 +202,7 @@ class SimpleSAML_XML_Signer {
public function addCertificate($file) {
assert('is_string($file)');
- $certFile = SimpleSAML_Utilities::resolveCert($file);
+ $certFile = \SimpleSAML\Utils\Config::getCertPath($file);
if (!file_exists($certFile)) {
throw new Exception('Could not find extra certificate file "' . $certFile . '".');
}
diff --git a/lib/SimpleSAML/XML/Validator.php b/lib/SimpleSAML/XML/Validator.php
index c973636..b9a9dfd 100644
--- a/lib/SimpleSAML/XML/Validator.php
+++ b/lib/SimpleSAML/XML/Validator.php
@@ -289,7 +289,134 @@ class SimpleSAML_XML_Validator {
throw new Exception('Key used to sign the message was not an X509 certificate.');
}
- SimpleSAML_Utilities::validateCA($this->x509Certificate, $caFile);
+ self::validateCertificate($this->x509Certificate, $caFile);
+ }
+
+ /**
+ * Validate a certificate against a CA file, by using the builtin
+ * openssl_x509_checkpurpose function
+ *
+ * @param string $certificate The certificate, in PEM format.
+ * @param string $caFile File with trusted certificates, in PEM-format.
+ * @return boolean|string TRUE on success, or a string with error messages if it failed.
+ * @deprecated
+ */
+ private static function validateCABuiltIn($certificate, $caFile) {
+ assert('is_string($certificate)');
+ assert('is_string($caFile)');
+
+ /* Clear openssl errors. */
+ while(openssl_error_string() !== FALSE);
+
+ $res = openssl_x509_checkpurpose($certificate, X509_PURPOSE_ANY, array($caFile));
+
+ $errors = '';
+ /* Log errors. */
+ while( ($error = openssl_error_string()) !== FALSE) {
+ $errors .= ' [' . $error . ']';
+ }
+
+ if($res !== TRUE) {
+ return $errors;
+ }
+
+ return TRUE;
+ }
+
+
+ /**
+ * Validate the certificate used to sign the XML against a CA file, by using the "openssl verify" command.
+ *
+ * This function uses the openssl verify command to verify a certificate, to work around limitations
+ * on the openssl_x509_checkpurpose function. That function will not work on certificates without a purpose
+ * set.
+ *
+ * @param string $certificate The certificate, in PEM format.
+ * @param string $caFile File with trusted certificates, in PEM-format.
+ * @return boolean|string TRUE on success, a string with error messages on failure.
+ * @deprecated
+ */
+ private static function validateCAExec($certificate, $caFile) {
+ assert('is_string($certificate)');
+ assert('is_string($caFile)');
+
+ $command = array(
+ 'openssl', 'verify',
+ '-CAfile', $caFile,
+ '-purpose', 'any',
+ );
+
+ $cmdline = '';
+ foreach($command as $c) {
+ $cmdline .= escapeshellarg($c) . ' ';
+ }
+
+ $cmdline .= '2>&1';
+ $descSpec = array(
+ 0 => array('pipe', 'r'),
+ 1 => array('pipe', 'w'),
+ );
+ $process = proc_open($cmdline, $descSpec, $pipes);
+ if (!is_resource($process)) {
+ throw new Exception('Failed to execute verification command: ' . $cmdline);
+ }
+
+ if (fwrite($pipes[0], $certificate) === FALSE) {
+ throw new Exception('Failed to write certificate for verification.');
+ }
+ fclose($pipes[0]);
+
+ $out = '';
+ while (!feof($pipes[1])) {
+ $line = trim(fgets($pipes[1]));
+ if(strlen($line) > 0) {
+ $out .= ' [' . $line . ']';
+ }
+ }
+ fclose($pipes[1]);
+
+ $status = proc_close($process);
+ if ($status !== 0 || $out !== ' [stdin: OK]') {
+ return $out;
+ }
+
+ return TRUE;
+ }
+
+
+ /**
+ * Validate the certificate used to sign the XML against a CA file.
+ *
+ * This function throws an exception if unable to validate against the given CA file.
+ *
+ * @param string $certificate The certificate, in PEM format.
+ * @param string $caFile File with trusted certificates, in PEM-format.
+ * @deprecated
+ */
+ public static function validateCertificate($certificate, $caFile) {
+ assert('is_string($certificate)');
+ assert('is_string($caFile)');
+
+ if (!file_exists($caFile)) {
+ throw new Exception('Could not load CA file: ' . $caFile);
+ }
+
+ SimpleSAML_Logger::debug('Validating certificate against CA file: ' . var_export($caFile, TRUE));
+
+ $resBuiltin = self::validateCABuiltIn($certificate, $caFile);
+ if ($resBuiltin !== TRUE) {
+ SimpleSAML_Logger::debug('Failed to validate with internal function: ' . var_export($resBuiltin, TRUE));
+
+ $resExternal = self::validateCAExec($certificate, $caFile);
+ if ($resExternal !== TRUE) {
+ SimpleSAML_Logger::debug('Failed to validate with external function: ' . var_export($resExternal, TRUE));
+ throw new Exception('Could not verify certificate against CA file "'
+ . $caFile . '". Internal result:' . $resBuiltin .
+ ' External result:' . $resExternal);
+ }
+ }
+
+ SimpleSAML_Logger::debug('Successfully validated certificate.');
}
}