File indexing completed on 2025-01-19 05:21:20
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 }