]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Updating Janrain OpenID auth library
authorMikael Nordfeldth <mmn@hethane.se>
Mon, 23 Sep 2013 23:49:34 +0000 (01:49 +0200)
committerMikael Nordfeldth <mmn@hethane.se>
Mon, 23 Sep 2013 23:49:34 +0000 (01:49 +0200)
Source: https://github.com/openid/php-openid

17 files changed:
extlib/Auth/OpenID/Association.php
extlib/Auth/OpenID/BigMath.php
extlib/Auth/OpenID/Consumer.php
extlib/Auth/OpenID/CryptUtil.php
extlib/Auth/OpenID/Extension.php
extlib/Auth/OpenID/FileStore.php
extlib/Auth/OpenID/HMAC.php
extlib/Auth/OpenID/MDB2Store.php [new file with mode: 0644]
extlib/Auth/OpenID/Message.php
extlib/Auth/OpenID/MySQLStore.php
extlib/Auth/OpenID/Parse.php
extlib/Auth/OpenID/PredisStore.php [new file with mode: 0644]
extlib/Auth/OpenID/SQLStore.php
extlib/Auth/OpenID/Server.php
extlib/Auth/Yadis/Manager.php
extlib/Auth/Yadis/ParanoidHTTPFetcher.php
extlib/Auth/Yadis/XML.php

index 7fdf399a3c9e6309ce39a304208d47a8a3b15e75..2729138ebb1ee5886a956dfe810e037e2683f436 100644 (file)
@@ -374,42 +374,7 @@ class Auth_OpenID_Association {
         }
 
         $calculated_sig = $this->getMessageSignature($message);
-
-        return $this->constantTimeCompare($calculated_sig, $sig);
-    }
-
-    /**
-     * String comparison function which will complete in a constant time
-     * for strings of any given matching length, to help prevent an attacker
-     * from distinguishing how much of a signature token they have guessed
-     * correctly.
-     *
-     * For this usage, it's assumed that the length of the string is known,
-     * so we may safely short-circuit on mismatched lengths which will be known
-     * to be invalid by the attacker.
-     *
-     * http://lists.openid.net/pipermail/openid-security/2010-July/001156.html
-     * http://rdist.root.org/2010/01/07/timing-independent-array-comparison/
-     */
-    private function constantTimeCompare($a, $b)
-    {
-        $len = strlen($a);
-        if (strlen($b) !== $len) {
-            // Short-circuit on length mismatch; attackers will already know
-            // the correct target length so this is safe.
-            return false;
-        }
-        if ($len == 0) {
-            // 0-length valid input shouldn't really happen. :)
-            return true;
-        }
-        $result = 0;
-        for ($i = 0; $i < strlen($a); $i++) {
-            // We use scary bitwise operations to avoid logical short-circuits
-            // in lower-level code.
-            $result |= ord($a{$i}) ^ ord($b{$i});
-        }
-        return ($result == 0);
+        return Auth_OpenID_CryptUtil::constEq($calculated_sig, $sig);
     }
 }
 
index 7fca2dc43ef67bce86f309e0048000e04dfaab13..58b46bf27b24bdfdf39225a3f19a3ea025e355d4 100644 (file)
@@ -365,7 +365,6 @@ function Auth_OpenID_detectMathLibrary($exts)
 {
     $loaded = false;
 
-       $hasDl = function_exists('dl');
     foreach ($exts as $extension) {
         if (extension_loaded($extension['extension'])) {
             return $extension;
index 021c038988cc3134536c99f0d5da59520afbc6d3..d562e33f354823498e55109b8670256e92672093 100644 (file)
@@ -957,6 +957,10 @@ class Auth_OpenID_GenericConsumer {
             }
 
             if (!$assoc->checkMessageSignature($message)) {
+                // If we get a "bad signature" here, it means that the association
+                // is unrecoverabley corrupted in some way. Any futher attempts
+                // to login with this association is likely to fail. Drop it.
+                $this->store->removeAssociation($server_url, $assoc_handle);
                 return new Auth_OpenID_FailureResponse(null,
                                                        "Bad signature");
             }
@@ -1179,9 +1183,11 @@ class Auth_OpenID_GenericConsumer {
     function _discoverAndVerify($claimed_id, $to_match_endpoints)
     {
         // oidutil.log('Performing discovery on %s' % (claimed_id,))
-        list($unused, $services) = call_user_func($this->discoverMethod,
-                                                  $claimed_id,
-                                                                                                 &$this->fetcher);
+        list($unused, $services) = call_user_func_array($this->discoverMethod,
+                                                        array(
+                                                            $claimed_id,
+                                                            &$this->fetcher,
+                                                        ));
 
         if (!$services) {
             return new Auth_OpenID_FailureResponse(null,
index a92626777932757074c39550f803f0faf802595a..3c60cea170037da51154ac871e75ce7c8daf2c0f 100644 (file)
@@ -104,5 +104,19 @@ class Auth_OpenID_CryptUtil {
 
         return $str;
     }
+
+    static function constEq($s1, $s2)
+    {
+        if (strlen($s1) != strlen($s2)) {
+            return false;
+        }
+
+        $result = true;
+        $length = strlen($s1);
+        for ($i = 0; $i < $length; $i++) {
+            $result &= ($s1[$i] == $s2[$i]);
+        }
+        return $result;
+    }
 }
 
index c4e38c0380bbf8a5fa25b40885f0442e9f30063b..542a1da26197ffa5176c67d7673e9cb7ec43da9c 100644 (file)
@@ -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;
     }
 }
index 074421a0bb3d504c946127f6efed686c4e6d6079..7eec791d24c29d846d48c94f9487b88868b07797 100644 (file)
@@ -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) {
index e9779bd4e0973055926348deae01d1065d5178d4..e6c4bdfd9dc81dfe5b11cd6022358486a134331a 100644 (file)
@@ -60,6 +60,13 @@ function Auth_OpenID_HMACSHA1($key, $text)
         $key = Auth_OpenID_SHA1($key, true);
     }
 
+    if (function_exists('hash_hmac') &&
+        function_exists('hash_algos') &&
+        (in_array('sha1', hash_algos()))) {
+        return hash_hmac('sha1', $text, $key, true);
+    }
+    // Home-made solution
+
     $key = str_pad($key, Auth_OpenID_SHA1_BLOCKSIZE, chr(0x00));
     $ipad = str_repeat(chr(0x36), Auth_OpenID_SHA1_BLOCKSIZE);
     $opad = str_repeat(chr(0x5c), Auth_OpenID_SHA1_BLOCKSIZE);
diff --git a/extlib/Auth/OpenID/MDB2Store.php b/extlib/Auth/OpenID/MDB2Store.php
new file mode 100644 (file)
index 0000000..fb27d5c
--- /dev/null
@@ -0,0 +1,413 @@
+<?php
+
+/**
+ * SQL-backed OpenID stores for use with PEAR::MDB2.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: See the COPYING file included in this distribution.
+ *
+ * @package OpenID
+ * @author JanRain, Inc. <openid@janrain.com>
+ * @copyright 2005 Janrain, Inc.
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL
+ */
+
+require_once 'MDB2.php';
+
+/**
+ * @access private
+ */
+require_once 'Auth/OpenID/Interface.php';
+
+/**
+ * @access private
+ */
+require_once 'Auth/OpenID.php';
+
+/**
+ * @access private
+ */
+require_once 'Auth/OpenID/Nonce.php';
+
+/**
+ * This store uses a PEAR::MDB2 connection to store persistence
+ * information.
+ *
+ * The table names used are determined by the class variables
+ * associations_table_name and nonces_table_name.  To change the name
+ * of the tables used, pass new table names into the constructor.
+ *
+ * To create the tables with the proper schema, see the createTables
+ * method.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
+    /**
+     * This creates a new MDB2Store instance.  It requires an
+     * established database connection be given to it, and it allows
+     * overriding the default table names.
+     *
+     * @param connection $connection This must be an established
+     * connection to a database of the correct type for the SQLStore
+     * subclass you're using.  This must be a PEAR::MDB2 connection
+     * handle.
+     *
+     * @param associations_table: This is an optional parameter to
+     * specify the name of the table used for storing associations.
+     * The default value is 'oid_associations'.
+     *
+     * @param nonces_table: This is an optional parameter to specify
+     * the name of the table used for storing nonces.  The default
+     * value is 'oid_nonces'.
+     */
+    function Auth_OpenID_MDB2Store($connection,
+                                  $associations_table = null,
+                                  $nonces_table = null)
+    {
+        $this->associations_table_name = "oid_associations";
+        $this->nonces_table_name = "oid_nonces";
+
+        // Check the connection object type to be sure it's a PEAR
+        // database connection.
+        if (!is_object($connection) ||
+            !is_subclass_of($connection, 'mdb2_driver_common')) {
+            trigger_error("Auth_OpenID_MDB2Store expected PEAR connection " .
+                          "object (got ".get_class($connection).")",
+                          E_USER_ERROR);
+            return;
+        }
+
+        $this->connection = $connection;
+
+        // Be sure to set the fetch mode so the results are keyed on
+        // column name instead of column index.
+        $this->connection->setFetchMode(MDB2_FETCHMODE_ASSOC);
+        
+        if (@PEAR::isError($this->connection->loadModule('Extended'))) {
+            trigger_error("Unable to load MDB2_Extended module", E_USER_ERROR);
+            return;
+        }
+
+        if ($associations_table) {
+            $this->associations_table_name = $associations_table;
+        }
+
+        if ($nonces_table) {
+            $this->nonces_table_name = $nonces_table;
+        }
+
+        $this->max_nonce_age = 6 * 60 * 60;
+    }
+
+    function tableExists($table_name)
+    {
+        return !@PEAR::isError($this->connection->query(
+                                  sprintf("SELECT * FROM %s LIMIT 0",
+                                          $table_name)));
+    }
+
+    function createTables()
+    {
+        $n = $this->create_nonce_table();
+        $a = $this->create_assoc_table();
+
+        if (!$n || !$a) {
+            return false;
+        }
+        return true;
+    }
+
+    function create_nonce_table()
+    {
+        if (!$this->tableExists($this->nonces_table_name)) {
+            switch ($this->connection->phptype) {
+                case "mysql":
+                case "mysqli":
+                    // Custom SQL for MySQL to use InnoDB and variable-
+                    // length keys
+                    $r = $this->connection->exec(
+                        sprintf("CREATE TABLE %s (\n".
+                                "  server_url VARCHAR(2047) NOT NULL DEFAULT '',\n".
+                                "  timestamp INTEGER NOT NULL,\n".
+                                "  salt CHAR(40) NOT NULL,\n".
+                                "  UNIQUE (server_url(255), timestamp, salt)\n".
+                                ") TYPE=InnoDB",
+                                $this->nonces_table_name));
+                    if (@PEAR::isError($r)) {
+                        return false;
+                    }
+                    break;
+                default:
+                    if (@PEAR::isError(
+                        $this->connection->loadModule('Manager'))) {
+                        return false;
+                    }
+                    $fields = array(
+                        "server_url" => array(
+                            "type" => "text",
+                            "length" => 2047,
+                            "notnull" => true
+                        ),
+                        "timestamp" => array(
+                            "type" => "integer",
+                            "notnull" => true
+                        ),
+                        "salt" => array(
+                            "type" => "text",
+                            "length" => 40,
+                            "fixed" => true,
+                            "notnull" => true
+                        )
+                    );
+                    $constraint = array(
+                        "unique" => 1,
+                        "fields" => array(
+                            "server_url" => true,
+                            "timestamp" => true,
+                            "salt" => true
+                        )
+                    );
+                    
+                    $r = $this->connection->createTable($this->nonces_table_name,
+                                                        $fields);
+                    if (@PEAR::isError($r)) {
+                        return false;
+                    }
+                    
+                    $r = $this->connection->createConstraint(
+                        $this->nonces_table_name,
+                        $this->nonces_table_name . "_constraint",
+                        $constraint);
+                    if (@PEAR::isError($r)) {
+                        return false;
+                    }
+                    break;
+            }
+        }
+        return true;
+    }
+
+    function create_assoc_table()
+    {
+        if (!$this->tableExists($this->associations_table_name)) {
+            switch ($this->connection->phptype) {
+                case "mysql":
+                case "mysqli":
+                    // Custom SQL for MySQL to use InnoDB and variable-
+                    // length keys
+                    $r = $this->connection->exec(
+                        sprintf("CREATE TABLE %s(\n".
+                                "  server_url VARCHAR(2047) NOT NULL DEFAULT '',\n".
+                                "  handle VARCHAR(255) NOT NULL,\n".
+                                "  secret BLOB NOT NULL,\n".
+                                "  issued INTEGER NOT NULL,\n".
+                                "  lifetime INTEGER NOT NULL,\n".
+                                "  assoc_type VARCHAR(64) NOT NULL,\n".
+                                "  PRIMARY KEY (server_url(255), handle)\n".
+                                ") TYPE=InnoDB",
+                            $this->associations_table_name));
+                    if (@PEAR::isError($r)) {
+                        return false;
+                    }
+                    break;
+                default:
+                    if (@PEAR::isError(
+                        $this->connection->loadModule('Manager'))) {
+                        return false;
+                    }
+                    $fields = array(
+                        "server_url" => array(
+                            "type" => "text",
+                            "length" => 2047,
+                            "notnull" => true
+                        ),
+                        "handle" => array(
+                            "type" => "text",
+                            "length" => 255,
+                            "notnull" => true
+                        ),
+                        "secret" => array(
+                            "type" => "blob",
+                            "length" => "255",
+                            "notnull" => true
+                        ),
+                        "issued" => array(
+                            "type" => "integer",
+                            "notnull" => true
+                        ),
+                        "lifetime" => array(
+                            "type" => "integer",
+                            "notnull" => true
+                        ),
+                        "assoc_type" => array(
+                            "type" => "text",
+                            "length" => 64,
+                            "notnull" => true
+                        )
+                    );
+                    $options = array(
+                        "primary" => array(
+                            "server_url" => true,
+                            "handle" => true
+                        )
+                    );
+                    
+                    $r = $this->connection->createTable(
+                        $this->associations_table_name,
+                        $fields,
+                        $options);
+                    if (@PEAR::isError($r)) {
+                        return false;
+                    }
+                    break;
+            }
+        }
+        return true;
+    }
+
+    function storeAssociation($server_url, $association)
+    {
+        $fields = array(
+            "server_url" => array(
+                "value" => $server_url,
+                "key" => true
+            ),
+            "handle" => array(
+                "value" => $association->handle,
+                "key" => true
+            ),
+            "secret" => array(
+                "value" => $association->secret,
+                "type" => "blob"
+            ),
+            "issued" => array(
+                "value" => $association->issued
+            ),
+            "lifetime" => array(
+                "value" => $association->lifetime
+            ),
+            "assoc_type" => array(
+                "value" => $association->assoc_type
+            )
+        );
+        
+        return !@PEAR::isError($this->connection->replace(
+                                  $this->associations_table_name,
+                                  $fields));
+    }
+
+    function cleanupNonces()
+    {
+        global $Auth_OpenID_SKEW;
+        $v = time() - $Auth_OpenID_SKEW;
+
+        return $this->connection->exec(
+            sprintf("DELETE FROM %s WHERE timestamp < %d",
+                    $this->nonces_table_name, $v));
+    }
+
+    function cleanupAssociations()
+    {
+        return $this->connection->exec(
+            sprintf("DELETE FROM %s WHERE issued + lifetime < %d",
+                    $this->associations_table_name, time()));
+    }
+
+    function getAssociation($server_url, $handle = null)
+    {
+        $sql = "";
+        $params = null;
+        $types = array(
+                       "text",
+                       "blob",
+                       "integer",
+                       "integer",
+                       "text"
+                       );
+        if ($handle !== null) {
+            $sql = sprintf("SELECT handle, secret, issued, lifetime, assoc_type " .
+                           "FROM %s WHERE server_url = ? AND handle = ?",
+                           $this->associations_table_name);
+            $params = array($server_url, $handle);
+        } else {
+            $sql = sprintf("SELECT handle, secret, issued, lifetime, assoc_type " .
+                           "FROM %s WHERE server_url = ? ORDER BY issued DESC",
+                           $this->associations_table_name);
+            $params = array($server_url);
+        }
+        
+        $assoc = $this->connection->getRow($sql, $types, $params);
+
+        if (!$assoc || @PEAR::isError($assoc)) {
+            return null;
+        } else {
+            $association = new Auth_OpenID_Association($assoc['handle'],
+                                                       stream_get_contents(
+                                                           $assoc['secret']),
+                                                       $assoc['issued'],
+                                                       $assoc['lifetime'],
+                                                       $assoc['assoc_type']);
+            fclose($assoc['secret']);
+            return $association;
+        }
+    }
+
+    function removeAssociation($server_url, $handle)
+    {
+        $r = $this->connection->execParam(
+            sprintf("DELETE FROM %s WHERE server_url = ? AND handle = ?",
+                    $this->associations_table_name),
+            array($server_url, $handle));
+        
+        if (@PEAR::isError($r) || $r == 0) {
+            return false;
+        }
+        return true;
+    }
+
+    function useNonce($server_url, $timestamp, $salt)
+    {
+        global $Auth_OpenID_SKEW;
+
+        if (abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
+            return false;
+        }
+        
+        $fields = array(
+                        "timestamp" => $timestamp,
+                        "salt" => $salt
+                        );
+        
+        if (!empty($server_url)) {
+            $fields["server_url"] = $server_url;
+        }
+        
+        $r = $this->connection->autoExecute(
+            $this->nonces_table_name,
+            $fields,
+            MDB2_AUTOQUERY_INSERT);
+        
+        if (@PEAR::isError($r)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Resets the store by removing all records from the store's
+     * tables.
+     */
+    function reset()
+    {
+        $this->connection->query(sprintf("DELETE FROM %s",
+                                         $this->associations_table_name));
+
+        $this->connection->query(sprintf("DELETE FROM %s",
+                                         $this->nonces_table_name));
+    }
+
+}
+
+?>
index 9aa1fa4684ad71c1ed83602a413cb9b7e8ece43d..16ec1c1db4c9469f2ba9810d62f2b516d150b946 100644 (file)
@@ -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, $value);
+                        htmlspecialchars($name), htmlspecialchars($value));
         }
 
         $form .= sprintf("<input type=\"submit\" value=\"%s\" />\n",
-                         $submit_text);
+                         htmlspecialchars($submit_text));
 
         $form .= "</form>\n";
 
index 810f059f1dfcc74ac5df6145e63e049e75a75244..a5299b3a5cdce35f9265f7b8109044ad265cd454 100644 (file)
@@ -32,7 +32,7 @@ class Auth_OpenID_MySQLStore extends Auth_OpenID_SQLStore {
 
         $this->sql['assoc_table'] =
             "CREATE TABLE %s (\n".
-            "  server_url BLOB NOT NULL,\n".
+            "  server_url VARCHAR(2047) NOT NULL,\n".
             "  handle VARCHAR(255) NOT NULL,\n".
             "  secret BLOB NOT NULL,\n".
             "  issued INTEGER NOT NULL,\n".
index 6c2e7216912439beb287e10f5a1825f43851b419..0461bdcff7a065078b684c779ba88c6e02d4245b 100644 (file)
@@ -219,7 +219,11 @@ class Auth_OpenID_Parse {
     function match($regexp, $text, &$match)
     {
         if (!is_callable('mb_ereg_search_init')) {
-            return preg_match($regexp, $text, $match);
+            if (!preg_match($regexp, $text, $match)) {
+                return false;
+            }
+            $match = $match[0];
+            return true;
         }
 
         $regexp = substr($regexp, 1, strlen($regexp) - 2 - strlen($this->_re_flags));
@@ -227,7 +231,7 @@ class Auth_OpenID_Parse {
         if (!mb_ereg_search($regexp)) {
             return false;
         }
-        list($match) = mb_ereg_search_getregs();
+        $match = mb_ereg_search_getregs();
         return true;
     }
 
@@ -269,7 +273,7 @@ class Auth_OpenID_Parse {
 
         // Try to find the <HEAD> tag.
         $head_re = $this->headFind();
-        $head_match = '';
+        $head_match = array();
         if (!$this->match($head_re, $stripped, $head_match)) {
                      ini_set( 'pcre.backtrack_limit', $old_btlimit );
                      return array();
@@ -278,7 +282,7 @@ class Auth_OpenID_Parse {
         $link_data = array();
         $link_matches = array();
 
-        if (!preg_match_all($this->_link_find, $head_match,
+        if (!preg_match_all($this->_link_find, $head_match[0],
                             $link_matches)) {
             ini_set( 'pcre.backtrack_limit', $old_btlimit );
             return array();
diff --git a/extlib/Auth/OpenID/PredisStore.php b/extlib/Auth/OpenID/PredisStore.php
new file mode 100644 (file)
index 0000000..7108c2f
--- /dev/null
@@ -0,0 +1,208 @@
+<?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->lpop($serverKey);
+        if (!$lastKey) { 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->predis->setnx($nonceKey);
+        if ($added) {
+            // Will set expiration
+            $this->predis->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;
+    }
+
+}
+
index c04059732cf370d01f915a3c755cf0c0fe5c6a96..2dc731a777413061e5956bfce81ca0aed6cc7753 100644 (file)
@@ -166,7 +166,7 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
      */
     function isError($value)
     {
-        return PEAR::isError($value);
+        return @PEAR::isError($value);
     }
 
     /**
index cc8ba961c2593eb559c55abbc8ef245add525563..9887d1e8d8d99c8b8c759d5c0c76b312dcb0c037 100644 (file)
@@ -817,11 +817,11 @@ class Auth_OpenID_CheckIDRequest extends Auth_OpenID_Request {
      */
     function returnToVerified()
     {
-       $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
+        $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
         return call_user_func_array($this->verifyReturnTo,
                                     array($this->trust_root, $this->return_to, $fetcher));
     }
-    
+
     static function fromMessage($message, $server)
     {
         $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode');
@@ -1704,7 +1704,7 @@ class Auth_OpenID_Server {
     {
         if (method_exists($this, "openid_" . $request->mode)) {
             $handler = array($this, "openid_" . $request->mode);
-            return call_user_func($handler, &$request);
+            return call_user_func_array($handler, array($request));
         }
         return null;
     }
index ee6f68bcb69fb7aa46a37b4e25bab27cd5d0b1e8..15e69079bb7cb48e7324922601c7097a3d9062b0 100644 (file)
@@ -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;
@@ -411,9 +411,11 @@ class Auth_Yadis_Discovery {
         if (!$manager || (!$manager->services)) {
             $this->destroyManager();
 
-            list($yadis_url, $services) = call_user_func($discover_cb,
-                                                         $this->url,
-                                                         &$fetcher);
+            list($yadis_url, $services) = call_user_func_array($discover_cb,
+                                                               array(
+                                                                $this->url,
+                                                                &$fetcher,
+                                                               ));
 
             $manager = $this->createManager($services, $yadis_url);
         }
index 4da7c94c0d92e433b9222b8261323834bd7c17ad..125029c4cb9f114cb896fd24955e4ed22df0da34 100644 (file)
@@ -129,8 +129,20 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
             curl_setopt($c, CURLOPT_URL, $url);
 
             if (defined('Auth_OpenID_VERIFY_HOST')) {
-                curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
-                curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
+                // set SSL verification options only if Auth_OpenID_VERIFY_HOST
+                // is explicitly set, otherwise use system default.
+                if (Auth_OpenID_VERIFY_HOST) {
+                    curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
+                    curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
+                    if (defined('Auth_OpenID_CAINFO')) {
+                        curl_setopt($c, CURLOPT_CAINFO, Auth_OpenID_CAINFO);
+                    }
+                } else {
+                    curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
+                }
+            }
+            if (defined('Auth_OpenID_HTTP_PROXY')) {
+                curl_setopt($c, CURLOPT_PROXY, Auth_OpenID_HTTP_PROXY);
             }
             curl_exec($c);
 
@@ -153,6 +165,7 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
                 curl_close($c);
 
                 if (defined('Auth_OpenID_VERIFY_HOST') &&
+                    Auth_OpenID_VERIFY_HOST == true &&
                     $this->isHTTPS($url)) {
                     Auth_OpenID::log('OpenID: Verified SSL host %s using '.
                                      'curl/get', $url);
@@ -166,10 +179,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);
             }
@@ -194,6 +203,10 @@ 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);
+        }
+
         curl_setopt($c, CURLOPT_POST, true);
         curl_setopt($c, CURLOPT_POSTFIELDS, $body);
         curl_setopt($c, CURLOPT_TIMEOUT, $this->timeout);
@@ -202,8 +215,17 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
                     array($this, "_writeData"));
 
         if (defined('Auth_OpenID_VERIFY_HOST')) {
-            curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
-            curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
+            // set SSL verification options only if Auth_OpenID_VERIFY_HOST
+            // is explicitly set, otherwise use system default.
+            if (Auth_OpenID_VERIFY_HOST) {
+                curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
+                curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
+                if (defined('Auth_OpenID_CAINFO')) {
+                    curl_setopt($c, CURLOPT_CAINFO, Auth_OpenID_CAINFO);
+                }
+            } else {
+                curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
+            }
         }
 
         curl_exec($c);
@@ -217,7 +239,9 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
             return null;
         }
 
-        if (defined('Auth_OpenID_VERIFY_HOST') && $this->isHTTPS($url)) {
+        if (defined('Auth_OpenID_VERIFY_HOST') &&
+            Auth_OpenID_VERIFY_HOST == true &&
+            $this->isHTTPS($url)) {
             Auth_OpenID::log('OpenID: Verified SSL host %s using '.
                              'curl/post', $url);
         }
@@ -235,9 +259,6 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
 
         }
 
-        Auth_OpenID::log("Successfully fetched '%s': POST response code %s",
-                         $url, $code);
-
         return new Auth_Yadis_HTTPResponse($url, $code,
                                            $new_headers, $body);
     }
index cf1f5c41b28638f32c4126ea651dfbf93dc984f2..39a9942220cadf1f9268d3f52e510f845f1c556a 100644 (file)
@@ -234,7 +234,19 @@ class Auth_Yadis_dom extends Auth_Yadis_XMLParser {
             return false;
         }
 
-        if (!@$this->doc->loadXML($xml_string)) {
+        // libxml_disable_entity_loader (PHP 5 >= 5.2.11)
+        if (function_exists('libxml_disable_entity_loader') && function_exists('libxml_use_internal_errors')) {
+            // disable external entities and libxml errors
+            $loader = libxml_disable_entity_loader(true);
+            $errors = libxml_use_internal_errors(true);
+            $parse_result = @$this->doc->loadXML($xml_string);
+            libxml_disable_entity_loader($loader);
+            libxml_use_internal_errors($errors);
+        } else {
+            $parse_result = @$this->doc->loadXML($xml_string);
+        }
+
+        if (!$parse_result) {
             return false;
         }