Warning, file /webapps/ocs-webserver/library/Zend/OpenId/Consumer.php was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 <?php
0002 
0003 /**
0004  * Zend Framework
0005  *
0006  * LICENSE
0007  *
0008  * This source file is subject to the new BSD license that is bundled
0009  * with this package in the file LICENSE.txt.
0010  * It is also available through the world-wide-web at this URL:
0011  * http://framework.zend.com/license/new-bsd
0012  * If you did not receive a copy of the license and are unable to
0013  * obtain it through the world-wide-web, please send an email
0014  * to license@zend.com so we can send you a copy immediately.
0015  *
0016  * @category   Zend
0017  * @package    Zend_OpenId
0018  * @subpackage Zend_OpenId_Consumer
0019  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0020  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0021  * @version    $Id: Consumer.php 24593 2012-01-05 20:35:02Z matthew $
0022  */
0023 
0024 /**
0025  * @see Zend_OpenId
0026  */
0027 // require_once "Zend/OpenId.php";
0028 
0029 /**
0030  * @see Zend_OpenId_Extension
0031  */
0032 // require_once "Zend/OpenId/Extension.php";
0033 
0034 /**
0035  * @see Zend_OpenId_Consumer_Storage
0036  */
0037 // require_once "Zend/OpenId/Consumer/Storage.php";
0038 
0039 /**
0040  * @see Zend_Http_Client
0041  */
0042 // require_once 'Zend/Http/Client.php';
0043 
0044 /**
0045  * OpenID consumer implementation
0046  *
0047  * @category   Zend
0048  * @package    Zend_OpenId
0049  * @subpackage Zend_OpenId_Consumer
0050  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0051  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0052  */
0053 class Zend_OpenId_Consumer
0054 {
0055 
0056     /**
0057      * Parameters required for signature
0058      */
0059     protected $_signParams = array('op_endpoint', 'return_to', 'response_nonce', 'assoc_handle');
0060 
0061     /**
0062      * Reference to an implementation of storage object
0063      *
0064      * @var Zend_OpenId_Consumer_Storage $_storage
0065      */
0066     protected $_storage = null;
0067 
0068     /**
0069      * Enables or disables consumer to use association with server based on
0070      * Diffie-Hellman key agreement
0071      *
0072      * @var Zend_OpenId_Consumer_Storage $_dumbMode
0073      */
0074     protected $_dumbMode = false;
0075 
0076     /**
0077      * Internal cache to prevent unnecessary access to storage
0078      *
0079      * @var array $_cache
0080      */
0081     protected $_cache = array();
0082 
0083     /**
0084      * HTTP client to make HTTP requests
0085      *
0086      * @var Zend_Http_Client $_httpClient
0087      */
0088     private $_httpClient = null;
0089 
0090     /**
0091      * HTTP session to store climed_id between requests
0092      *
0093      * @var Zend_Session_Namespace $_session
0094      */
0095     private $_session = null;
0096 
0097     /**
0098      * Last error message for logi, check or verify failure
0099      *
0100      * @var string $_error
0101      */
0102     private $_error = '';
0103 
0104     /**
0105      * Constructs a Zend_OpenId_Consumer object with given $storage.
0106      * Enables or disables future association with server based on
0107      * Diffie-Hellman key agreement.
0108      *
0109      * @param Zend_OpenId_Consumer_Storage $storage implementation of custom
0110      *  storage object
0111      * @param bool $dumbMode Enables or disables consumer to use association
0112      *  with server based on Diffie-Hellman key agreement
0113      */
0114     public function __construct(Zend_OpenId_Consumer_Storage $storage = null,
0115                                 $dumbMode = false)
0116     {
0117         if ($storage === null) {
0118             // require_once "Zend/OpenId/Consumer/Storage/File.php";
0119             $this->_storage = new Zend_OpenId_Consumer_Storage_File();
0120         } else {
0121             $this->_storage = $storage;
0122         }
0123         $this->_dumbMode = $dumbMode;
0124     }
0125 
0126     /**
0127      * Performs check (with possible user interaction) of OpenID identity.
0128      *
0129      * This is the first step of OpenID authentication process.
0130      * On success the function does not return (it does HTTP redirection to
0131      * server and exits). On failure it returns false.
0132      *
0133      * @param string $id OpenID identity
0134      * @param string $returnTo URL to redirect response from server to
0135      * @param string $root HTTP URL to identify consumer on server
0136      * @param mixed $extensions extension object or array of extensions objects
0137      * @param Zend_Controller_Response_Abstract $response an optional response
0138      *  object to perform HTTP or HTML form redirection
0139      * @return bool
0140      */
0141     public function login($id, $returnTo = null, $root = null, $extensions = null,
0142                           Zend_Controller_Response_Abstract $response = null)
0143     {
0144         return $this->_checkId(
0145             false,
0146             $id,
0147             $returnTo,
0148             $root,
0149             $extensions,
0150             $response);
0151     }
0152 
0153     /**
0154      * Performs immediate check (without user interaction) of OpenID identity.
0155      *
0156      * This is the first step of OpenID authentication process.
0157      * On success the function does not return (it does HTTP redirection to
0158      * server and exits). On failure it returns false.
0159      *
0160      * @param string $id OpenID identity
0161      * @param string $returnTo HTTP URL to redirect response from server to
0162      * @param string $root HTTP URL to identify consumer on server
0163      * @param mixed $extensions extension object or array of extensions objects
0164      * @param Zend_Controller_Response_Abstract $response an optional response
0165      *  object to perform HTTP or HTML form redirection
0166      * @return bool
0167      */
0168     public function check($id, $returnTo=null, $root=null, $extensions = null,
0169                           Zend_Controller_Response_Abstract $response = null)
0170 
0171     {
0172         return $this->_checkId(
0173             true,
0174             $id,
0175             $returnTo,
0176             $root,
0177             $extensions,
0178             $response);
0179     }
0180 
0181     /**
0182      * Verifies authentication response from OpenID server.
0183      *
0184      * This is the second step of OpenID authentication process.
0185      * The function returns true on successful authentication and false on
0186      * failure.
0187      *
0188      * @param array $params HTTP query data from OpenID server
0189      * @param string &$identity this argument is set to end-user's claimed
0190      *  identifier or OpenID provider local identifier.
0191      * @param mixed $extensions extension object or array of extensions objects
0192      * @return bool
0193      */
0194     public function verify($params, &$identity = "", $extensions = null)
0195     {
0196         $this->_setError('');
0197 
0198         $version = 1.1;
0199         if (isset($params['openid_ns']) &&
0200             $params['openid_ns'] == Zend_OpenId::NS_2_0) {
0201             $version = 2.0;
0202         }
0203 
0204         if (isset($params["openid_claimed_id"])) {
0205             $identity = $params["openid_claimed_id"];
0206         } else if (isset($params["openid_identity"])){
0207             $identity = $params["openid_identity"];
0208         } else {
0209             $identity = "";
0210         }
0211 
0212         if ($version < 2.0 && !isset($params["openid_claimed_id"])) {
0213             if ($this->_session !== null) {
0214                 if ($this->_session->identity === $identity) {
0215                     $identity = $this->_session->claimed_id;
0216                 }
0217             } else if (defined('SID')) {
0218                 if (isset($_SESSION["zend_openid"]["identity"]) &&
0219                     isset($_SESSION["zend_openid"]["claimed_id"]) &&
0220                     $_SESSION["zend_openid"]["identity"] === $identity) {
0221                     $identity = $_SESSION["zend_openid"]["claimed_id"];
0222                 }
0223             } else {
0224                 // require_once "Zend/Session/Namespace.php";
0225                 $this->_session = new Zend_Session_Namespace("zend_openid");
0226                 if ($this->_session->identity === $identity) {
0227                     $identity = $this->_session->claimed_id;
0228                 }
0229             }
0230         }
0231 
0232         if (empty($params['openid_mode'])) {
0233             $this->_setError("Missing openid.mode");
0234             return false;
0235         }
0236         if (empty($params['openid_return_to'])) {
0237             $this->_setError("Missing openid.return_to");
0238             return false;
0239         }
0240         if (empty($params['openid_signed'])) {
0241             $this->_setError("Missing openid.signed");
0242             return false;
0243         }
0244         if (empty($params['openid_sig'])) {
0245             $this->_setError("Missing openid.sig");
0246             return false;
0247         }
0248         if ($params['openid_mode'] != 'id_res') {
0249             $this->_setError("Wrong openid.mode '".$params['openid_mode']."' != 'id_res'");
0250             return false;
0251         }
0252         if (empty($params['openid_assoc_handle'])) {
0253             $this->_setError("Missing openid.assoc_handle");
0254             return false;
0255         }
0256         if ($params['openid_return_to'] != Zend_OpenId::selfUrl()) {
0257             /* Ignore query part in openid.return_to */
0258             $pos = strpos($params['openid_return_to'], '?');
0259             if ($pos === false ||
0260                 SUBSTR($params['openid_return_to'], 0 , $pos) != Zend_OpenId::selfUrl()) {
0261 
0262                 $this->_setError("Wrong openid.return_to '".
0263                     $params['openid_return_to']."' != '" . Zend_OpenId::selfUrl() ."'");
0264                 return false;
0265             }
0266         }
0267         if ($version >= 2.0) {
0268             if (empty($params['openid_response_nonce'])) {
0269                 $this->_setError("Missing openid.response_nonce");
0270                 return false;
0271             }
0272             if (empty($params['openid_op_endpoint'])) {
0273                 $this->_setError("Missing openid.op_endpoint");
0274                 return false;
0275             /* OpenID 2.0 (11.3) Checking the Nonce */
0276             } else if (!$this->_storage->isUniqueNonce($params['openid_op_endpoint'], $params['openid_response_nonce'])) {
0277                 $this->_setError("Duplicate openid.response_nonce");
0278                 return false;
0279             }
0280         }
0281 
0282         if (!empty($params['openid_invalidate_handle'])) {
0283             if ($this->_storage->getAssociationByHandle(
0284                 $params['openid_invalidate_handle'],
0285                 $url,
0286                 $macFunc,
0287                 $secret,
0288                 $expires)) {
0289                 $this->_storage->delAssociation($url);
0290             }
0291         }
0292 
0293         if ($this->_storage->getAssociationByHandle(
0294                 $params['openid_assoc_handle'],
0295                 $url,
0296                 $macFunc,
0297                 $secret,
0298                 $expires)) {
0299             // Security fix - check the association bewteen op_endpoint and assoc_handle
0300             if (isset($params['openid_op_endpoint']) && $url !== $params['openid_op_endpoint']) {
0301                 $this->_setError("The op_endpoint URI is not the same of URI associated with the assoc_handle");
0302                 return false;
0303             }       
0304             $signed = explode(',', $params['openid_signed']);
0305             // Check the parameters for the signature
0306             // @see https://openid.net/specs/openid-authentication-2_0.html#positive_assertions
0307             $toCheck = $this->_signParams;
0308             if (isset($params['openid_claimed_id']) && isset($params['openid_identity'])) {
0309                 $toCheck = array_merge($toCheck, array('claimed_id', 'identity'));
0310             }
0311             foreach ($toCheck as $param) {
0312                 if (!in_array($param, $signed, true)) {
0313                     $this->_setError("The required parameter $param is missing in the signed");
0314                     return false;
0315                 }
0316             }
0317             
0318             $data = '';
0319             foreach ($signed as $key) {
0320                 $data .= $key . ':' . $params['openid_' . strtr($key,'.','_')] . "\n";
0321             }
0322             if (base64_decode($params['openid_sig']) ==
0323                 Zend_OpenId::hashHmac($macFunc, $data, $secret)) {
0324                 if (!Zend_OpenId_Extension::forAll($extensions, 'parseResponse', $params)) {
0325                     $this->_setError("Extension::parseResponse failure");
0326                     return false;
0327                 }
0328                 /* OpenID 2.0 (11.2) Verifying Discovered Information */
0329                 if (isset($params['openid_claimed_id'])) {
0330                     $id = $params['openid_claimed_id'];
0331                     if (!Zend_OpenId::normalize($id)) {
0332                         $this->_setError("Normalization failed");
0333                         return false;
0334                     } else if (!$this->_discovery($id, $discovered_server, $discovered_version)) {
0335                         $this->_setError("Discovery failed: " . $this->getError());
0336                         return false;
0337                     } else if ((!empty($params['openid_identity']) &&
0338                                 $params["openid_identity"] != $id) ||
0339                                (!empty($params['openid_op_endpoint']) &&
0340                                 $params['openid_op_endpoint'] != $discovered_server) ||
0341                                $discovered_version != $version) {
0342                         $this->_setError("Discovery information verification failed");
0343                         return false;
0344                     }
0345                 }
0346                 return true;
0347             }
0348             $this->_storage->delAssociation($url);
0349             $this->_setError("Signature check failed");
0350             return false;
0351         }
0352         else
0353         {
0354             /* Use dumb mode */
0355             if (isset($params['openid_claimed_id'])) {
0356                 $id = $params['openid_claimed_id'];
0357             } else if (isset($params['openid_identity'])) {
0358                 $id = $params['openid_identity'];
0359             } else {
0360                 $this->_setError("Missing openid.claimed_id and openid.identity");
0361                 return false;
0362             }
0363 
0364             if (!Zend_OpenId::normalize($id)) {
0365                 $this->_setError("Normalization failed");
0366                 return false;
0367             } else if (!$this->_discovery($id, $server, $discovered_version)) {
0368                 $this->_setError("Discovery failed: " . $this->getError());
0369                 return false;
0370             }
0371 
0372             /* OpenID 2.0 (11.2) Verifying Discovered Information */
0373             if ((isset($params['openid_identity']) &&
0374                  $params["openid_identity"] != $id) ||
0375                 (isset($params['openid_op_endpoint']) &&
0376                  $params['openid_op_endpoint'] != $server) ||
0377                 $discovered_version != $version) {
0378                 $this->_setError("Discovery information verification failed");
0379                 return false;
0380             }
0381 
0382             $params2 = array();
0383             foreach ($params as $key => $val) {
0384                 if (strpos($key, 'openid_ns_') === 0) {
0385                     $key = 'openid.ns.' . substr($key, strlen('openid_ns_'));
0386                 } else if (strpos($key, 'openid_sreg_') === 0) {
0387                     $key = 'openid.sreg.' . substr($key, strlen('openid_sreg_'));
0388                 } else if (strpos($key, 'openid_') === 0) {
0389                     $key = 'openid.' . substr($key, strlen('openid_'));
0390                 }
0391                 $params2[$key] = $val;
0392             }
0393             $params2['openid.mode'] = 'check_authentication';
0394             $ret = $this->_httpRequest($server, 'POST', $params2, $status);
0395             if ($status != 200) {
0396                 $this->_setError("'Dumb' signature verification HTTP request failed");
0397                 return false;
0398             }
0399             $r = array();
0400             if (is_string($ret)) {
0401                 foreach(explode("\n", $ret) as $line) {
0402                     $line = trim($line);
0403                     if (!empty($line)) {
0404                         $x = explode(':', $line, 2);
0405                         if (is_array($x) && count($x) == 2) {
0406                             list($key, $value) = $x;
0407                             $r[trim($key)] = trim($value);
0408                         }
0409                     }
0410                 }
0411             }
0412             $ret = $r;
0413             if (!empty($ret['invalidate_handle'])) {
0414                 if ($this->_storage->getAssociationByHandle(
0415                     $ret['invalidate_handle'],
0416                     $url,
0417                     $macFunc,
0418                     $secret,
0419                     $expires)) {
0420                     $this->_storage->delAssociation($url);
0421                 }
0422             }
0423             if (isset($ret['is_valid']) && $ret['is_valid'] == 'true') {
0424                 if (!Zend_OpenId_Extension::forAll($extensions, 'parseResponse', $params)) {
0425                     $this->_setError("Extension::parseResponse failure");
0426                     return false;
0427                 }
0428                 return true;
0429             }
0430             $this->_setError("'Dumb' signature verification failed");
0431             return false;
0432         }
0433     }
0434 
0435     /**
0436      * Store assiciation in internal chace and external storage
0437      *
0438      * @param string $url OpenID server url
0439      * @param string $handle association handle
0440      * @param string $macFunc HMAC function (sha1 or sha256)
0441      * @param string $secret shared secret
0442      * @param integer $expires expiration UNIX time
0443      * @return void
0444      */
0445     protected function _addAssociation($url, $handle, $macFunc, $secret, $expires)
0446     {
0447         $this->_cache[$url] = array($handle, $macFunc, $secret, $expires);
0448         return $this->_storage->addAssociation(
0449             $url,
0450             $handle,
0451             $macFunc,
0452             $secret,
0453             $expires);
0454     }
0455 
0456     /**
0457      * Retrive assiciation information for given $url from internal cahce or
0458      * external storage
0459      *
0460      * @param string $url OpenID server url
0461      * @param string &$handle association handle
0462      * @param string &$macFunc HMAC function (sha1 or sha256)
0463      * @param string &$secret shared secret
0464      * @param integer &$expires expiration UNIX time
0465      * @return void
0466      */
0467     protected function _getAssociation($url, &$handle, &$macFunc, &$secret, &$expires)
0468     {
0469         if (isset($this->_cache[$url])) {
0470             $handle   = $this->_cache[$url][0];
0471             $macFunc = $this->_cache[$url][1];
0472             $secret   = $this->_cache[$url][2];
0473             $expires  = $this->_cache[$url][3];
0474             return true;
0475         }
0476         if ($this->_storage->getAssociation(
0477                 $url,
0478                 $handle,
0479                 $macFunc,
0480                 $secret,
0481                 $expires)) {
0482             $this->_cache[$url] = array($handle, $macFunc, $secret, $expires);
0483             return true;
0484         }
0485         return false;
0486     }
0487 
0488     /**
0489      * Performs HTTP request to given $url using given HTTP $method.
0490      * Send additinal query specified by variable/value array,
0491      * On success returns HTTP response without headers, false on failure.
0492      *
0493      * @param string $url OpenID server url
0494      * @param string $method HTTP request method 'GET' or 'POST'
0495      * @param array $params additional qwery parameters to be passed with
0496      * @param int &$staus HTTP status code
0497      *  request
0498      * @return mixed
0499      */
0500     protected function _httpRequest($url, $method = 'GET', array $params = array(), &$status = null)
0501     {
0502         $client = $this->_httpClient;
0503         if ($client === null) {
0504             $client = new Zend_Http_Client(
0505                     $url,
0506                     array(
0507                         'maxredirects' => 4,
0508                         'timeout'      => 15,
0509                         'useragent'    => 'Zend_OpenId'
0510                     )
0511                 );
0512         } else {
0513             $client->setUri($url);
0514         }
0515 
0516         $client->resetParameters();
0517         if ($method == 'POST') {
0518             $client->setMethod(Zend_Http_Client::POST);
0519             $client->setParameterPost($params);
0520         } else {
0521             $client->setMethod(Zend_Http_Client::GET);
0522             $client->setParameterGet($params);
0523         }
0524 
0525         try {
0526             $response = $client->request();
0527         } catch (Exception $e) {
0528             $this->_setError('HTTP Request failed: ' . $e->getMessage());
0529             return false;
0530         }
0531         $status = $response->getStatus();
0532         $body = $response->getBody();
0533         if ($status == 200 || ($status == 400 && !empty($body))) {
0534             return $body;
0535         }else{
0536             $this->_setError('Bad HTTP response');
0537             return false;
0538         }
0539     }
0540 
0541     /**
0542      * Create (or reuse existing) association between OpenID consumer and
0543      * OpenID server based on Diffie-Hellman key agreement. Returns true
0544      * on success and false on failure.
0545      *
0546      * @param string $url OpenID server url
0547      * @param float $version OpenID protocol version
0548      * @param string $priv_key for testing only
0549      * @return bool
0550      */
0551     protected function _associate($url, $version, $priv_key=null)
0552     {
0553 
0554         /* Check if we already have association in chace or storage */
0555         if ($this->_getAssociation(
0556                 $url,
0557                 $handle,
0558                 $macFunc,
0559                 $secret,
0560                 $expires)) {
0561             return true;
0562         }
0563 
0564         if ($this->_dumbMode) {
0565             /* Use dumb mode */
0566             return true;
0567         }
0568 
0569         $params = array();
0570 
0571         if ($version >= 2.0) {
0572             $params = array(
0573                 'openid.ns'           => Zend_OpenId::NS_2_0,
0574                 'openid.mode'         => 'associate',
0575                 'openid.assoc_type'   => 'HMAC-SHA256',
0576                 'openid.session_type' => 'DH-SHA256',
0577             );
0578         } else {
0579             $params = array(
0580                 'openid.mode'         => 'associate',
0581                 'openid.assoc_type'   => 'HMAC-SHA1',
0582                 'openid.session_type' => 'DH-SHA1',
0583             );
0584         }
0585 
0586         $dh = Zend_OpenId::createDhKey(pack('H*', Zend_OpenId::DH_P),
0587                                        pack('H*', Zend_OpenId::DH_G),
0588                                        $priv_key);
0589         $dh_details = Zend_OpenId::getDhKeyDetails($dh);
0590 
0591         $params['openid.dh_modulus']         = base64_encode(
0592             Zend_OpenId::btwoc($dh_details['p']));
0593         $params['openid.dh_gen']             = base64_encode(
0594             Zend_OpenId::btwoc($dh_details['g']));
0595         $params['openid.dh_consumer_public'] = base64_encode(
0596             Zend_OpenId::btwoc($dh_details['pub_key']));
0597 
0598         while(1) {
0599             $ret = $this->_httpRequest($url, 'POST', $params, $status);
0600             if ($ret === false) {
0601                 $this->_setError("HTTP request failed");
0602                 return false;
0603             }
0604 
0605             $r = array();
0606             $bad_response = false;
0607             foreach(explode("\n", $ret) as $line) {
0608                 $line = trim($line);
0609                 if (!empty($line)) {
0610                     $x = explode(':', $line, 2);
0611                     if (is_array($x) && count($x) == 2) {
0612                         list($key, $value) = $x;
0613                         $r[trim($key)] = trim($value);
0614                     } else {
0615                         $bad_response = true;
0616                     }
0617                 }
0618             }
0619             if ($bad_response && strpos($ret, 'Unknown session type') !== false) {
0620                 $r['error_code'] = 'unsupported-type';
0621             }
0622             $ret = $r;
0623 
0624             if (isset($ret['error_code']) &&
0625                 $ret['error_code'] == 'unsupported-type') {
0626                 if ($params['openid.session_type'] == 'DH-SHA256') {
0627                     $params['openid.session_type'] = 'DH-SHA1';
0628                     $params['openid.assoc_type'] = 'HMAC-SHA1';
0629                 } else if ($params['openid.session_type'] == 'DH-SHA1') {
0630                     $params['openid.session_type'] = 'no-encryption';
0631                 } else {
0632                     $this->_setError("The OpenID service responded with: " . $ret['error_code']);
0633                     return false;
0634                 }
0635             } else {
0636                 break;
0637             }
0638         }
0639 
0640         if ($status != 200) {
0641             $this->_setError("The server responded with status code: " . $status);
0642             return false;
0643         }
0644 
0645         if ($version >= 2.0 &&
0646             isset($ret['ns']) &&
0647             $ret['ns'] != Zend_OpenId::NS_2_0) {
0648             $this->_setError("Wrong namespace definition in the server response");
0649             return false;
0650         }
0651 
0652         if (!isset($ret['assoc_handle']) ||
0653             !isset($ret['expires_in']) ||
0654             !isset($ret['assoc_type']) ||
0655             $params['openid.assoc_type'] != $ret['assoc_type']) {
0656             if ($params['openid.assoc_type'] != $ret['assoc_type']) {
0657                 $this->_setError("The returned assoc_type differed from the supplied openid.assoc_type");
0658             } else {
0659                 $this->_setError("Missing required data from provider (assoc_handle, expires_in, assoc_type are required)");
0660             }
0661             return false;
0662         }
0663 
0664         $handle     = $ret['assoc_handle'];
0665         $expiresIn = $ret['expires_in'];
0666 
0667         if ($ret['assoc_type'] == 'HMAC-SHA1') {
0668             $macFunc = 'sha1';
0669         } else if ($ret['assoc_type'] == 'HMAC-SHA256' &&
0670             $version >= 2.0) {
0671             $macFunc = 'sha256';
0672         } else {
0673             $this->_setError("Unsupported assoc_type");
0674             return false;
0675         }
0676 
0677         if ((empty($ret['session_type']) ||
0678              ($version >= 2.0 && $ret['session_type'] == 'no-encryption')) &&
0679              isset($ret['mac_key'])) {
0680             $secret = base64_decode($ret['mac_key']);
0681         } else if (isset($ret['session_type']) &&
0682             $ret['session_type'] == 'DH-SHA1' &&
0683             !empty($ret['dh_server_public']) &&
0684             !empty($ret['enc_mac_key'])) {
0685             $dhFunc = 'sha1';
0686         } else if (isset($ret['session_type']) &&
0687             $ret['session_type'] == 'DH-SHA256' &&
0688             $version >= 2.0 &&
0689             !empty($ret['dh_server_public']) &&
0690             !empty($ret['enc_mac_key'])) {
0691             $dhFunc = 'sha256';
0692         } else {
0693             $this->_setError("Unsupported session_type");
0694             return false;
0695         }
0696         if (isset($dhFunc)) {
0697             $serverPub = base64_decode($ret['dh_server_public']);
0698             $dhSec = Zend_OpenId::computeDhSecret($serverPub, $dh);
0699             if ($dhSec === false) {
0700                 $this->_setError("DH secret comutation failed");
0701                 return false;
0702             }
0703             $sec = Zend_OpenId::digest($dhFunc, $dhSec);
0704             if ($sec === false) {
0705                 $this->_setError("Could not create digest");
0706                 return false;
0707             }
0708             $secret = $sec ^ base64_decode($ret['enc_mac_key']);
0709         }
0710         if ($macFunc == 'sha1') {
0711             if (Zend_OpenId::strlen($secret) != 20) {
0712                 $this->_setError("The length of the sha1 secret must be 20");
0713                 return false;
0714             }
0715         } else if ($macFunc == 'sha256') {
0716             if (Zend_OpenId::strlen($secret) != 32) {
0717                 $this->_setError("The length of the sha256 secret must be 32");
0718                 return false;
0719             }
0720         }
0721         $this->_addAssociation(
0722             $url,
0723             $handle,
0724             $macFunc,
0725             $secret,
0726             time() + $expiresIn);
0727         return true;
0728     }
0729 
0730     /**
0731      * Performs discovery of identity and finds OpenID URL, OpenID server URL
0732      * and OpenID protocol version. Returns true on succees and false on
0733      * failure.
0734      *
0735      * @param string &$id OpenID identity URL
0736      * @param string &$server OpenID server URL
0737      * @param float &$version OpenID protocol version
0738      * @return bool
0739      * @todo OpenID 2.0 (7.3) XRI and Yadis discovery
0740      */
0741     protected function _discovery(&$id, &$server, &$version)
0742     {
0743         $realId = $id;
0744         if ($this->_storage->getDiscoveryInfo(
0745                 $id,
0746                 $realId,
0747                 $server,
0748                 $version,
0749                 $expire)) {
0750             $id = $realId;
0751             return true;
0752         }
0753 
0754         $response = $this->_httpRequest($id, 'GET', array(), $status);
0755         if ($status != 200 || !is_string($response)) {
0756             return false;
0757         }
0758 
0759         /* OpenID 2.0 (7.3) XRI and Yadis discovery */
0760         if (preg_match(
0761                 '/<meta[^>]*http-equiv=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?X-XRDS-Location[ \t]*[^"\']*\\1[^>]*content=(["\'])([^"\']+)\\2[^>]*\/?>/i',
0762                 $response,
0763                 $r)) {
0764             $XRDS = $r[3];
0765             $version = 2.0;
0766             $response = $this->_httpRequest($XRDS); 
0767             if (preg_match(
0768                     '/<URI>([^\t]*)<\/URI>/i',
0769                     $response,
0770                     $x)) {
0771                 $server = $x[1];
0772                 // $realId 
0773                 $realId = 'http://specs.openid.net/auth/2.0/identifier_select';
0774             }
0775             else {
0776                 $this->_setError("Unable to get URI for XRDS discovery");
0777             }
0778         }
0779 
0780         /* HTML-based discovery */
0781         else if (preg_match(
0782                 '/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.provider[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
0783                 $response,
0784                 $r)) {
0785             $version = 2.0;
0786             $server = $r[3];
0787         } else if (preg_match(
0788                 '/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.provider[ \t]*[^"\']*\\3[^>]*\/?>/i',
0789                 $response,
0790                 $r)) {
0791             $version = 2.0;
0792             $server = $r[2];
0793         } else if (preg_match(
0794                 '/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.server[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
0795                 $response,
0796                 $r)) {
0797             $version = 1.1;
0798             $server = $r[3];
0799         } else if (preg_match(
0800                 '/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.server[ \t]*[^"\']*\\3[^>]*\/?>/i',
0801                 $response,
0802                 $r)) {
0803             $version = 1.1;
0804             $server = $r[2];
0805         } else {
0806             return false;
0807         }
0808         if ($version >= 2.0) {
0809             if (preg_match(
0810                     '/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.local_id[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
0811                     $response,
0812                     $r)) {
0813                 $realId = $r[3];
0814             } else if (preg_match(
0815                     '/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.local_id[ \t]*[^"\']*\\3[^>]*\/?>/i',
0816                     $response,
0817                     $r)) {
0818                 $realId = $r[2];
0819             }
0820         } else {
0821             if (preg_match(
0822                     '/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.delegate[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
0823                     $response,
0824                     $r)) {
0825                 $realId = $r[3];
0826             } else if (preg_match(
0827                     '/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.delegate[ \t]*[^"\']*\\3[^>]*\/?>/i',
0828                     $response,
0829                     $r)) {
0830                 $realId = $r[2];
0831             }
0832         }
0833 
0834         $expire = time() + 60 * 60;
0835         $this->_storage->addDiscoveryInfo($id, $realId, $server, $version, $expire);
0836         $id = $realId;
0837         return true;
0838     }
0839 
0840     /**
0841      * Performs check of OpenID identity.
0842      *
0843      * This is the first step of OpenID authentication process.
0844      * On success the function does not return (it does HTTP redirection to
0845      * server and exits). On failure it returns false.
0846      *
0847      * @param bool $immediate enables or disables interaction with user
0848      * @param string $id OpenID identity
0849      * @param string $returnTo HTTP URL to redirect response from server to
0850      * @param string $root HTTP URL to identify consumer on server
0851      * @param mixed $extensions extension object or array of extensions objects
0852      * @param Zend_Controller_Response_Abstract $response an optional response
0853      *  object to perform HTTP or HTML form redirection
0854      * @return bool
0855      */
0856     protected function _checkId($immediate, $id, $returnTo=null, $root=null,
0857         $extensions=null, Zend_Controller_Response_Abstract $response = null)
0858     {
0859         $this->_setError('');
0860 
0861         if (!Zend_OpenId::normalize($id)) {
0862             $this->_setError("Normalisation failed");
0863             return false;
0864         }
0865         $claimedId = $id;
0866 
0867         if (!$this->_discovery($id, $server, $version)) {
0868             $this->_setError("Discovery failed: " . $this->getError());
0869             return false;
0870         }
0871         if (!$this->_associate($server, $version)) {
0872             $this->_setError("Association failed: " . $this->getError());
0873             return false;
0874         }
0875         if (!$this->_getAssociation(
0876                 $server,
0877                 $handle,
0878                 $macFunc,
0879                 $secret,
0880                 $expires)) {
0881             /* Use dumb mode */
0882             unset($handle);
0883             unset($macFunc);
0884             unset($secret);
0885             unset($expires);
0886         }
0887 
0888         $params = array();
0889         if ($version >= 2.0) {
0890             $params['openid.ns'] = Zend_OpenId::NS_2_0;
0891         }
0892 
0893         $params['openid.mode'] = $immediate ?
0894             'checkid_immediate' : 'checkid_setup';
0895 
0896         $params['openid.identity'] = $id;
0897 
0898         $params['openid.claimed_id'] = $claimedId;
0899 
0900         if ($version <= 2.0) {
0901             if ($this->_session !== null) {
0902                 $this->_session->identity = $id;
0903                 $this->_session->claimed_id = $claimedId;
0904             } else if (defined('SID')) {
0905                 $_SESSION["zend_openid"] = array(
0906                     "identity" => $id,
0907                     "claimed_id" => $claimedId);
0908             } else {
0909                 // require_once "Zend/Session/Namespace.php";
0910                 $this->_session = new Zend_Session_Namespace("zend_openid");
0911                 $this->_session->identity = $id;
0912                 $this->_session->claimed_id = $claimedId;
0913             }
0914         }
0915 
0916         if (isset($handle)) {
0917             $params['openid.assoc_handle'] = $handle;
0918         }
0919 
0920         $params['openid.return_to'] = Zend_OpenId::absoluteUrl($returnTo);
0921 
0922         if (empty($root)) {
0923             $root = Zend_OpenId::selfUrl();
0924             if ($root[strlen($root)-1] != '/') {
0925                 $root = dirname($root);
0926             }
0927         }
0928         if ($version >= 2.0) {
0929             $params['openid.realm'] = $root;
0930         } else {
0931             $params['openid.trust_root'] = $root;
0932         }
0933 
0934         if (!Zend_OpenId_Extension::forAll($extensions, 'prepareRequest', $params)) {
0935             $this->_setError("Extension::prepareRequest failure");
0936             return false;
0937         }
0938 
0939         Zend_OpenId::redirect($server, $params, $response);
0940         return true;
0941     }
0942 
0943     /**
0944      * Sets HTTP client object to make HTTP requests
0945      *
0946      * @param Zend_Http_Client $client HTTP client object to be used
0947      */
0948     public function setHttpClient($client) {
0949         $this->_httpClient = $client;
0950     }
0951 
0952     /**
0953      * Returns HTTP client object that will be used to make HTTP requests
0954      *
0955      * @return Zend_Http_Client
0956      */
0957     public function getHttpClient() {
0958         return $this->_httpClient;
0959     }
0960 
0961     /**
0962      * Sets session object to store climed_id
0963      *
0964      * @param Zend_Session_Namespace $session HTTP client object to be used
0965      */
0966     public function setSession(Zend_Session_Namespace $session) {
0967         $this->_session = $session;
0968     }
0969 
0970     /**
0971      * Returns session object that is used to store climed_id
0972      *
0973      * @return Zend_Session_Namespace
0974      */
0975     public function getSession() {
0976         return $this->_session;
0977     }
0978 
0979     /**
0980      * Saves error message
0981      *
0982      * @param string $message error message
0983      */
0984     protected function _setError($message)
0985     {
0986         $this->_error = $message;
0987     }
0988 
0989     /**
0990      * Returns error message that explains failure of login, check or verify
0991      *
0992      * @return string
0993      */
0994     public function getError()
0995     {
0996         return $this->_error;
0997     }
0998 
0999 }