mirror of
				https://github.com/Icinga/icingaweb2.git
				synced 2025-10-31 03:14:31 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			993 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			993 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| /**
 | |
|  * Zend Framework
 | |
|  *
 | |
|  * LICENSE
 | |
|  *
 | |
|  * This source file is subject to the new BSD license that is bundled
 | |
|  * with this package in the file LICENSE.txt.
 | |
|  * It is also available through the world-wide-web at this URL:
 | |
|  * http://framework.zend.com/license/new-bsd
 | |
|  * If you did not receive a copy of the license and are unable to
 | |
|  * obtain it through the world-wide-web, please send an email
 | |
|  * to license@zend.com so we can send you a copy immediately.
 | |
|  *
 | |
|  * @category   Zend
 | |
|  * @package    Zend_OpenId
 | |
|  * @subpackage Zend_OpenId_Consumer
 | |
|  * @copyright  Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
 | |
|  * @license    http://framework.zend.com/license/new-bsd     New BSD License
 | |
|  * @version    $Id: Consumer.php 24593 2012-01-05 20:35:02Z matthew $
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * @see Zend_OpenId
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * @see Zend_OpenId_Extension
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * @see Zend_OpenId_Consumer_Storage
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * @see Zend_Http_Client
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * OpenID consumer implementation
 | |
|  *
 | |
|  * @category   Zend
 | |
|  * @package    Zend_OpenId
 | |
|  * @subpackage Zend_OpenId_Consumer
 | |
|  * @copyright  Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
 | |
|  * @license    http://framework.zend.com/license/new-bsd     New BSD License
 | |
|  */
 | |
| class Zend_OpenId_Consumer
 | |
| {
 | |
| 
 | |
|     /**
 | |
|      * Parameters required for signature
 | |
|      */
 | |
|     protected $_signParams = array('op_endpoint', 'return_to', 'response_nonce', 'assoc_handle');
 | |
| 
 | |
|     /**
 | |
|      * Reference to an implementation of storage object
 | |
|      *
 | |
|      * @var Zend_OpenId_Consumer_Storage $_storage
 | |
|      */
 | |
|     protected $_storage = null;
 | |
| 
 | |
|     /**
 | |
|      * Enables or disables consumer to use association with server based on
 | |
|      * Diffie-Hellman key agreement
 | |
|      *
 | |
|      * @var Zend_OpenId_Consumer_Storage $_dumbMode
 | |
|      */
 | |
|     protected $_dumbMode = false;
 | |
| 
 | |
|     /**
 | |
|      * Internal cache to prevent unnecessary access to storage
 | |
|      *
 | |
|      * @var array $_cache
 | |
|      */
 | |
|     protected $_cache = array();
 | |
| 
 | |
|     /**
 | |
|      * HTTP client to make HTTP requests
 | |
|      *
 | |
|      * @var Zend_Http_Client $_httpClient
 | |
|      */
 | |
|     private $_httpClient = null;
 | |
| 
 | |
|     /**
 | |
|      * HTTP session to store climed_id between requests
 | |
|      *
 | |
|      * @var Zend_Session_Namespace $_session
 | |
|      */
 | |
|     private $_session = null;
 | |
| 
 | |
|     /**
 | |
|      * Last error message for logi, check or verify failure
 | |
|      *
 | |
|      * @var string $_error
 | |
|      */
 | |
|     private $_error = '';
 | |
| 
 | |
|     /**
 | |
|      * Constructs a Zend_OpenId_Consumer object with given $storage.
 | |
|      * Enables or disables future association with server based on
 | |
|      * Diffie-Hellman key agreement.
 | |
|      *
 | |
|      * @param Zend_OpenId_Consumer_Storage $storage implementation of custom
 | |
|      *  storage object
 | |
|      * @param bool $dumbMode Enables or disables consumer to use association
 | |
|      *  with server based on Diffie-Hellman key agreement
 | |
|      */
 | |
|     public function __construct(Zend_OpenId_Consumer_Storage $storage = null,
 | |
|                                 $dumbMode = false)
 | |
|     {
 | |
|         if ($storage === null) {
 | |
|             $this->_storage = new Zend_OpenId_Consumer_Storage_File();
 | |
|         } else {
 | |
|             $this->_storage = $storage;
 | |
|         }
 | |
|         $this->_dumbMode = $dumbMode;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Performs check (with possible user interaction) of OpenID identity.
 | |
|      *
 | |
|      * This is the first step of OpenID authentication process.
 | |
|      * On success the function does not return (it does HTTP redirection to
 | |
|      * server and exits). On failure it returns false.
 | |
|      *
 | |
|      * @param string $id OpenID identity
 | |
|      * @param string $returnTo URL to redirect response from server to
 | |
|      * @param string $root HTTP URL to identify consumer on server
 | |
|      * @param mixed $extensions extension object or array of extensions objects
 | |
|      * @param Zend_Controller_Response_Abstract $response an optional response
 | |
|      *  object to perform HTTP or HTML form redirection
 | |
|      * @return bool
 | |
|      */
 | |
|     public function login($id, $returnTo = null, $root = null, $extensions = null,
 | |
|                           Zend_Controller_Response_Abstract $response = null)
 | |
|     {
 | |
|         return $this->_checkId(
 | |
|             false,
 | |
|             $id,
 | |
|             $returnTo,
 | |
|             $root,
 | |
|             $extensions,
 | |
|             $response);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Performs immediate check (without user interaction) of OpenID identity.
 | |
|      *
 | |
|      * This is the first step of OpenID authentication process.
 | |
|      * On success the function does not return (it does HTTP redirection to
 | |
|      * server and exits). On failure it returns false.
 | |
|      *
 | |
|      * @param string $id OpenID identity
 | |
|      * @param string $returnTo HTTP URL to redirect response from server to
 | |
|      * @param string $root HTTP URL to identify consumer on server
 | |
|      * @param mixed $extensions extension object or array of extensions objects
 | |
|      * @param Zend_Controller_Response_Abstract $response an optional response
 | |
|      *  object to perform HTTP or HTML form redirection
 | |
|      * @return bool
 | |
|      */
 | |
|     public function check($id, $returnTo=null, $root=null, $extensions = null,
 | |
|                           Zend_Controller_Response_Abstract $response = null)
 | |
| 
 | |
|     {
 | |
|         return $this->_checkId(
 | |
|             true,
 | |
|             $id,
 | |
|             $returnTo,
 | |
|             $root,
 | |
|             $extensions,
 | |
|             $response);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Verifies authentication response from OpenID server.
 | |
|      *
 | |
|      * This is the second step of OpenID authentication process.
 | |
|      * The function returns true on successful authentication and false on
 | |
|      * failure.
 | |
|      *
 | |
|      * @param array $params HTTP query data from OpenID server
 | |
|      * @param string &$identity this argument is set to end-user's claimed
 | |
|      *  identifier or OpenID provider local identifier.
 | |
|      * @param mixed $extensions extension object or array of extensions objects
 | |
|      * @return bool
 | |
|      */
 | |
|     public function verify($params, &$identity = "", $extensions = null)
 | |
|     {
 | |
|         $this->_setError('');
 | |
| 
 | |
|         $version = 1.1;
 | |
|         if (isset($params['openid_ns']) &&
 | |
|             $params['openid_ns'] == Zend_OpenId::NS_2_0) {
 | |
|             $version = 2.0;
 | |
|         }
 | |
| 
 | |
|         if (isset($params["openid_claimed_id"])) {
 | |
|             $identity = $params["openid_claimed_id"];
 | |
|         } else if (isset($params["openid_identity"])){
 | |
|             $identity = $params["openid_identity"];
 | |
|         } else {
 | |
|             $identity = "";
 | |
|         }
 | |
| 
 | |
|         if ($version < 2.0 && !isset($params["openid_claimed_id"])) {
 | |
|             if ($this->_session !== null) {
 | |
|                 if ($this->_session->identity === $identity) {
 | |
|                     $identity = $this->_session->claimed_id;
 | |
|                 }
 | |
|             } else if (defined('SID')) {
 | |
|                 if (isset($_SESSION["zend_openid"]["identity"]) &&
 | |
|                     isset($_SESSION["zend_openid"]["claimed_id"]) &&
 | |
|                     $_SESSION["zend_openid"]["identity"] === $identity) {
 | |
|                     $identity = $_SESSION["zend_openid"]["claimed_id"];
 | |
|                 }
 | |
|             } else {
 | |
|                 $this->_session = new Zend_Session_Namespace("zend_openid");
 | |
|                 if ($this->_session->identity === $identity) {
 | |
|                     $identity = $this->_session->claimed_id;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (empty($params['openid_mode'])) {
 | |
|             $this->_setError("Missing openid.mode");
 | |
|             return false;
 | |
|         }
 | |
|         if (empty($params['openid_return_to'])) {
 | |
|             $this->_setError("Missing openid.return_to");
 | |
|             return false;
 | |
|         }
 | |
|         if (empty($params['openid_signed'])) {
 | |
|             $this->_setError("Missing openid.signed");
 | |
|             return false;
 | |
|         }
 | |
|         if (empty($params['openid_sig'])) {
 | |
|             $this->_setError("Missing openid.sig");
 | |
|             return false;
 | |
|         }
 | |
|         if ($params['openid_mode'] != 'id_res') {
 | |
|             $this->_setError("Wrong openid.mode '".$params['openid_mode']."' != 'id_res'");
 | |
|             return false;
 | |
|         }
 | |
|         if (empty($params['openid_assoc_handle'])) {
 | |
|             $this->_setError("Missing openid.assoc_handle");
 | |
|             return false;
 | |
|         }
 | |
|         if ($params['openid_return_to'] != Zend_OpenId::selfUrl()) {
 | |
|             /* Ignore query part in openid.return_to */
 | |
|             $pos = strpos($params['openid_return_to'], '?');
 | |
|             if ($pos === false ||
 | |
|                 SUBSTR($params['openid_return_to'], 0 , $pos) != Zend_OpenId::selfUrl()) {
 | |
| 
 | |
|                 $this->_setError("Wrong openid.return_to '".
 | |
|                     $params['openid_return_to']."' != '" . Zend_OpenId::selfUrl() ."'");
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
|         if ($version >= 2.0) {
 | |
|             if (empty($params['openid_response_nonce'])) {
 | |
|                 $this->_setError("Missing openid.response_nonce");
 | |
|                 return false;
 | |
|             }
 | |
|             if (empty($params['openid_op_endpoint'])) {
 | |
|                 $this->_setError("Missing openid.op_endpoint");
 | |
|                 return false;
 | |
|             /* OpenID 2.0 (11.3) Checking the Nonce */
 | |
|             } else if (!$this->_storage->isUniqueNonce($params['openid_op_endpoint'], $params['openid_response_nonce'])) {
 | |
|                 $this->_setError("Duplicate openid.response_nonce");
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (!empty($params['openid_invalidate_handle'])) {
 | |
|             if ($this->_storage->getAssociationByHandle(
 | |
|                 $params['openid_invalidate_handle'],
 | |
|                 $url,
 | |
|                 $macFunc,
 | |
|                 $secret,
 | |
|                 $expires)) {
 | |
|                 $this->_storage->delAssociation($url);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if ($this->_storage->getAssociationByHandle(
 | |
|                 $params['openid_assoc_handle'],
 | |
|                 $url,
 | |
|                 $macFunc,
 | |
|                 $secret,
 | |
|                 $expires)) {
 | |
|             // Security fix - check the association bewteen op_endpoint and assoc_handle
 | |
|             if (isset($params['openid_op_endpoint']) && $url !== $params['openid_op_endpoint']) {
 | |
|                 $this->_setError("The op_endpoint URI is not the same of URI associated with the assoc_handle");
 | |
|                 return false;
 | |
|             }       
 | |
|             $signed = explode(',', $params['openid_signed']);
 | |
|             // Check the parameters for the signature
 | |
|             // @see https://openid.net/specs/openid-authentication-2_0.html#positive_assertions
 | |
|             $toCheck = $this->_signParams;
 | |
|             if (isset($params['openid_claimed_id']) && isset($params['openid_identity'])) {
 | |
|                 $toCheck = array_merge($toCheck, array('claimed_id', 'identity'));
 | |
|             }
 | |
|             foreach ($toCheck as $param) {
 | |
|                 if (!in_array($param, $signed, true)) {
 | |
|                     $this->_setError("The required parameter $param is missing in the signed");
 | |
|                     return false;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             $data = '';
 | |
|             foreach ($signed as $key) {
 | |
|                 $data .= $key . ':' . $params['openid_' . strtr($key,'.','_')] . "\n";
 | |
|             }
 | |
|             if (base64_decode($params['openid_sig']) ==
 | |
|                 Zend_OpenId::hashHmac($macFunc, $data, $secret)) {
 | |
|                 if (!Zend_OpenId_Extension::forAll($extensions, 'parseResponse', $params)) {
 | |
|                     $this->_setError("Extension::parseResponse failure");
 | |
|                     return false;
 | |
|                 }
 | |
|                 /* OpenID 2.0 (11.2) Verifying Discovered Information */
 | |
|                 if (isset($params['openid_claimed_id'])) {
 | |
|                     $id = $params['openid_claimed_id'];
 | |
|                     if (!Zend_OpenId::normalize($id)) {
 | |
|                         $this->_setError("Normalization failed");
 | |
|                         return false;
 | |
|                     } else if (!$this->_discovery($id, $discovered_server, $discovered_version)) {
 | |
|                         $this->_setError("Discovery failed: " . $this->getError());
 | |
|                         return false;
 | |
|                     } else if ((!empty($params['openid_identity']) &&
 | |
|                                 $params["openid_identity"] != $id) ||
 | |
|                                (!empty($params['openid_op_endpoint']) &&
 | |
|                                 $params['openid_op_endpoint'] != $discovered_server) ||
 | |
|                                $discovered_version != $version) {
 | |
|                         $this->_setError("Discovery information verification failed");
 | |
|                         return false;
 | |
|                     }
 | |
|                 }
 | |
|                 return true;
 | |
|             }
 | |
|             $this->_storage->delAssociation($url);
 | |
|             $this->_setError("Signature check failed");
 | |
|             return false;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             /* Use dumb mode */
 | |
|             if (isset($params['openid_claimed_id'])) {
 | |
|                 $id = $params['openid_claimed_id'];
 | |
|             } else if (isset($params['openid_identity'])) {
 | |
|                 $id = $params['openid_identity'];
 | |
|             } else {
 | |
|                 $this->_setError("Missing openid.claimed_id and openid.identity");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (!Zend_OpenId::normalize($id)) {
 | |
|                 $this->_setError("Normalization failed");
 | |
|                 return false;
 | |
|             } else if (!$this->_discovery($id, $server, $discovered_version)) {
 | |
|                 $this->_setError("Discovery failed: " . $this->getError());
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             /* OpenID 2.0 (11.2) Verifying Discovered Information */
 | |
|             if ((isset($params['openid_identity']) &&
 | |
|                  $params["openid_identity"] != $id) ||
 | |
|                 (isset($params['openid_op_endpoint']) &&
 | |
|                  $params['openid_op_endpoint'] != $server) ||
 | |
|                 $discovered_version != $version) {
 | |
|                 $this->_setError("Discovery information verification failed");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             $params2 = array();
 | |
|             foreach ($params as $key => $val) {
 | |
|                 if (strpos($key, 'openid_ns_') === 0) {
 | |
|                     $key = 'openid.ns.' . substr($key, strlen('openid_ns_'));
 | |
|                 } else if (strpos($key, 'openid_sreg_') === 0) {
 | |
|                     $key = 'openid.sreg.' . substr($key, strlen('openid_sreg_'));
 | |
|                 } else if (strpos($key, 'openid_') === 0) {
 | |
|                     $key = 'openid.' . substr($key, strlen('openid_'));
 | |
|                 }
 | |
|                 $params2[$key] = $val;
 | |
|             }
 | |
|             $params2['openid.mode'] = 'check_authentication';
 | |
|             $ret = $this->_httpRequest($server, 'POST', $params2, $status);
 | |
|             if ($status != 200) {
 | |
|                 $this->_setError("'Dumb' signature verification HTTP request failed");
 | |
|                 return false;
 | |
|             }
 | |
|             $r = array();
 | |
|             if (is_string($ret)) {
 | |
|                 foreach(explode("\n", $ret) as $line) {
 | |
|                     $line = trim($line);
 | |
|                     if (!empty($line)) {
 | |
|                         $x = explode(':', $line, 2);
 | |
|                         if (is_array($x) && count($x) == 2) {
 | |
|                             list($key, $value) = $x;
 | |
|                             $r[trim($key)] = trim($value);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             $ret = $r;
 | |
|             if (!empty($ret['invalidate_handle'])) {
 | |
|                 if ($this->_storage->getAssociationByHandle(
 | |
|                     $ret['invalidate_handle'],
 | |
|                     $url,
 | |
|                     $macFunc,
 | |
|                     $secret,
 | |
|                     $expires)) {
 | |
|                     $this->_storage->delAssociation($url);
 | |
|                 }
 | |
|             }
 | |
|             if (isset($ret['is_valid']) && $ret['is_valid'] == 'true') {
 | |
|                 if (!Zend_OpenId_Extension::forAll($extensions, 'parseResponse', $params)) {
 | |
|                     $this->_setError("Extension::parseResponse failure");
 | |
|                     return false;
 | |
|                 }
 | |
|                 return true;
 | |
|             }
 | |
|             $this->_setError("'Dumb' signature verification failed");
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Store assiciation in internal chace and external storage
 | |
|      *
 | |
|      * @param string $url OpenID server url
 | |
|      * @param string $handle association handle
 | |
|      * @param string $macFunc HMAC function (sha1 or sha256)
 | |
|      * @param string $secret shared secret
 | |
|      * @param integer $expires expiration UNIX time
 | |
|      * @return void
 | |
|      */
 | |
|     protected function _addAssociation($url, $handle, $macFunc, $secret, $expires)
 | |
|     {
 | |
|         $this->_cache[$url] = array($handle, $macFunc, $secret, $expires);
 | |
|         return $this->_storage->addAssociation(
 | |
|             $url,
 | |
|             $handle,
 | |
|             $macFunc,
 | |
|             $secret,
 | |
|             $expires);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Retrive assiciation information for given $url from internal cahce or
 | |
|      * external storage
 | |
|      *
 | |
|      * @param string $url OpenID server url
 | |
|      * @param string &$handle association handle
 | |
|      * @param string &$macFunc HMAC function (sha1 or sha256)
 | |
|      * @param string &$secret shared secret
 | |
|      * @param integer &$expires expiration UNIX time
 | |
|      * @return void
 | |
|      */
 | |
|     protected function _getAssociation($url, &$handle, &$macFunc, &$secret, &$expires)
 | |
|     {
 | |
|         if (isset($this->_cache[$url])) {
 | |
|             $handle   = $this->_cache[$url][0];
 | |
|             $macFunc = $this->_cache[$url][1];
 | |
|             $secret   = $this->_cache[$url][2];
 | |
|             $expires  = $this->_cache[$url][3];
 | |
|             return true;
 | |
|         }
 | |
|         if ($this->_storage->getAssociation(
 | |
|                 $url,
 | |
|                 $handle,
 | |
|                 $macFunc,
 | |
|                 $secret,
 | |
|                 $expires)) {
 | |
|             $this->_cache[$url] = array($handle, $macFunc, $secret, $expires);
 | |
|             return true;
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Performs HTTP request to given $url using given HTTP $method.
 | |
|      * Send additinal query specified by variable/value array,
 | |
|      * On success returns HTTP response without headers, false on failure.
 | |
|      *
 | |
|      * @param string $url OpenID server url
 | |
|      * @param string $method HTTP request method 'GET' or 'POST'
 | |
|      * @param array $params additional qwery parameters to be passed with
 | |
|      * @param int &$staus HTTP status code
 | |
|      *  request
 | |
|      * @return mixed
 | |
|      */
 | |
|     protected function _httpRequest($url, $method = 'GET', array $params = array(), &$status = null)
 | |
|     {
 | |
|         $client = $this->_httpClient;
 | |
|         if ($client === null) {
 | |
|             $client = new Zend_Http_Client(
 | |
|                     $url,
 | |
|                     array(
 | |
|                         'maxredirects' => 4,
 | |
|                         'timeout'      => 15,
 | |
|                         'useragent'    => 'Zend_OpenId'
 | |
|                     )
 | |
|                 );
 | |
|         } else {
 | |
|             $client->setUri($url);
 | |
|         }
 | |
| 
 | |
|         $client->resetParameters();
 | |
|         if ($method == 'POST') {
 | |
|             $client->setMethod(Zend_Http_Client::POST);
 | |
|             $client->setParameterPost($params);
 | |
|         } else {
 | |
|             $client->setMethod(Zend_Http_Client::GET);
 | |
|             $client->setParameterGet($params);
 | |
|         }
 | |
| 
 | |
|         try {
 | |
|             $response = $client->request();
 | |
|         } catch (Exception $e) {
 | |
|             $this->_setError('HTTP Request failed: ' . $e->getMessage());
 | |
|             return false;
 | |
|         }
 | |
|         $status = $response->getStatus();
 | |
|         $body = $response->getBody();
 | |
|         if ($status == 200 || ($status == 400 && !empty($body))) {
 | |
|             return $body;
 | |
|         }else{
 | |
|             $this->_setError('Bad HTTP response');
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Create (or reuse existing) association between OpenID consumer and
 | |
|      * OpenID server based on Diffie-Hellman key agreement. Returns true
 | |
|      * on success and false on failure.
 | |
|      *
 | |
|      * @param string $url OpenID server url
 | |
|      * @param float $version OpenID protocol version
 | |
|      * @param string $priv_key for testing only
 | |
|      * @return bool
 | |
|      */
 | |
|     protected function _associate($url, $version, $priv_key=null)
 | |
|     {
 | |
| 
 | |
|         /* Check if we already have association in chace or storage */
 | |
|         if ($this->_getAssociation(
 | |
|                 $url,
 | |
|                 $handle,
 | |
|                 $macFunc,
 | |
|                 $secret,
 | |
|                 $expires)) {
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         if ($this->_dumbMode) {
 | |
|             /* Use dumb mode */
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         $params = array();
 | |
| 
 | |
|         if ($version >= 2.0) {
 | |
|             $params = array(
 | |
|                 'openid.ns'           => Zend_OpenId::NS_2_0,
 | |
|                 'openid.mode'         => 'associate',
 | |
|                 'openid.assoc_type'   => 'HMAC-SHA256',
 | |
|                 'openid.session_type' => 'DH-SHA256',
 | |
|             );
 | |
|         } else {
 | |
|             $params = array(
 | |
|                 'openid.mode'         => 'associate',
 | |
|                 'openid.assoc_type'   => 'HMAC-SHA1',
 | |
|                 'openid.session_type' => 'DH-SHA1',
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         $dh = Zend_OpenId::createDhKey(pack('H*', Zend_OpenId::DH_P),
 | |
|                                        pack('H*', Zend_OpenId::DH_G),
 | |
|                                        $priv_key);
 | |
|         $dh_details = Zend_OpenId::getDhKeyDetails($dh);
 | |
| 
 | |
|         $params['openid.dh_modulus']         = base64_encode(
 | |
|             Zend_OpenId::btwoc($dh_details['p']));
 | |
|         $params['openid.dh_gen']             = base64_encode(
 | |
|             Zend_OpenId::btwoc($dh_details['g']));
 | |
|         $params['openid.dh_consumer_public'] = base64_encode(
 | |
|             Zend_OpenId::btwoc($dh_details['pub_key']));
 | |
| 
 | |
|         while(1) {
 | |
|             $ret = $this->_httpRequest($url, 'POST', $params, $status);
 | |
|             if ($ret === false) {
 | |
|                 $this->_setError("HTTP request failed");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             $r = array();
 | |
|             $bad_response = false;
 | |
|             foreach(explode("\n", $ret) as $line) {
 | |
|                 $line = trim($line);
 | |
|                 if (!empty($line)) {
 | |
|                     $x = explode(':', $line, 2);
 | |
|                     if (is_array($x) && count($x) == 2) {
 | |
|                         list($key, $value) = $x;
 | |
|                         $r[trim($key)] = trim($value);
 | |
|                     } else {
 | |
|                         $bad_response = true;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             if ($bad_response && strpos($ret, 'Unknown session type') !== false) {
 | |
|                 $r['error_code'] = 'unsupported-type';
 | |
|             }
 | |
|             $ret = $r;
 | |
| 
 | |
|             if (isset($ret['error_code']) &&
 | |
|                 $ret['error_code'] == 'unsupported-type') {
 | |
|                 if ($params['openid.session_type'] == 'DH-SHA256') {
 | |
|                     $params['openid.session_type'] = 'DH-SHA1';
 | |
|                     $params['openid.assoc_type'] = 'HMAC-SHA1';
 | |
|                 } else if ($params['openid.session_type'] == 'DH-SHA1') {
 | |
|                     $params['openid.session_type'] = 'no-encryption';
 | |
|                 } else {
 | |
|                     $this->_setError("The OpenID service responded with: " . $ret['error_code']);
 | |
|                     return false;
 | |
|                 }
 | |
|             } else {
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if ($status != 200) {
 | |
|             $this->_setError("The server responded with status code: " . $status);
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if ($version >= 2.0 &&
 | |
|             isset($ret['ns']) &&
 | |
|             $ret['ns'] != Zend_OpenId::NS_2_0) {
 | |
|             $this->_setError("Wrong namespace definition in the server response");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if (!isset($ret['assoc_handle']) ||
 | |
|             !isset($ret['expires_in']) ||
 | |
|             !isset($ret['assoc_type']) ||
 | |
|             $params['openid.assoc_type'] != $ret['assoc_type']) {
 | |
|             if ($params['openid.assoc_type'] != $ret['assoc_type']) {
 | |
|                 $this->_setError("The returned assoc_type differed from the supplied openid.assoc_type");
 | |
|             } else {
 | |
|                 $this->_setError("Missing required data from provider (assoc_handle, expires_in, assoc_type are required)");
 | |
|             }
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         $handle     = $ret['assoc_handle'];
 | |
|         $expiresIn = $ret['expires_in'];
 | |
| 
 | |
|         if ($ret['assoc_type'] == 'HMAC-SHA1') {
 | |
|             $macFunc = 'sha1';
 | |
|         } else if ($ret['assoc_type'] == 'HMAC-SHA256' &&
 | |
|             $version >= 2.0) {
 | |
|             $macFunc = 'sha256';
 | |
|         } else {
 | |
|             $this->_setError("Unsupported assoc_type");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if ((empty($ret['session_type']) ||
 | |
|              ($version >= 2.0 && $ret['session_type'] == 'no-encryption')) &&
 | |
|              isset($ret['mac_key'])) {
 | |
|             $secret = base64_decode($ret['mac_key']);
 | |
|         } else if (isset($ret['session_type']) &&
 | |
|             $ret['session_type'] == 'DH-SHA1' &&
 | |
|             !empty($ret['dh_server_public']) &&
 | |
|             !empty($ret['enc_mac_key'])) {
 | |
|             $dhFunc = 'sha1';
 | |
|         } else if (isset($ret['session_type']) &&
 | |
|             $ret['session_type'] == 'DH-SHA256' &&
 | |
|             $version >= 2.0 &&
 | |
|             !empty($ret['dh_server_public']) &&
 | |
|             !empty($ret['enc_mac_key'])) {
 | |
|             $dhFunc = 'sha256';
 | |
|         } else {
 | |
|             $this->_setError("Unsupported session_type");
 | |
|             return false;
 | |
|         }
 | |
|         if (isset($dhFunc)) {
 | |
|             $serverPub = base64_decode($ret['dh_server_public']);
 | |
|             $dhSec = Zend_OpenId::computeDhSecret($serverPub, $dh);
 | |
|             if ($dhSec === false) {
 | |
|                 $this->_setError("DH secret comutation failed");
 | |
|                 return false;
 | |
|             }
 | |
|             $sec = Zend_OpenId::digest($dhFunc, $dhSec);
 | |
|             if ($sec === false) {
 | |
|                 $this->_setError("Could not create digest");
 | |
|                 return false;
 | |
|             }
 | |
|             $secret = $sec ^ base64_decode($ret['enc_mac_key']);
 | |
|         }
 | |
|         if ($macFunc == 'sha1') {
 | |
|             if (Zend_OpenId::strlen($secret) != 20) {
 | |
|                 $this->_setError("The length of the sha1 secret must be 20");
 | |
|                 return false;
 | |
|             }
 | |
|         } else if ($macFunc == 'sha256') {
 | |
|             if (Zend_OpenId::strlen($secret) != 32) {
 | |
|                 $this->_setError("The length of the sha256 secret must be 32");
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
|         $this->_addAssociation(
 | |
|             $url,
 | |
|             $handle,
 | |
|             $macFunc,
 | |
|             $secret,
 | |
|             time() + $expiresIn);
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Performs discovery of identity and finds OpenID URL, OpenID server URL
 | |
|      * and OpenID protocol version. Returns true on succees and false on
 | |
|      * failure.
 | |
|      *
 | |
|      * @param string &$id OpenID identity URL
 | |
|      * @param string &$server OpenID server URL
 | |
|      * @param float &$version OpenID protocol version
 | |
|      * @return bool
 | |
|      * @todo OpenID 2.0 (7.3) XRI and Yadis discovery
 | |
|      */
 | |
|     protected function _discovery(&$id, &$server, &$version)
 | |
|     {
 | |
|         $realId = $id;
 | |
|         if ($this->_storage->getDiscoveryInfo(
 | |
|                 $id,
 | |
|                 $realId,
 | |
|                 $server,
 | |
|                 $version,
 | |
|                 $expire)) {
 | |
|             $id = $realId;
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         $response = $this->_httpRequest($id, 'GET', array(), $status);
 | |
|         if ($status != 200 || !is_string($response)) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         /* OpenID 2.0 (7.3) XRI and Yadis discovery */
 | |
|         if (preg_match(
 | |
|                 '/<meta[^>]*http-equiv=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?X-XRDS-Location[ \t]*[^"\']*\\1[^>]*content=(["\'])([^"\']+)\\2[^>]*\/?>/i',
 | |
|                 $response,
 | |
|                 $r)) {
 | |
|             $XRDS = $r[3];
 | |
|             $version = 2.0;
 | |
|             $response = $this->_httpRequest($XRDS); 
 | |
|             if (preg_match(
 | |
|                     '/<URI>([^\t]*)<\/URI>/i',
 | |
|                     $response,
 | |
|                     $x)) {
 | |
|                 $server = $x[1];
 | |
|                 // $realId 
 | |
|                 $realId = 'http://specs.openid.net/auth/2.0/identifier_select';
 | |
|             }
 | |
|             else {
 | |
|                 $this->_setError("Unable to get URI for XRDS discovery");
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /* HTML-based discovery */
 | |
|         else if (preg_match(
 | |
|                 '/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.provider[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
 | |
|                 $response,
 | |
|                 $r)) {
 | |
|             $version = 2.0;
 | |
|             $server = $r[3];
 | |
|         } else if (preg_match(
 | |
|                 '/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.provider[ \t]*[^"\']*\\3[^>]*\/?>/i',
 | |
|                 $response,
 | |
|                 $r)) {
 | |
|             $version = 2.0;
 | |
|             $server = $r[2];
 | |
|         } else if (preg_match(
 | |
|                 '/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.server[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
 | |
|                 $response,
 | |
|                 $r)) {
 | |
|             $version = 1.1;
 | |
|             $server = $r[3];
 | |
|         } else if (preg_match(
 | |
|                 '/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.server[ \t]*[^"\']*\\3[^>]*\/?>/i',
 | |
|                 $response,
 | |
|                 $r)) {
 | |
|             $version = 1.1;
 | |
|             $server = $r[2];
 | |
|         } else {
 | |
|             return false;
 | |
|         }
 | |
|         if ($version >= 2.0) {
 | |
|             if (preg_match(
 | |
|                     '/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.local_id[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
 | |
|                     $response,
 | |
|                     $r)) {
 | |
|                 $realId = $r[3];
 | |
|             } else if (preg_match(
 | |
|                     '/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.local_id[ \t]*[^"\']*\\3[^>]*\/?>/i',
 | |
|                     $response,
 | |
|                     $r)) {
 | |
|                 $realId = $r[2];
 | |
|             }
 | |
|         } else {
 | |
|             if (preg_match(
 | |
|                     '/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.delegate[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
 | |
|                     $response,
 | |
|                     $r)) {
 | |
|                 $realId = $r[3];
 | |
|             } else if (preg_match(
 | |
|                     '/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.delegate[ \t]*[^"\']*\\3[^>]*\/?>/i',
 | |
|                     $response,
 | |
|                     $r)) {
 | |
|                 $realId = $r[2];
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         $expire = time() + 60 * 60;
 | |
|         $this->_storage->addDiscoveryInfo($id, $realId, $server, $version, $expire);
 | |
|         $id = $realId;
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Performs check of OpenID identity.
 | |
|      *
 | |
|      * This is the first step of OpenID authentication process.
 | |
|      * On success the function does not return (it does HTTP redirection to
 | |
|      * server and exits). On failure it returns false.
 | |
|      *
 | |
|      * @param bool $immediate enables or disables interaction with user
 | |
|      * @param string $id OpenID identity
 | |
|      * @param string $returnTo HTTP URL to redirect response from server to
 | |
|      * @param string $root HTTP URL to identify consumer on server
 | |
|      * @param mixed $extensions extension object or array of extensions objects
 | |
|      * @param Zend_Controller_Response_Abstract $response an optional response
 | |
|      *  object to perform HTTP or HTML form redirection
 | |
|      * @return bool
 | |
|      */
 | |
|     protected function _checkId($immediate, $id, $returnTo=null, $root=null,
 | |
|         $extensions=null, Zend_Controller_Response_Abstract $response = null)
 | |
|     {
 | |
|         $this->_setError('');
 | |
| 
 | |
|         if (!Zend_OpenId::normalize($id)) {
 | |
|             $this->_setError("Normalisation failed");
 | |
|             return false;
 | |
|         }
 | |
|         $claimedId = $id;
 | |
| 
 | |
|         if (!$this->_discovery($id, $server, $version)) {
 | |
|             $this->_setError("Discovery failed: " . $this->getError());
 | |
|             return false;
 | |
|         }
 | |
|         if (!$this->_associate($server, $version)) {
 | |
|             $this->_setError("Association failed: " . $this->getError());
 | |
|             return false;
 | |
|         }
 | |
|         if (!$this->_getAssociation(
 | |
|                 $server,
 | |
|                 $handle,
 | |
|                 $macFunc,
 | |
|                 $secret,
 | |
|                 $expires)) {
 | |
|             /* Use dumb mode */
 | |
|             unset($handle);
 | |
|             unset($macFunc);
 | |
|             unset($secret);
 | |
|             unset($expires);
 | |
|         }
 | |
| 
 | |
|         $params = array();
 | |
|         if ($version >= 2.0) {
 | |
|             $params['openid.ns'] = Zend_OpenId::NS_2_0;
 | |
|         }
 | |
| 
 | |
|         $params['openid.mode'] = $immediate ?
 | |
|             'checkid_immediate' : 'checkid_setup';
 | |
| 
 | |
|         $params['openid.identity'] = $id;
 | |
| 
 | |
|         $params['openid.claimed_id'] = $claimedId;
 | |
| 
 | |
|         if ($version <= 2.0) {
 | |
|             if ($this->_session !== null) {
 | |
|                 $this->_session->identity = $id;
 | |
|                 $this->_session->claimed_id = $claimedId;
 | |
|             } else if (defined('SID')) {
 | |
|                 $_SESSION["zend_openid"] = array(
 | |
|                     "identity" => $id,
 | |
|                     "claimed_id" => $claimedId);
 | |
|             } else {
 | |
|                 $this->_session = new Zend_Session_Namespace("zend_openid");
 | |
|                 $this->_session->identity = $id;
 | |
|                 $this->_session->claimed_id = $claimedId;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (isset($handle)) {
 | |
|             $params['openid.assoc_handle'] = $handle;
 | |
|         }
 | |
| 
 | |
|         $params['openid.return_to'] = Zend_OpenId::absoluteUrl($returnTo);
 | |
| 
 | |
|         if (empty($root)) {
 | |
|             $root = Zend_OpenId::selfUrl();
 | |
|             if ($root[strlen($root)-1] != '/') {
 | |
|                 $root = dirname($root);
 | |
|             }
 | |
|         }
 | |
|         if ($version >= 2.0) {
 | |
|             $params['openid.realm'] = $root;
 | |
|         } else {
 | |
|             $params['openid.trust_root'] = $root;
 | |
|         }
 | |
| 
 | |
|         if (!Zend_OpenId_Extension::forAll($extensions, 'prepareRequest', $params)) {
 | |
|             $this->_setError("Extension::prepareRequest failure");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         Zend_OpenId::redirect($server, $params, $response);
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets HTTP client object to make HTTP requests
 | |
|      *
 | |
|      * @param Zend_Http_Client $client HTTP client object to be used
 | |
|      */
 | |
|     public function setHttpClient($client) {
 | |
|         $this->_httpClient = $client;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns HTTP client object that will be used to make HTTP requests
 | |
|      *
 | |
|      * @return Zend_Http_Client
 | |
|      */
 | |
|     public function getHttpClient() {
 | |
|         return $this->_httpClient;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Sets session object to store climed_id
 | |
|      *
 | |
|      * @param Zend_Session_Namespace $session HTTP client object to be used
 | |
|      */
 | |
|     public function setSession(Zend_Session_Namespace $session) {
 | |
|         $this->_session = $session;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns session object that is used to store climed_id
 | |
|      *
 | |
|      * @return Zend_Session_Namespace
 | |
|      */
 | |
|     public function getSession() {
 | |
|         return $this->_session;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Saves error message
 | |
|      *
 | |
|      * @param string $message error message
 | |
|      */
 | |
|     protected function _setError($message)
 | |
|     {
 | |
|         $this->_error = $message;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Returns error message that explains failure of login, check or verify
 | |
|      *
 | |
|      * @return string
 | |
|      */
 | |
|     public function getError()
 | |
|     {
 | |
|         return $this->_error;
 | |
|     }
 | |
| 
 | |
| }
 |