File indexing completed on 2024-12-22 05:36:28

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Auth
0017  * @subpackage Adapter
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 
0024 /**
0025  * @see Zend_Auth_Adapter_Interface
0026  */
0027 // require_once 'Zend/Auth/Adapter/Interface.php';
0028 
0029 
0030 /**
0031  * @category   Zend
0032  * @package    Zend_Auth
0033  * @subpackage Adapter
0034  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0035  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0036  */
0037 class Zend_Auth_Adapter_Digest implements Zend_Auth_Adapter_Interface
0038 {
0039     /**
0040      * Filename against which authentication queries are performed
0041      *
0042      * @var string
0043      */
0044     protected $_filename;
0045 
0046     /**
0047      * Digest authentication realm
0048      *
0049      * @var string
0050      */
0051     protected $_realm;
0052 
0053     /**
0054      * Digest authentication user
0055      *
0056      * @var string
0057      */
0058     protected $_username;
0059 
0060     /**
0061      * Password for the user of the realm
0062      *
0063      * @var string
0064      */
0065     protected $_password;
0066 
0067     /**
0068      * Sets adapter options
0069      *
0070      * @param  mixed $filename
0071      * @param  mixed $realm
0072      * @param  mixed $username
0073      * @param  mixed $password
0074      */
0075     public function __construct($filename = null, $realm = null, $username = null, $password = null)
0076     {
0077         $options = array('filename', 'realm', 'username', 'password');
0078         foreach ($options as $option) {
0079             if (null !== $$option) {
0080                 $methodName = 'set' . ucfirst($option);
0081                 $this->$methodName($$option);
0082             }
0083         }
0084     }
0085 
0086     /**
0087      * Returns the filename option value or null if it has not yet been set
0088      *
0089      * @return string|null
0090      */
0091     public function getFilename()
0092     {
0093         return $this->_filename;
0094     }
0095 
0096     /**
0097      * Sets the filename option value
0098      *
0099      * @param  mixed $filename
0100      * @return Zend_Auth_Adapter_Digest Provides a fluent interface
0101      */
0102     public function setFilename($filename)
0103     {
0104         $this->_filename = (string) $filename;
0105         return $this;
0106     }
0107 
0108     /**
0109      * Returns the realm option value or null if it has not yet been set
0110      *
0111      * @return string|null
0112      */
0113     public function getRealm()
0114     {
0115         return $this->_realm;
0116     }
0117 
0118     /**
0119      * Sets the realm option value
0120      *
0121      * @param  mixed $realm
0122      * @return Zend_Auth_Adapter_Digest Provides a fluent interface
0123      */
0124     public function setRealm($realm)
0125     {
0126         $this->_realm = (string) $realm;
0127         return $this;
0128     }
0129 
0130     /**
0131      * Returns the username option value or null if it has not yet been set
0132      *
0133      * @return string|null
0134      */
0135     public function getUsername()
0136     {
0137         return $this->_username;
0138     }
0139 
0140     /**
0141      * Sets the username option value
0142      *
0143      * @param  mixed $username
0144      * @return Zend_Auth_Adapter_Digest Provides a fluent interface
0145      */
0146     public function setUsername($username)
0147     {
0148         $this->_username = (string) $username;
0149         return $this;
0150     }
0151 
0152     /**
0153      * Returns the password option value or null if it has not yet been set
0154      *
0155      * @return string|null
0156      */
0157     public function getPassword()
0158     {
0159         return $this->_password;
0160     }
0161 
0162     /**
0163      * Sets the password option value
0164      *
0165      * @param  mixed $password
0166      * @return Zend_Auth_Adapter_Digest Provides a fluent interface
0167      */
0168     public function setPassword($password)
0169     {
0170         $this->_password = (string) $password;
0171         return $this;
0172     }
0173 
0174     /**
0175      * Defined by Zend_Auth_Adapter_Interface
0176      *
0177      * @throws Zend_Auth_Adapter_Exception
0178      * @return Zend_Auth_Result
0179      */
0180     public function authenticate()
0181     {
0182         $optionsRequired = array('filename', 'realm', 'username', 'password');
0183         foreach ($optionsRequired as $optionRequired) {
0184             if (null === $this->{"_$optionRequired"}) {
0185                 /**
0186                  * @see Zend_Auth_Adapter_Exception
0187                  */
0188                 // require_once 'Zend/Auth/Adapter/Exception.php';
0189                 throw new Zend_Auth_Adapter_Exception("Option '$optionRequired' must be set before authentication");
0190             }
0191         }
0192 
0193         if (false === ($fileHandle = @fopen($this->_filename, 'r'))) {
0194             /**
0195              * @see Zend_Auth_Adapter_Exception
0196              */
0197             // require_once 'Zend/Auth/Adapter/Exception.php';
0198             throw new Zend_Auth_Adapter_Exception("Cannot open '$this->_filename' for reading");
0199         }
0200 
0201         $id       = "$this->_username:$this->_realm";
0202         $idLength = strlen($id);
0203 
0204         $result = array(
0205             'code'  => Zend_Auth_Result::FAILURE,
0206             'identity' => array(
0207                 'realm'    => $this->_realm,
0208                 'username' => $this->_username,
0209                 ),
0210             'messages' => array()
0211             );
0212 
0213         while ($line = trim(fgets($fileHandle))) {
0214             if (substr($line, 0, $idLength) === $id) {
0215                 if ($this->_secureStringCompare(substr($line, -32), md5("$this->_username:$this->_realm:$this->_password"))) {
0216                     $result['code'] = Zend_Auth_Result::SUCCESS;
0217                 } else {
0218                     $result['code'] = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID;
0219                     $result['messages'][] = 'Password incorrect';
0220                 }
0221                 return new Zend_Auth_Result($result['code'], $result['identity'], $result['messages']);
0222             }
0223         }
0224 
0225         $result['code'] = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND;
0226         $result['messages'][] = "Username '$this->_username' and realm '$this->_realm' combination not found";
0227         return new Zend_Auth_Result($result['code'], $result['identity'], $result['messages']);
0228     }
0229 
0230     /**
0231      * Securely compare two strings for equality while avoided C level memcmp()
0232      * optimisations capable of leaking timing information useful to an attacker
0233      * attempting to iteratively guess the unknown string (e.g. password) being
0234      * compared against.
0235      *
0236      * @param string $a
0237      * @param string $b
0238      * @return bool
0239      */
0240     protected function _secureStringCompare($a, $b)
0241     {
0242         if (strlen($a) !== strlen($b)) {
0243             return false;
0244         }
0245         $result = 0;
0246         for ($i = 0; $i < strlen($a); $i++) {
0247             $result |= ord($a[$i]) ^ ord($b[$i]);
0248         }
0249         return $result == 0;
0250     }
0251 }