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 @@
* identity check.
*
* LIBRARY DESIGN
*
*
* 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
* securely.
......@@ -427,7 +427,7 @@ class Auth_OpenID_Consumer {
$loader->fromSession($endpoint_data);
$message = Auth_OpenID_Message::fromPostArgs($query);
$response = $this->consumer->complete($message, $endpoint,
$response = $this->consumer->complete($message, $endpoint,
$current_url);
$this->session->del($this->_token_key);
......@@ -616,6 +616,9 @@ class Auth_OpenID_GenericConsumer {
$this->store = $store;
$this->negotiator = Auth_OpenID_getDefaultNegotiator();
$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();
......@@ -666,7 +669,7 @@ class Auth_OpenID_GenericConsumer {
'_completeInvalid');
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 {
list($unused, $services) = call_user_func_array($this->discoverMethod,
array(
$claimed_id,
&$this->fetcher,
$this->fetcher,
));
if (!$services) {
......@@ -1202,7 +1205,7 @@ class Auth_OpenID_GenericConsumer {
/**
* @access private
*/
function _verifyDiscoveryServices($claimed_id,
function _verifyDiscoveryServices($claimed_id,
$services, $to_match_endpoints)
{
// Search the services resulting from discovery to find one
......@@ -1210,7 +1213,7 @@ class Auth_OpenID_GenericConsumer {
foreach ($services as $endpoint) {
foreach ($to_match_endpoints as $to_match_endpoint) {
$result = $this->_verifyDiscoverySingle($endpoint,
$result = $this->_verifyDiscoverySingle($endpoint,
$to_match_endpoint);
if (!Auth_OpenID::isFailure($result)) {
......@@ -1368,7 +1371,7 @@ class Auth_OpenID_GenericConsumer {
}
}
$ca_message = $message->copy();
$ca_message->setArg(Auth_OpenID_OPENID_NS, 'mode',
$ca_message->setArg(Auth_OpenID_OPENID_NS, 'mode',
'check_authentication');
return $ca_message;
}
......@@ -1606,7 +1609,7 @@ class Auth_OpenID_GenericConsumer {
$expires_in = Auth_OpenID::intval($expires_in_str);
if ($expires_in === false) {
$err = sprintf("Could not parse expires_in from association ".
"response %s", print_r($assoc_response, true));
return new Auth_OpenID_FailureResponse(null, $err);
......@@ -1953,7 +1956,7 @@ class Auth_OpenID_AuthRequest {
function htmlMarkup($realm, $return_to=null, $immediate=false,
$form_tag_attrs=null)
{
$form = $this->formMarkup($realm, $return_to, $immediate,
$form = $this->formMarkup($realm, $return_to, $immediate,
$form_tag_attrs);
if (Auth_OpenID::isFailure($form)) {
......
......@@ -39,7 +39,7 @@ class Auth_OpenID_Extension {
*
* Returns the message with the extension arguments added.
*/
function toMessage($message)
function toMessage($message, $request = null)
{
$implicit = $message->isOpenID1();
$added = $message->namespaces->addAlias($this->ns_uri,
......@@ -53,8 +53,13 @@ class Auth_OpenID_Extension {
}
}
$message->updateArgs($this->ns_uri,
$this->getExtensionArgs());
if ($request !== null) {
$message->updateArgs($this->ns_uri,
$this->getExtensionArgs($request));
} else {
$message->updateArgs($this->ns_uri,
$this->getExtensionArgs());
}
return $message;
}
}
......
......@@ -300,13 +300,22 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
return null;
}
if (file_exists($filename) !== true) {
return null;
}
$assoc_file = @fopen($filename, 'rb');
if ($assoc_file === false) {
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);
if (!$assoc_s) {
......
......@@ -85,7 +85,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
// column name instead of column index.
$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);
return;
}
......@@ -103,7 +103,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
function tableExists($table_name)
{
return !PEAR::isError($this->connection->query(
return !@PEAR::isError($this->connection->query(
sprintf("SELECT * FROM %s LIMIT 0",
$table_name)));
}
......@@ -135,12 +135,12 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
" UNIQUE (server_url(255), timestamp, salt)\n".
") TYPE=InnoDB",
$this->nonces_table_name));
if (PEAR::isError($r)) {
if (@PEAR::isError($r)) {
return false;
}
break;
default:
if (PEAR::isError(
if (@PEAR::isError(
$this->connection->loadModule('Manager'))) {
return false;
}
......@@ -172,7 +172,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
$r = $this->connection->createTable($this->nonces_table_name,
$fields);
if (PEAR::isError($r)) {
if (@PEAR::isError($r)) {
return false;
}
......@@ -180,7 +180,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
$this->nonces_table_name,
$this->nonces_table_name . "_constraint",
$constraint);
if (PEAR::isError($r)) {
if (@PEAR::isError($r)) {
return false;
}
break;
......@@ -208,12 +208,12 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
" PRIMARY KEY (server_url(255), handle)\n".
") TYPE=InnoDB",
$this->associations_table_name));
if (PEAR::isError($r)) {
if (@PEAR::isError($r)) {
return false;
}
break;
default:
if (PEAR::isError(
if (@PEAR::isError(
$this->connection->loadModule('Manager'))) {
return false;
}
......@@ -258,7 +258,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
$this->associations_table_name,
$fields,
$options);
if (PEAR::isError($r)) {
if (@PEAR::isError($r)) {
return false;
}
break;
......@@ -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,
$fields));
}
......@@ -340,7 +340,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
$assoc = $this->connection->getRow($sql, $types, $params);
if (!$assoc || PEAR::isError($assoc)) {
if (!$assoc || @PEAR::isError($assoc)) {
return null;
} else {
$association = new Auth_OpenID_Association($assoc['handle'],
......@@ -361,7 +361,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
$this->associations_table_name),
array($server_url, $handle));
if (PEAR::isError($r) || $r == 0) {
if (@PEAR::isError($r) || $r == 0) {
return false;
}
return true;
......@@ -389,7 +389,7 @@ class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
$fields,
MDB2_AUTOQUERY_INSERT);
if (PEAR::isError($r)) {
if (@PEAR::isError($r)) {
return false;
}
return true;
......
......@@ -675,7 +675,7 @@ class Auth_OpenID_Message {
if ($form_tag_attrs) {
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 {
foreach ($this->toPostArgs() as $name => $value) {
$form .= sprintf(
"<input type=\"hidden\" name=\"%s\" value=\"%s\" />\n",
$name, urldecode($value));
htmlspecialchars($name), htmlspecialchars($value));
}
$form .= sprintf("<input type=\"submit\" value=\"%s\" />\n",
$submit_text);
htmlspecialchars($submit_text));
$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);
}
/**
* Key is prefixed with $prefix and 'openid_association_server_' string
*/
function associationServerKey($server_url)
{
return $this->prefix .
'openid_association_server_' .
sha1($server_url);
}
/**
* Report that this storage doesn't support cleanup
*/
function supportsCleanup()
{
return false;
}
}
......@@ -166,7 +166,7 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
*/
function isError($value)
{
return PEAR::isError($value);
return @PEAR::isError($value);
}
/**
......
......@@ -210,7 +210,7 @@ class Auth_OpenID_TrustRoot {
if ($parts['host'] == 'localhost') {
return true;
}
$host_parts = explode('.', $parts['host']);
if ($parts['wildcard']) {
// Remove the empty string from the beginning of the array
......@@ -413,7 +413,7 @@ function Auth_OpenID_getAllowedReturnURLs($relying_party_url, $fetcher,
}
call_user_func_array($discover_function,
array($relying_party_url, &$fetcher));
array($relying_party_url, $fetcher));
$return_to_urls = array();
$matching_endpoints = Auth_OpenID_extractReturnURL($endpoints);
......
......@@ -37,7 +37,7 @@ class Auth_Yadis_PHPSession {
*/
function get($name, $default=null)
{
if (array_key_exists($name, $_SESSION)) {
if (isset($_SESSION) && array_key_exists($name, $_SESSION)) {
return $_SESSION[$name];
} else {
return $default;
......@@ -414,7 +414,7 @@ class Auth_Yadis_Discovery {
list($yadis_url, $services) = call_user_func_array($discover_cb,
array(
$this->url,
&$fetcher,
$fetcher,
));
$manager = $this->createManager($services, $yadis_url);
......
......@@ -90,6 +90,15 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
$this->reset();
$c = curl_init();
if (defined('Auth_OpenID_DISABLE_SSL_VERIFYPEER')
&& Auth_OpenID_DISABLE_SSL_VERIFYPEER === true) {
trigger_error(
'You have disabled SSL verifcation, this is a TERRIBLE ' .
'idea in almost all cases. Set Auth_OpenID_DISABLE_SSL_' .
'VERIFYPEER to false if you want to be safe again',
E_USER_WARNING);
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
}
if ($c === false) {
Auth_OpenID::log(
......@@ -141,6 +150,9 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
}
}
if (defined('Auth_OpenID_HTTP_PROXY')) {
curl_setopt($c, CURLOPT_PROXY, Auth_OpenID_HTTP_PROXY);
}
// <TYPO3-specific>
if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']) {
......@@ -190,10 +202,6 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
}
}
Auth_OpenID::log(
"Successfully fetched '%s': GET response code %s",
$url, $code);
return new Auth_Yadis_HTTPResponse($url, $code,
$new_headers, $body);
}
......@@ -218,6 +226,23 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
curl_setopt($c, CURLOPT_NOSIGNAL, true);
}
if (defined('Auth_OpenID_HTTP_PROXY')) {
curl_setopt($c, CURLOPT_PROXY, Auth_OpenID_HTTP_PROXY);
}
// <TYPO3-specific>
if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']) {
curl_setopt($c, CURLOPT_PROXY, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']);
if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']) {
curl_setopt($c, CURLOPT_HTTPPROXYTUNNEL, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']);
}
if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']) {
curl_setopt($c, CURLOPT_PROXYUSERPWD, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']);
}
}
// </TYPO3-specific>
curl_setopt($c, CURLOPT_POST, true);
curl_setopt($c, CURLOPT_POSTFIELDS, $body);
curl_setopt($c, CURLOPT_TIMEOUT, $this->timeout);
......@@ -239,19 +264,6 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
}
}
// <TYPO3-specific>
if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']) {
curl_setopt($c, CURLOPT_PROXY, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']);
if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']) {
curl_setopt($c, CURLOPT_HTTPPROXYTUNNEL, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']);
}