<?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_Auth * @subpackage Adapter * @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$ */ /** * @see Zend_Auth_Adapter_Interface */ require_once 'Zend/Auth/Adapter/Interface.php'; /** * @category Zend * @package Zend_Auth * @subpackage Adapter * @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_Auth_Adapter_Digest implements Zend_Auth_Adapter_Interface { /** * Filename against which authentication queries are performed * * @var string */ protected $_filename; /** * Digest authentication realm * * @var string */ protected $_realm; /** * Digest authentication user * * @var string */ protected $_username; /** * Password for the user of the realm * * @var string */ protected $_password; /** * Sets adapter options * * @param mixed $filename * @param mixed $realm * @param mixed $username * @param mixed $password * @return void */ public function __construct($filename = null, $realm = null, $username = null, $password = null) { $options = array('filename', 'realm', 'username', 'password'); foreach ($options as $option) { if (null !== $$option) { $methodName = 'set' . ucfirst($option); $this->$methodName($$option); } } } /** * Returns the filename option value or null if it has not yet been set * * @return string|null */ public function getFilename() { return $this->_filename; } /** * Sets the filename option value * * @param mixed $filename * @return Zend_Auth_Adapter_Digest Provides a fluent interface */ public function setFilename($filename) { $this->_filename = (string) $filename; return $this; } /** * Returns the realm option value or null if it has not yet been set * * @return string|null */ public function getRealm() { return $this->_realm; } /** * Sets the realm option value * * @param mixed $realm * @return Zend_Auth_Adapter_Digest Provides a fluent interface */ public function setRealm($realm) { $this->_realm = (string) $realm; return $this; } /** * Returns the username option value or null if it has not yet been set * * @return string|null */ public function getUsername() { return $this->_username; } /** * Sets the username option value * * @param mixed $username * @return Zend_Auth_Adapter_Digest Provides a fluent interface */ public function setUsername($username) { $this->_username = (string) $username; return $this; } /** * Returns the password option value or null if it has not yet been set * * @return string|null */ public function getPassword() { return $this->_password; } /** * Sets the password option value * * @param mixed $password * @return Zend_Auth_Adapter_Digest Provides a fluent interface */ public function setPassword($password) { $this->_password = (string) $password; return $this; } /** * Defined by Zend_Auth_Adapter_Interface * * @throws Zend_Auth_Adapter_Exception * @return Zend_Auth_Result */ public function authenticate() { $optionsRequired = array('filename', 'realm', 'username', 'password'); foreach ($optionsRequired as $optionRequired) { if (null === $this->{"_$optionRequired"}) { /** * @see Zend_Auth_Adapter_Exception */ require_once 'Zend/Auth/Adapter/Exception.php'; throw new Zend_Auth_Adapter_Exception("Option '$optionRequired' must be set before authentication"); } } if (false === ($fileHandle = @fopen($this->_filename, 'r'))) { /** * @see Zend_Auth_Adapter_Exception */ require_once 'Zend/Auth/Adapter/Exception.php'; throw new Zend_Auth_Adapter_Exception("Cannot open '$this->_filename' for reading"); } $id = "$this->_username:$this->_realm"; $idLength = strlen($id); $result = array( 'code' => Zend_Auth_Result::FAILURE, 'identity' => array( 'realm' => $this->_realm, 'username' => $this->_username, ), 'messages' => array() ); while ($line = trim(fgets($fileHandle))) { if (substr($line, 0, $idLength) === $id) { if ($this->_secureStringCompare(substr($line, -32), md5("$this->_username:$this->_realm:$this->_password"))) { $result['code'] = Zend_Auth_Result::SUCCESS; } else { $result['code'] = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID; $result['messages'][] = 'Password incorrect'; } return new Zend_Auth_Result($result['code'], $result['identity'], $result['messages']); } } $result['code'] = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND; $result['messages'][] = "Username '$this->_username' and realm '$this->_realm' combination not found"; return new Zend_Auth_Result($result['code'], $result['identity'], $result['messages']); } /** * Securely compare two strings for equality while avoided C level memcmp() * optimisations capable of leaking timing information useful to an attacker * attempting to iteratively guess the unknown string (e.g. password) being * compared against. * * @param string $a * @param string $b * @return bool */ protected function _secureStringCompare($a, $b) { if (strlen($a) !== strlen($b)) { return false; } $result = 0; for ($i = 0; $i < strlen($a); $i++) { $result |= ord($a[$i]) ^ ord($b[$i]); } return $result == 0; } }