Commit d3222e5f authored by Markus Klein's avatar Markus Klein Committed by Oliver Hader
Browse files

[SECURITY] Fix DoS in openid

Upgrade openid to latest upstream version.
This includes the sec fix already.

Change-Id: I00c77ed8ab31ed21dd21914d920bb28633963816
Resolves: #62357
Releases: master, 6.2, 6.1, 6.0, 4.7, 4.6, 4.5
Security-Commit: 77a76dd5347f2f997f4ff0db596e1237481752dd
Security-Bulletin: TYPO3-CORE-SA-2014-002
Reviewed-on: http://review.typo3.org/33451

Reviewed-by: Oliver Hader's avatarOliver Hader <oliver.hader@typo3.org>
Tested-by: Oliver Hader's avatarOliver Hader <oliver.hader@typo3.org>
parent 4079e1a4
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
* identity check. * identity check.
* *
* LIBRARY DESIGN * LIBRARY DESIGN
* *
* This consumer library is designed with that flow in mind. The goal * This consumer library is designed with that flow in mind. The goal
* is to make it as easy as possible to perform the above steps * is to make it as easy as possible to perform the above steps
* securely. * securely.
...@@ -427,7 +427,7 @@ class Auth_OpenID_Consumer { ...@@ -427,7 +427,7 @@ class Auth_OpenID_Consumer {
$loader->fromSession($endpoint_data); $loader->fromSession($endpoint_data);
$message = Auth_OpenID_Message::fromPostArgs($query); $message = Auth_OpenID_Message::fromPostArgs($query);
$response = $this->consumer->complete($message, $endpoint, $response = $this->consumer->complete($message, $endpoint,
$current_url); $current_url);
$this->session->del($this->_token_key); $this->session->del($this->_token_key);
...@@ -616,6 +616,9 @@ class Auth_OpenID_GenericConsumer { ...@@ -616,6 +616,9 @@ class Auth_OpenID_GenericConsumer {
$this->store = $store; $this->store = $store;
$this->negotiator = Auth_OpenID_getDefaultNegotiator(); $this->negotiator = Auth_OpenID_getDefaultNegotiator();
$this->_use_assocs = (is_null($this->store) ? false : true); $this->_use_assocs = (is_null($this->store) ? false : true);
if (get_class($this->store) == "Auth_OpenID_DumbStore") {
$this->_use_assocs = false;
}
$this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); $this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
...@@ -666,7 +669,7 @@ class Auth_OpenID_GenericConsumer { ...@@ -666,7 +669,7 @@ class Auth_OpenID_GenericConsumer {
'_completeInvalid'); '_completeInvalid');
return call_user_func_array(array($this, $method), return call_user_func_array(array($this, $method),
array($message, &$endpoint, $return_to)); array($message, $endpoint, $return_to));
} }
/** /**
...@@ -1186,7 +1189,7 @@ class Auth_OpenID_GenericConsumer { ...@@ -1186,7 +1189,7 @@ class Auth_OpenID_GenericConsumer {
list($unused, $services) = call_user_func_array($this->discoverMethod, list($unused, $services) = call_user_func_array($this->discoverMethod,
array( array(
$claimed_id, $claimed_id,
&$this->fetcher, $this->fetcher,
)); ));
if (!$services) { if (!$services) {
...@@ -1202,7 +1205,7 @@ class Auth_OpenID_GenericConsumer { ...@@ -1202,7 +1205,7 @@ class Auth_OpenID_GenericConsumer {
/** /**
* @access private * @access private
*/ */
function _verifyDiscoveryServices($claimed_id, function _verifyDiscoveryServices($claimed_id,
$services, $to_match_endpoints) $services, $to_match_endpoints)
{ {
// Search the services resulting from discovery to find one // Search the services resulting from discovery to find one
...@@ -1210,7 +1213,7 @@ class Auth_OpenID_GenericConsumer { ...@@ -1210,7 +1213,7 @@ class Auth_OpenID_GenericConsumer {
foreach ($services as $endpoint) { foreach ($services as $endpoint) {
foreach ($to_match_endpoints as $to_match_endpoint) { foreach ($to_match_endpoints as $to_match_endpoint) {
$result = $this->_verifyDiscoverySingle($endpoint, $result = $this->_verifyDiscoverySingle($endpoint,
$to_match_endpoint); $to_match_endpoint);
if (!Auth_OpenID::isFailure($result)) { if (!Auth_OpenID::isFailure($result)) {
...@@ -1368,7 +1371,7 @@ class Auth_OpenID_GenericConsumer { ...@@ -1368,7 +1371,7 @@ class Auth_OpenID_GenericConsumer {
} }
} }
$ca_message = $message->copy(); $ca_message = $message->copy();
$ca_message->setArg(Auth_OpenID_OPENID_NS, 'mode', $ca_message->setArg(Auth_OpenID_OPENID_NS, 'mode',
'check_authentication'); 'check_authentication');
return $ca_message; return $ca_message;
} }
...@@ -1606,7 +1609,7 @@ class Auth_OpenID_GenericConsumer { ...@@ -1606,7 +1609,7 @@ class Auth_OpenID_GenericConsumer {
$expires_in = Auth_OpenID::intval($expires_in_str); $expires_in = Auth_OpenID::intval($expires_in_str);
if ($expires_in === false) { if ($expires_in === false) {
$err = sprintf("Could not parse expires_in from association ". $err = sprintf("Could not parse expires_in from association ".
"response %s", print_r($assoc_response, true)); "response %s", print_r($assoc_response, true));
return new Auth_OpenID_FailureResponse(null, $err); return new Auth_OpenID_FailureResponse(null, $err);
...@@ -1953,7 +1956,7 @@ class Auth_OpenID_AuthRequest { ...@@ -1953,7 +1956,7 @@ class Auth_OpenID_AuthRequest {
function htmlMarkup($realm, $return_to=null, $immediate=false, function htmlMarkup($realm, $return_to=null, $immediate=false,
$form_tag_attrs=null) $form_tag_attrs=null)
{ {
$form = $this->formMarkup($realm, $return_to, $immediate, $form = $this->formMarkup($realm, $return_to, $immediate,
$form_tag_attrs); $form_tag_attrs);
if (Auth_OpenID::isFailure($form)) { if (Auth_OpenID::isFailure($form)) {
......
...@@ -39,7 +39,7 @@ class Auth_OpenID_Extension { ...@@ -39,7 +39,7 @@ class Auth_OpenID_Extension {
* *
* Returns the message with the extension arguments added. * Returns the message with the extension arguments added.
*/ */
function toMessage($message) function toMessage($message, $request = null)
{ {
$implicit = $message->isOpenID1(); $implicit = $message->isOpenID1();
$added = $message->namespaces->addAlias($this->ns_uri, $added = $message->namespaces->addAlias($this->ns_uri,
...@@ -53,8 +53,13 @@ class Auth_OpenID_Extension { ...@@ -53,8 +53,13 @@ class Auth_OpenID_Extension {
} }
} }
$message->updateArgs($this->ns_uri, if ($request !== null) {
$this->getExtensionArgs()); $message->updateArgs($this->ns_uri,
$this->getExtensionArgs($request));
} else {
$message->updateArgs($this->ns_uri,
$this->getExtensionArgs());
}
return $message; return $message;
} }
} }
......
...@@ -300,13 +300,22 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore { ...@@ -300,13 +300,22 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
return null; return null;
} }
if (file_exists($filename) !== true) {
return null;
}
$assoc_file = @fopen($filename, 'rb'); $assoc_file = @fopen($filename, 'rb');
if ($assoc_file === false) { if ($assoc_file === false) {
return null; return null;
} }
$assoc_s = fread($assoc_file, filesize($filename)); $filesize = filesize($filename);
if ($filesize === false || $filesize <= 0) {
return null;
}
$assoc_s = fread($assoc_file, $filesize);
fclose($assoc_file); fclose($assoc_file);
if (!$assoc_s) { if (!$assoc_s) {
......
...@@ -85,7 +85,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore { ...@@ -85,7 +85,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
// column name instead of column index. // column name instead of column index.
$this->connection->setFetchMode(MDB2_FETCHMODE_ASSOC); $this->connection->setFetchMode(MDB2_FETCHMODE_ASSOC);
if (PEAR::isError($this->connection->loadModule('Extended'))) { if (@PEAR::isError($this->connection->loadModule('Extended'))) {
trigger_error("Unable to load MDB2_Extended module", E_USER_ERROR); trigger_error("Unable to load MDB2_Extended module", E_USER_ERROR);
return; return;
} }
...@@ -103,7 +103,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore { ...@@ -103,7 +103,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
function tableExists($table_name) function tableExists($table_name)
{ {
return !PEAR::isError($this->connection->query( return !@PEAR::isError($this->connection->query(
sprintf("SELECT * FROM %s LIMIT 0", sprintf("SELECT * FROM %s LIMIT 0",
$table_name))); $table_name)));
} }
...@@ -135,12 +135,12 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore { ...@@ -135,12 +135,12 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
" UNIQUE (server_url(255), timestamp, salt)\n". " UNIQUE (server_url(255), timestamp, salt)\n".
") TYPE=InnoDB", ") TYPE=InnoDB",
$this->nonces_table_name)); $this->nonces_table_name));
if (PEAR::isError($r)) { if (@PEAR::isError($r)) {
return false; return false;
} }
break; break;
default: default:
if (PEAR::isError( if (@PEAR::isError(
$this->connection->loadModule('Manager'))) { $this->connection->loadModule('Manager'))) {
return false; return false;
} }
...@@ -172,7 +172,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore { ...@@ -172,7 +172,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
$r = $this->connection->createTable($this->nonces_table_name, $r = $this->connection->createTable($this->nonces_table_name,
$fields); $fields);
if (PEAR::isError($r)) { if (@PEAR::isError($r)) {
return false; return false;
} }
...@@ -180,7 +180,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore { ...@@ -180,7 +180,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
$this->nonces_table_name, $this->nonces_table_name,
$this->nonces_table_name . "_constraint", $this->nonces_table_name . "_constraint",
$constraint); $constraint);
if (PEAR::isError($r)) { if (@PEAR::isError($r)) {
return false; return false;
} }
break; break;
...@@ -208,12 +208,12 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore { ...@@ -208,12 +208,12 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
" PRIMARY KEY (server_url(255), handle)\n". " PRIMARY KEY (server_url(255), handle)\n".
") TYPE=InnoDB", ") TYPE=InnoDB",
$this->associations_table_name)); $this->associations_table_name));
if (PEAR::isError($r)) { if (@PEAR::isError($r)) {
return false; return false;
} }
break; break;
default: default:
if (PEAR::isError( if (@PEAR::isError(
$this->connection->loadModule('Manager'))) { $this->connection->loadModule('Manager'))) {
return false; return false;
} }
...@@ -258,7 +258,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore { ...@@ -258,7 +258,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
$this->associations_table_name, $this->associations_table_name,
$fields, $fields,
$options); $options);
if (PEAR::isError($r)) { if (@PEAR::isError($r)) {
return false; return false;
} }
break; break;
...@@ -293,7 +293,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore { ...@@ -293,7 +293,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
) )
); );
return !PEAR::isError($this->connection->replace( return !@PEAR::isError($this->connection->replace(
$this->associations_table_name, $this->associations_table_name,
$fields)); $fields));
} }
...@@ -340,7 +340,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore { ...@@ -340,7 +340,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
$assoc = $this->connection->getRow($sql, $types, $params); $assoc = $this->connection->getRow($sql, $types, $params);
if (!$assoc || PEAR::isError($assoc)) { if (!$assoc || @PEAR::isError($assoc)) {
return null; return null;
} else { } else {
$association = new Auth_OpenID_Association($assoc['handle'], $association = new Auth_OpenID_Association($assoc['handle'],
...@@ -361,7 +361,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore { ...@@ -361,7 +361,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
$this->associations_table_name), $this->associations_table_name),
array($server_url, $handle)); array($server_url, $handle));
if (PEAR::isError($r) || $r == 0) { if (@PEAR::isError($r) || $r == 0) {
return false; return false;
} }
return true; return true;
...@@ -389,7 +389,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore { ...@@ -389,7 +389,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
$fields, $fields,
MDB2_AUTOQUERY_INSERT); MDB2_AUTOQUERY_INSERT);
if (PEAR::isError($r)) { if (@PEAR::isError($r)) {
return false; return false;
} }
return true; return true;
......
...@@ -675,7 +675,7 @@ class Auth_OpenID_Message { ...@@ -675,7 +675,7 @@ class Auth_OpenID_Message {
if ($form_tag_attrs) { if ($form_tag_attrs) {
foreach ($form_tag_attrs as $name => $attr) { foreach ($form_tag_attrs as $name => $attr) {
$form .= sprintf(" %s=\"%s\"", $name, $attr); $form .= sprintf(" %s=\"%s\"", $name, htmlspecialchars($attr));
} }
} }
...@@ -684,11 +684,11 @@ class Auth_OpenID_Message { ...@@ -684,11 +684,11 @@ class Auth_OpenID_Message {
foreach ($this->toPostArgs() as $name => $value) { foreach ($this->toPostArgs() as $name => $value) {
$form .= sprintf( $form .= sprintf(
"<input type=\"hidden\" name=\"%s\" value=\"%s\" />\n", "<input type=\"hidden\" name=\"%s\" value=\"%s\" />\n",
$name, urldecode($value)); htmlspecialchars($name), htmlspecialchars($value));
} }
$form .= sprintf("<input type=\"submit\" value=\"%s\" />\n", $form .= sprintf("<input type=\"submit\" value=\"%s\" />\n",
$submit_text); htmlspecialchars($submit_text));
$form .= "</form>\n"; $form .= "</form>\n";
......
<?php
/**
* Supplies Redis server store backend for OpenID servers and consumers.
* Uses Predis library {@see https://github.com/nrk/predis}.
* Requires PHP >= 5.3.
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author Ville Mattila <ville@eventio.fi>
* @copyright 2008 JanRain Inc., 2013 Eventio Oy / Ville Mattila
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
* Contributed by Eventio Oy <http://www.eventio.fi/>
*/
/**
* Import the interface for creating a new store class.
*/
require_once 'Auth/OpenID/Interface.php';
/**
* Supplies Redis server store backend for OpenID servers and consumers.
* Uses Predis library {@see https://github.com/nrk/predis}.
* Requires PHP >= 5.3.
*
* @package OpenID
*/
class Auth_OpenID_PredisStore extends Auth_OpenID_OpenIDStore {
/**
* @var \Predis\Client
*/
protected $redis;
/**
* Prefix for Redis keys
* @var string
*/
protected $prefix;
/**
* Initializes a new {@link Auth_OpenID_PredisStore} instance.
*
* @param \Predis\Client $redis Predis client object
* @param string $prefix Prefix for all keys stored to the Redis
*/
function Auth_OpenID_PredisStore(\Predis\Client $redis, $prefix = '')
{
$this->prefix = $prefix;
$this->redis = $redis;
}
/**
* Store association until its expiration time in Redis server.
* Overwrites any existing association with same server_url and
* handle. Handles list of associations for every server.
*/
function storeAssociation($server_url, $association)
{
// create Redis keys for association itself
// and list of associations for this server
$associationKey = $this->associationKey($server_url,
$association->handle);
$serverKey = $this->associationServerKey($server_url);
// save association to server's associations' keys list
$this->redis->lpush(
$serverKey,
$associationKey
);
// Will touch the association list expiration, to avoid filling up
$newExpiration = ($association->issued + $association->lifetime);
$expirationKey = $serverKey.'_expires_at';
$expiration = $this->redis->get($expirationKey);
if (!$expiration || $newExpiration > $expiration) {
$this->redis->set($expirationKey, $newExpiration);
$this->redis->expireat($serverKey, $newExpiration);
$this->redis->expireat($expirationKey, $newExpiration);
}
// save association itself, will automatically expire
$this->redis->setex(
$associationKey,
$newExpiration - time(),
serialize($association)
);
}
/**
* Read association from Redis. If no handle given
* and multiple associations found, returns latest issued
*/
function getAssociation($server_url, $handle = null)
{
// simple case: handle given
if ($handle !== null) {
return $this->getAssociationFromServer(
$this->associationKey($server_url, $handle)
);
}
// no handle given, receiving the latest issued
$serverKey = $this->associationServerKey($server_url);
$lastKey = $this->redis->lindex($serverKey, -1);
if (!$lastKey) {
// no previous association with this server
return null;
}
// get association, return null if failed
return $this->getAssociationFromServer($lastKey);
}
/**
* Function to actually receive and unserialize the association
* from the server.
*/
private function getAssociationFromServer($associationKey)
{
$association = $this->redis->get($associationKey);
return $association ? unserialize($association) : null;
}
/**
* Immediately delete association from Redis.
*/
function removeAssociation($server_url, $handle)
{
// create Redis keys
$serverKey = $this->associationServerKey($server_url);
$associationKey = $this->associationKey($server_url,
$handle);
// Removing the association from the server's association list
$removed = $this->redis->lrem($serverKey, 0, $associationKey);
if ($removed < 1) {
return false;
}
// Delete the association itself
return $this->redis->del($associationKey);
}
/**
* Create nonce for server and salt, expiring after
* $Auth_OpenID_SKEW seconds.
*/
function useNonce($server_url, $timestamp, $salt)
{
global $Auth_OpenID_SKEW;
// save one request to memcache when nonce obviously expired
if (abs($timestamp - time()) > $Auth_OpenID_SKEW) {
return false;
}
// SETNX will set the value only of the key doesn't exist yet.
$nonceKey = $this->nonceKey($server_url, $salt);
$added = $this->redis->setnx($nonceKey, "1");
if ($added) {
// Will set expiration
$this->redis->expire($nonceKey, $Auth_OpenID_SKEW);
return true;
} else {
return false;
}
}
/**
* Build up nonce key
*/
private function nonceKey($server_url, $salt)
{
return $this->prefix .
'openid_nonce_' .
sha1($server_url) . '_' . sha1($salt);
}
/**
* Key is prefixed with $prefix and 'openid_association_' string
*/
function associationKey($server_url, $handle = null)
{
return $this->prefix .
'openid_association_' .
sha1($server_url) . '_' . sha1($handle);
}