File indexing completed on 2024-12-22 05:36:53
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_Provider 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$ 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 * OpenID provider (server) implementation 0036 * 0037 * @category Zend 0038 * @package Zend_OpenId 0039 * @subpackage Zend_OpenId_Provider 0040 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0041 * @license http://framework.zend.com/license/new-bsd New BSD License 0042 */ 0043 class Zend_OpenId_Provider 0044 { 0045 0046 /** 0047 * Reference to an implementation of storage object 0048 * 0049 * @var Zend_OpenId_Provider_Storage $_storage 0050 */ 0051 private $_storage; 0052 0053 /** 0054 * Reference to an implementation of user object 0055 * 0056 * @var Zend_OpenId_Provider_User $_user 0057 */ 0058 private $_user; 0059 0060 /** 0061 * Time to live of association session in secconds 0062 * 0063 * @var integer $_sessionTtl 0064 */ 0065 private $_sessionTtl; 0066 0067 /** 0068 * URL to peform interactive user login 0069 * 0070 * @var string $_loginUrl 0071 */ 0072 private $_loginUrl; 0073 0074 /** 0075 * URL to peform interactive validation of consumer by user 0076 * 0077 * @var string $_trustUrl 0078 */ 0079 private $_trustUrl; 0080 0081 /** 0082 * The OP Endpoint URL 0083 * 0084 * @var string $_opEndpoint 0085 */ 0086 private $_opEndpoint; 0087 0088 /** 0089 * Constructs a Zend_OpenId_Provider object with given parameters. 0090 * 0091 * @param string $loginUrl is an URL that provides login screen for 0092 * end-user (by default it is the same URL with additional GET variable 0093 * openid.action=login) 0094 * @param string $trustUrl is an URL that shows a question if end-user 0095 * trust to given consumer (by default it is the same URL with additional 0096 * GET variable openid.action=trust) 0097 * @param Zend_OpenId_Provider_User $user is an object for communication 0098 * with User-Agent and store information about logged-in user (it is a 0099 * Zend_OpenId_Provider_User_Session object by default) 0100 * @param Zend_OpenId_Provider_Storage $storage is an object for keeping 0101 * persistent database (it is a Zend_OpenId_Provider_Storage_File object 0102 * by default) 0103 * @param integer $sessionTtl is a default time to live for association 0104 * session in seconds (1 hour by default). Consumer must reestablish 0105 * association after that time. 0106 */ 0107 public function __construct($loginUrl = null, 0108 $trustUrl = null, 0109 Zend_OpenId_Provider_User $user = null, 0110 Zend_OpenId_Provider_Storage $storage = null, 0111 $sessionTtl = 3600) 0112 { 0113 if ($loginUrl === null) { 0114 $loginUrl = Zend_OpenId::selfUrl() . '?openid.action=login'; 0115 } else { 0116 $loginUrl = Zend_OpenId::absoluteUrl($loginUrl); 0117 } 0118 $this->_loginUrl = $loginUrl; 0119 if ($trustUrl === null) { 0120 $trustUrl = Zend_OpenId::selfUrl() . '?openid.action=trust'; 0121 } else { 0122 $trustUrl = Zend_OpenId::absoluteUrl($trustUrl); 0123 } 0124 $this->_trustUrl = $trustUrl; 0125 if ($user === null) { 0126 // require_once "Zend/OpenId/Provider/User/Session.php"; 0127 $this->_user = new Zend_OpenId_Provider_User_Session(); 0128 } else { 0129 $this->_user = $user; 0130 } 0131 if ($storage === null) { 0132 // require_once "Zend/OpenId/Provider/Storage/File.php"; 0133 $this->_storage = new Zend_OpenId_Provider_Storage_File(); 0134 } else { 0135 $this->_storage = $storage; 0136 } 0137 $this->_sessionTtl = $sessionTtl; 0138 } 0139 0140 /** 0141 * Sets the OP Endpoint URL 0142 * 0143 * @param string $url the OP Endpoint URL 0144 * @return null 0145 */ 0146 public function setOpEndpoint($url) 0147 { 0148 $this->_opEndpoint = $url; 0149 } 0150 0151 /** 0152 * Registers a new user with given $id and $password 0153 * Returns true in case of success and false if user with given $id already 0154 * exists 0155 * 0156 * @param string $id user identity URL 0157 * @param string $password encoded user password 0158 * @return bool 0159 */ 0160 public function register($id, $password) 0161 { 0162 if (!Zend_OpenId::normalize($id) || empty($id)) { 0163 return false; 0164 } 0165 return $this->_storage->addUser($id, md5($id.$password)); 0166 } 0167 0168 /** 0169 * Returns true if user with given $id exists and false otherwise 0170 * 0171 * @param string $id user identity URL 0172 * @return bool 0173 */ 0174 public function hasUser($id) { 0175 if (!Zend_OpenId::normalize($id)) { 0176 return false; 0177 } 0178 return $this->_storage->hasUser($id); 0179 } 0180 0181 /** 0182 * Performs login of user with given $id and $password 0183 * Returns true in case of success and false otherwise 0184 * 0185 * @param string $id user identity URL 0186 * @param string $password user password 0187 * @return bool 0188 */ 0189 public function login($id, $password) 0190 { 0191 if (!Zend_OpenId::normalize($id)) { 0192 return false; 0193 } 0194 if (!$this->_storage->checkUser($id, md5($id.$password))) { 0195 return false; 0196 } 0197 $this->_user->setLoggedInUser($id); 0198 return true; 0199 } 0200 0201 /** 0202 * Performs logout. Clears information about logged in user. 0203 * 0204 * @return void 0205 */ 0206 public function logout() 0207 { 0208 $this->_user->delLoggedInUser(); 0209 return true; 0210 } 0211 0212 /** 0213 * Returns identity URL of current logged in user or false 0214 * 0215 * @return mixed 0216 */ 0217 public function getLoggedInUser() { 0218 return $this->_user->getLoggedInUser(); 0219 } 0220 0221 /** 0222 * Retrieve consumer's root URL from request query. 0223 * Returns URL or false in case of failure 0224 * 0225 * @param array $params query arguments 0226 * @return mixed 0227 */ 0228 public function getSiteRoot($params) 0229 { 0230 $version = 1.1; 0231 if (isset($params['openid_ns']) && 0232 $params['openid_ns'] == Zend_OpenId::NS_2_0) { 0233 $version = 2.0; 0234 } 0235 if ($version >= 2.0 && isset($params['openid_realm'])) { 0236 $root = $params['openid_realm']; 0237 } else if ($version < 2.0 && isset($params['openid_trust_root'])) { 0238 $root = $params['openid_trust_root']; 0239 } else if (isset($params['openid_return_to'])) { 0240 $root = $params['openid_return_to']; 0241 } else { 0242 return false; 0243 } 0244 if (Zend_OpenId::normalizeUrl($root) && !empty($root)) { 0245 return $root; 0246 } 0247 return false; 0248 } 0249 0250 /** 0251 * Allows consumer with given root URL to authenticate current logged 0252 * in user. Returns true on success and false on error. 0253 * 0254 * @param string $root root URL 0255 * @param mixed $extensions extension object or array of extensions objects 0256 * @return bool 0257 */ 0258 public function allowSite($root, $extensions=null) 0259 { 0260 $id = $this->getLoggedInUser(); 0261 if ($id === false) { 0262 return false; 0263 } 0264 if ($extensions !== null) { 0265 $data = array(); 0266 Zend_OpenId_Extension::forAll($extensions, 'getTrustData', $data); 0267 } else { 0268 $data = true; 0269 } 0270 $this->_storage->addSite($id, $root, $data); 0271 return true; 0272 } 0273 0274 /** 0275 * Prohibit consumer with given root URL to authenticate current logged 0276 * in user. Returns true on success and false on error. 0277 * 0278 * @param string $root root URL 0279 * @return bool 0280 */ 0281 public function denySite($root) 0282 { 0283 $id = $this->getLoggedInUser(); 0284 if ($id === false) { 0285 return false; 0286 } 0287 $this->_storage->addSite($id, $root, false); 0288 return true; 0289 } 0290 0291 /** 0292 * Delete consumer with given root URL from known sites of current logged 0293 * in user. Next time this consumer will try to authenticate the user, 0294 * Provider will ask user's confirmation. 0295 * Returns true on success and false on error. 0296 * 0297 * @param string $root root URL 0298 * @return bool 0299 */ 0300 public function delSite($root) 0301 { 0302 $id = $this->getLoggedInUser(); 0303 if ($id === false) { 0304 return false; 0305 } 0306 $this->_storage->addSite($id, $root, null); 0307 return true; 0308 } 0309 0310 /** 0311 * Returns list of known consumers for current logged in user or false 0312 * if he is not logged in. 0313 * 0314 * @return mixed 0315 */ 0316 public function getTrustedSites() 0317 { 0318 $id = $this->getLoggedInUser(); 0319 if ($id === false) { 0320 return false; 0321 } 0322 return $this->_storage->getTrustedSites($id); 0323 } 0324 0325 /** 0326 * Handles HTTP request from consumer 0327 * 0328 * @param array $params GET or POST variables. If this parameter is omited 0329 * or set to null, then $_GET or $_POST superglobal variable is used 0330 * according to REQUEST_METHOD. 0331 * @param mixed $extensions extension object or array of extensions objects 0332 * @param Zend_Controller_Response_Abstract $response an optional response 0333 * object to perform HTTP or HTML form redirection 0334 * @return mixed 0335 */ 0336 public function handle($params=null, $extensions=null, 0337 Zend_Controller_Response_Abstract $response = null) 0338 { 0339 if ($params === null) { 0340 if ($_SERVER["REQUEST_METHOD"] == "GET") { 0341 $params = $_GET; 0342 } else if ($_SERVER["REQUEST_METHOD"] == "POST") { 0343 $params = $_POST; 0344 } else { 0345 return false; 0346 } 0347 } 0348 $version = 1.1; 0349 if (isset($params['openid_ns']) && 0350 $params['openid_ns'] == Zend_OpenId::NS_2_0) { 0351 $version = 2.0; 0352 } 0353 if (isset($params['openid_mode'])) { 0354 if ($params['openid_mode'] == 'associate') { 0355 $response = $this->_associate($version, $params); 0356 $ret = ''; 0357 foreach ($response as $key => $val) { 0358 $ret .= $key . ':' . $val . "\n"; 0359 } 0360 return $ret; 0361 } else if ($params['openid_mode'] == 'checkid_immediate') { 0362 $ret = $this->_checkId($version, $params, 1, $extensions, $response); 0363 if (is_bool($ret)) return $ret; 0364 if (!empty($params['openid_return_to'])) { 0365 Zend_OpenId::redirect($params['openid_return_to'], $ret, $response); 0366 } 0367 return true; 0368 } else if ($params['openid_mode'] == 'checkid_setup') { 0369 $ret = $this->_checkId($version, $params, 0, $extensions, $response); 0370 if (is_bool($ret)) return $ret; 0371 if (!empty($params['openid_return_to'])) { 0372 Zend_OpenId::redirect($params['openid_return_to'], $ret, $response); 0373 } 0374 return true; 0375 } else if ($params['openid_mode'] == 'check_authentication') { 0376 $response = $this->_checkAuthentication($version, $params); 0377 $ret = ''; 0378 foreach ($response as $key => $val) { 0379 $ret .= $key . ':' . $val . "\n"; 0380 } 0381 return $ret; 0382 } 0383 } 0384 return false; 0385 } 0386 0387 /** 0388 * Generates a secret key for given hash function, returns RAW key or false 0389 * if function is not supported 0390 * 0391 * @param string $func hash function (sha1 or sha256) 0392 * @return mixed 0393 */ 0394 protected function _genSecret($func) 0395 { 0396 if ($func == 'sha1') { 0397 $macLen = 20; /* 160 bit */ 0398 } else if ($func == 'sha256') { 0399 $macLen = 32; /* 256 bit */ 0400 } else { 0401 return false; 0402 } 0403 return Zend_OpenId::randomBytes($macLen); 0404 } 0405 0406 /** 0407 * Processes association request from OpenID consumerm generates secret 0408 * shared key and send it back using Diffie-Hellman encruption. 0409 * Returns array of variables to push back to consumer. 0410 * 0411 * @param float $version OpenID version 0412 * @param array $params GET or POST request variables 0413 * @return array 0414 */ 0415 protected function _associate($version, $params) 0416 { 0417 $ret = array(); 0418 0419 if ($version >= 2.0) { 0420 $ret['ns'] = Zend_OpenId::NS_2_0; 0421 } 0422 0423 if (isset($params['openid_assoc_type']) && 0424 $params['openid_assoc_type'] == 'HMAC-SHA1') { 0425 $macFunc = 'sha1'; 0426 } else if (isset($params['openid_assoc_type']) && 0427 $params['openid_assoc_type'] == 'HMAC-SHA256' && 0428 $version >= 2.0) { 0429 $macFunc = 'sha256'; 0430 } else { 0431 $ret['error'] = 'Wrong "openid.assoc_type"'; 0432 $ret['error-code'] = 'unsupported-type'; 0433 return $ret; 0434 } 0435 0436 $ret['assoc_type'] = $params['openid_assoc_type']; 0437 0438 $secret = $this->_genSecret($macFunc); 0439 0440 if (empty($params['openid_session_type']) || 0441 $params['openid_session_type'] == 'no-encryption') { 0442 $ret['mac_key'] = base64_encode($secret); 0443 } else if (isset($params['openid_session_type']) && 0444 $params['openid_session_type'] == 'DH-SHA1') { 0445 $dhFunc = 'sha1'; 0446 } else if (isset($params['openid_session_type']) && 0447 $params['openid_session_type'] == 'DH-SHA256' && 0448 $version >= 2.0) { 0449 $dhFunc = 'sha256'; 0450 } else { 0451 $ret['error'] = 'Wrong "openid.session_type"'; 0452 $ret['error-code'] = 'unsupported-type'; 0453 return $ret; 0454 } 0455 0456 if (isset($params['openid_session_type'])) { 0457 $ret['session_type'] = $params['openid_session_type']; 0458 } 0459 0460 if (isset($dhFunc)) { 0461 if (empty($params['openid_dh_consumer_public'])) { 0462 $ret['error'] = 'Wrong "openid.dh_consumer_public"'; 0463 return $ret; 0464 } 0465 if (empty($params['openid_dh_gen'])) { 0466 $g = pack('H*', Zend_OpenId::DH_G); 0467 } else { 0468 $g = base64_decode($params['openid_dh_gen']); 0469 } 0470 if (empty($params['openid_dh_modulus'])) { 0471 $p = pack('H*', Zend_OpenId::DH_P); 0472 } else { 0473 $p = base64_decode($params['openid_dh_modulus']); 0474 } 0475 0476 $dh = Zend_OpenId::createDhKey($p, $g); 0477 $dh_details = Zend_OpenId::getDhKeyDetails($dh); 0478 0479 $sec = Zend_OpenId::computeDhSecret( 0480 base64_decode($params['openid_dh_consumer_public']), $dh); 0481 if ($sec === false) { 0482 $ret['error'] = 'Wrong "openid.session_type"'; 0483 $ret['error-code'] = 'unsupported-type'; 0484 return $ret; 0485 } 0486 $sec = Zend_OpenId::digest($dhFunc, $sec); 0487 $ret['dh_server_public'] = base64_encode( 0488 Zend_OpenId::btwoc($dh_details['pub_key'])); 0489 $ret['enc_mac_key'] = base64_encode($secret ^ $sec); 0490 } 0491 0492 $handle = uniqid(); 0493 $expiresIn = $this->_sessionTtl; 0494 0495 $ret['assoc_handle'] = $handle; 0496 $ret['expires_in'] = $expiresIn; 0497 0498 $this->_storage->addAssociation($handle, 0499 $macFunc, $secret, time() + $expiresIn); 0500 0501 return $ret; 0502 } 0503 0504 /** 0505 * Performs authentication (or authentication check). 0506 * 0507 * @param float $version OpenID version 0508 * @param array $params GET or POST request variables 0509 * @param bool $immediate enables or disables interaction with user 0510 * @param mixed $extensions extension object or array of extensions objects 0511 * @param Zend_Controller_Response_Abstract $response 0512 * @return array 0513 */ 0514 protected function _checkId($version, $params, $immediate, $extensions=null, 0515 Zend_Controller_Response_Abstract $response = null) 0516 { 0517 $ret = array(); 0518 0519 if ($version >= 2.0) { 0520 $ret['openid.ns'] = Zend_OpenId::NS_2_0; 0521 } 0522 $root = $this->getSiteRoot($params); 0523 if ($root === false) { 0524 return false; 0525 } 0526 0527 if (isset($params['openid_identity']) && 0528 !$this->_storage->hasUser($params['openid_identity'])) { 0529 $ret['openid.mode'] = ($immediate && $version >= 2.0) ? 'setup_needed': 'cancel'; 0530 return $ret; 0531 } 0532 0533 /* Check if user already logged in into the server */ 0534 if (!isset($params['openid_identity']) || 0535 $this->_user->getLoggedInUser() !== $params['openid_identity']) { 0536 $params2 = array(); 0537 foreach ($params as $key => $val) { 0538 if (strpos($key, 'openid_ns_') === 0) { 0539 $key = 'openid.ns.' . substr($key, strlen('openid_ns_')); 0540 } else if (strpos($key, 'openid_sreg_') === 0) { 0541 $key = 'openid.sreg.' . substr($key, strlen('openid_sreg_')); 0542 } else if (strpos($key, 'openid_') === 0) { 0543 $key = 'openid.' . substr($key, strlen('openid_')); 0544 } 0545 $params2[$key] = $val; 0546 } 0547 if ($immediate) { 0548 $params2['openid.mode'] = 'checkid_setup'; 0549 $ret['openid.mode'] = ($version >= 2.0) ? 'setup_needed': 'id_res'; 0550 $ret['openid.user_setup_url'] = $this->_loginUrl 0551 . (strpos($this->_loginUrl, '?') === false ? '?' : '&') 0552 . Zend_OpenId::paramsToQuery($params2); 0553 return $ret; 0554 } else { 0555 /* Redirect to Server Login Screen */ 0556 Zend_OpenId::redirect($this->_loginUrl, $params2, $response); 0557 return true; 0558 } 0559 } 0560 0561 if (!Zend_OpenId_Extension::forAll($extensions, 'parseRequest', $params)) { 0562 $ret['openid.mode'] = ($immediate && $version >= 2.0) ? 'setup_needed': 'cancel'; 0563 return $ret; 0564 } 0565 0566 /* Check if user trusts to the consumer */ 0567 $trusted = null; 0568 $sites = $this->_storage->getTrustedSites($params['openid_identity']); 0569 if (isset($params['openid_return_to'])) { 0570 $root = $params['openid_return_to']; 0571 } 0572 if (isset($sites[$root])) { 0573 $trusted = $sites[$root]; 0574 } else { 0575 foreach ($sites as $site => $t) { 0576 if (strpos($root, $site) === 0) { 0577 $trusted = $t; 0578 break; 0579 } else { 0580 /* OpenID 2.0 (9.2) check for realm wild-card matching */ 0581 $n = strpos($site, '://*.'); 0582 if ($n != false) { 0583 $regex = '/^' 0584 . preg_quote(substr($site, 0, $n+3), '/') 0585 . '[A-Za-z1-9_\.]+?' 0586 . preg_quote(substr($site, $n+4), '/') 0587 . '/'; 0588 if (preg_match($regex, $root)) { 0589 $trusted = $t; 0590 break; 0591 } 0592 } 0593 } 0594 } 0595 } 0596 0597 if (is_array($trusted)) { 0598 if (!Zend_OpenId_Extension::forAll($extensions, 'checkTrustData', $trusted)) { 0599 $trusted = null; 0600 } 0601 } 0602 0603 if ($trusted === false) { 0604 $ret['openid.mode'] = 'cancel'; 0605 return $ret; 0606 } else if ($trusted === null) { 0607 /* Redirect to Server Trust Screen */ 0608 $params2 = array(); 0609 foreach ($params as $key => $val) { 0610 if (strpos($key, 'openid_ns_') === 0) { 0611 $key = 'openid.ns.' . substr($key, strlen('openid_ns_')); 0612 } else if (strpos($key, 'openid_sreg_') === 0) { 0613 $key = 'openid.sreg.' . substr($key, strlen('openid_sreg_')); 0614 } else if (strpos($key, 'openid_') === 0) { 0615 $key = 'openid.' . substr($key, strlen('openid_')); 0616 } 0617 $params2[$key] = $val; 0618 } 0619 if ($immediate) { 0620 $params2['openid.mode'] = 'checkid_setup'; 0621 $ret['openid.mode'] = ($version >= 2.0) ? 'setup_needed': 'id_res'; 0622 $ret['openid.user_setup_url'] = $this->_trustUrl 0623 . (strpos($this->_trustUrl, '?') === false ? '?' : '&') 0624 . Zend_OpenId::paramsToQuery($params2); 0625 return $ret; 0626 } else { 0627 Zend_OpenId::redirect($this->_trustUrl, $params2, $response); 0628 return true; 0629 } 0630 } 0631 0632 return $this->_respond($version, $ret, $params, $extensions); 0633 } 0634 0635 /** 0636 * Perepares information to send back to consumer's authentication request, 0637 * signs it using shared secret and send back through HTTP redirection 0638 * 0639 * @param array $params GET or POST request variables 0640 * @param mixed $extensions extension object or array of extensions objects 0641 * @param Zend_Controller_Response_Abstract $response an optional response 0642 * object to perform HTTP or HTML form redirection 0643 * @return bool 0644 */ 0645 public function respondToConsumer($params, $extensions=null, 0646 Zend_Controller_Response_Abstract $response = null) 0647 { 0648 $version = 1.1; 0649 if (isset($params['openid_ns']) && 0650 $params['openid_ns'] == Zend_OpenId::NS_2_0) { 0651 $version = 2.0; 0652 } 0653 $ret = array(); 0654 if ($version >= 2.0) { 0655 $ret['openid.ns'] = Zend_OpenId::NS_2_0; 0656 } 0657 $ret = $this->_respond($version, $ret, $params, $extensions); 0658 if (!empty($params['openid_return_to'])) { 0659 Zend_OpenId::redirect($params['openid_return_to'], $ret, $response); 0660 } 0661 return true; 0662 } 0663 0664 /** 0665 * Perepares information to send back to consumer's authentication request 0666 * and signs it using shared secret. 0667 * 0668 * @param float $version OpenID protcol version 0669 * @param array $ret arguments to be send back to consumer 0670 * @param array $params GET or POST request variables 0671 * @param mixed $extensions extension object or array of extensions objects 0672 * @return array 0673 */ 0674 protected function _respond($version, $ret, $params, $extensions=null) 0675 { 0676 if (empty($params['openid_assoc_handle']) || 0677 !$this->_storage->getAssociation($params['openid_assoc_handle'], 0678 $macFunc, $secret, $expires)) { 0679 /* Use dumb mode */ 0680 if (!empty($params['openid_assoc_handle'])) { 0681 $ret['openid.invalidate_handle'] = $params['openid_assoc_handle']; 0682 } 0683 $macFunc = $version >= 2.0 ? 'sha256' : 'sha1'; 0684 $secret = $this->_genSecret($macFunc); 0685 $handle = uniqid(); 0686 $expiresIn = $this->_sessionTtl; 0687 $this->_storage->addAssociation($handle, 0688 $macFunc, $secret, time() + $expiresIn); 0689 $ret['openid.assoc_handle'] = $handle; 0690 } else { 0691 $ret['openid.assoc_handle'] = $params['openid_assoc_handle']; 0692 } 0693 if (isset($params['openid_return_to'])) { 0694 $ret['openid.return_to'] = $params['openid_return_to']; 0695 } 0696 if (isset($params['openid_claimed_id'])) { 0697 $ret['openid.claimed_id'] = $params['openid_claimed_id']; 0698 } 0699 if (isset($params['openid_identity'])) { 0700 $ret['openid.identity'] = $params['openid_identity']; 0701 } 0702 0703 if ($version >= 2.0) { 0704 if (!empty($this->_opEndpoint)) { 0705 $ret['openid.op_endpoint'] = $this->_opEndpoint; 0706 } else { 0707 $ret['openid.op_endpoint'] = Zend_OpenId::selfUrl(); 0708 } 0709 } 0710 $ret['openid.response_nonce'] = gmdate('Y-m-d\TH:i:s\Z') . uniqid(); 0711 $ret['openid.mode'] = 'id_res'; 0712 0713 Zend_OpenId_Extension::forAll($extensions, 'prepareResponse', $ret); 0714 0715 $signed = ''; 0716 $data = ''; 0717 foreach ($ret as $key => $val) { 0718 if (strpos($key, 'openid.') === 0) { 0719 $key = substr($key, strlen('openid.')); 0720 if (!empty($signed)) { 0721 $signed .= ','; 0722 } 0723 $signed .= $key; 0724 $data .= $key . ':' . $val . "\n"; 0725 } 0726 } 0727 $signed .= ',signed'; 0728 $data .= 'signed:' . $signed . "\n"; 0729 $ret['openid.signed'] = $signed; 0730 0731 $ret['openid.sig'] = base64_encode( 0732 Zend_OpenId::hashHmac($macFunc, $data, $secret)); 0733 0734 return $ret; 0735 } 0736 0737 /** 0738 * Performs authentication validation for dumb consumers 0739 * Returns array of variables to push back to consumer. 0740 * It MUST contain 'is_valid' variable with value 'true' or 'false'. 0741 * 0742 * @param float $version OpenID version 0743 * @param array $params GET or POST request variables 0744 * @return array 0745 */ 0746 protected function _checkAuthentication($version, $params) 0747 { 0748 $ret = array(); 0749 if ($version >= 2.0) { 0750 $ret['ns'] = Zend_OpenId::NS_2_0; 0751 } 0752 $ret['openid.mode'] = 'id_res'; 0753 0754 if (empty($params['openid_assoc_handle']) || 0755 empty($params['openid_signed']) || 0756 empty($params['openid_sig']) || 0757 !$this->_storage->getAssociation($params['openid_assoc_handle'], 0758 $macFunc, $secret, $expires)) { 0759 $ret['is_valid'] = 'false'; 0760 return $ret; 0761 } 0762 0763 $signed = explode(',', $params['openid_signed']); 0764 $data = ''; 0765 foreach ($signed as $key) { 0766 $data .= $key . ':'; 0767 if ($key == 'mode') { 0768 $data .= "id_res\n"; 0769 } else { 0770 $data .= $params['openid_' . strtr($key,'.','_')]."\n"; 0771 } 0772 } 0773 if ($this->_secureStringCompare(base64_decode($params['openid_sig']), 0774 Zend_OpenId::hashHmac($macFunc, $data, $secret))) { 0775 $ret['is_valid'] = 'true'; 0776 } else { 0777 $ret['is_valid'] = 'false'; 0778 } 0779 return $ret; 0780 } 0781 0782 /** 0783 * Securely compare two strings for equality while avoided C level memcmp() 0784 * optimisations capable of leaking timing information useful to an attacker 0785 * attempting to iteratively guess the unknown string (e.g. password) being 0786 * compared against. 0787 * 0788 * @param string $a 0789 * @param string $b 0790 * @return bool 0791 */ 0792 protected function _secureStringCompare($a, $b) 0793 { 0794 if (strlen($a) !== strlen($b)) { 0795 return false; 0796 } 0797 $result = 0; 0798 for ($i = 0; $i < strlen($a); $i++) { 0799 $result |= ord($a[$i]) ^ ord($b[$i]); 0800 } 0801 return $result == 0; 0802 } 0803 }