Location: PHPKode > projects > SolarPHP > solar-system-1.1.1/solar/source/solar/Solar/Auth/Adapter/Htpasswd.php
<?php
/**
 * 
 * Authenticate against a file generated by htpasswd.
 * 
 * Format for each line is "username:hashedpassword\n";
 * 
 * Automatically checks against DES, SHA, and apr1-MD5.
 * 
 * SECURITY NOTE: Default DES encryption will only check up to the first
 * 8 characters of a password; chars after 8 are ignored.  This means
 * that if the real password is "atechars", the word "atecharsnine" would
 * be valid.  This is bad.  As a workaround, if the password provided by
 * the user is longer than 8 characters, and DES encryption is being
 * used, this class will *not* validate it.
 * 
 * @category Solar
 * 
 * @package Solar_Auth
 * 
 * @author Paul M. Jones <hide@address.com>
 * 
 * @license http://opensource.org/licenses/bsd-license.php BSD
 * 
 * @version $Id: Htpasswd.php 4405 2010-02-18 04:27:25Z pmjones $
 * 
 */
class Solar_Auth_Adapter_Htpasswd extends Solar_Auth_Adapter
{
    /**
     * 
     * Default configuration values.
     * 
     * @config string file Path to password file.
     * 
     * @var array
     * 
     */
    protected $_Solar_Auth_Adapter_Htpasswd = array(
        'file'  => null,
    );
    
    /**
     * 
     * Verifies a username handle and password.
     * 
     * @return mixed An array of verified user information, or boolean false
     * if verification failed.
     * 
     */
    protected function _processLogin()
    {
        // force the full, real path to the file
        $file = realpath($this->_config['file']);
        
        // does the file exist?
        if (! file_exists($file)) {
            throw $this->_exception('ERR_FILE_NOT_FOUND', array(
                'file' => $file,
            ));
        }
        
        // open the file
        $fp = @fopen($file, 'r');
        if (! $fp) {
            throw $this->_exception('ERR_FILE_NOT_READABLE', array(
                'file' => $file,
            ));
        }
        
        // find the user's line in the file
        $len = strlen($this->_handle) + 1;
        $ok = false;
        while ($line = fgets($fp)) {
            if (substr($line, 0, $len) == "{$this->_handle}:") {
                // found the line, leave the loop
                $ok = true;
                break;
            }
        }
        
        // close the file
        fclose($fp);
        
        // did we find the username?
        if (! $ok) {
            // username not in the file
            return false;
        }
        
        // break up the pieces: 0 = handle, 1 = encrypted (hashed)
        // passwd. may be more than that but we don't care.
        $tmp = explode(':', trim($line));
        $stored_hash = $tmp[1];
        
        // what kind of encryption hash are we using?  look at the first
        // few characters of the hash to find out.
        if (substr($stored_hash, 0, 6) == '$apr1$') {
        
            // use the apache-specific MD5 encryption
            $computed_hash = self::_apr1($this->_passwd, $stored_hash);
            
        } elseif (substr($stored_hash, 0, 5) == '{SHA}') {
        
            // use SHA1 encryption.  pack SHA binary into hexadecimal,
            // then encode into characters using base64. this is per
            // Tomas V. V. Cox.
            $hex = pack('H40', sha1($this->_passwd));
            $computed_hash = '{SHA}' . base64_encode($hex);
            
        } else {
        
            // use DES encryption (the default).
            // 
            // Note that crypt() will only check up to the first 8
            // characters of a password; chars after 8 are ignored. This
            // means that if the real password is "atecharsnine", the
            // word "atechars" would be valid.  This is bad.  As a
            // workaround, if the password provided by the user is
            // longer than 8 characters, this method will *not* validate
            // it.
            //
            // is the password longer than 8 characters?
            if (strlen($this->_passwd) > 8) {
                // automatically reject
                return false;
            } else {
                $computed_hash = crypt($this->_passwd, $stored_hash);
            }
        }
        
        // did the hashes match?
        if ($stored_hash == $computed_hash) {
            return array('handle' => $this->_handle);
        } else {
            return false;
        }
    }
    
    /**
     * 
     * APR compatible MD5 encryption.
     * 
     * @author Mike Wallner <hide@address.com>
     * 
     * @author Paul M. Jones (minor modfications) <hide@address.com>
     * 
     * @param string $plain Plaintext to crypt.
     * 
     * @param string $salt The salt to use for encryption.
     * 
     * @return string The APR MD5 encrypted string.
     * 
     */
    protected static function _apr1($plain, $salt)
    {
        if (preg_match('/^\$apr1\$/', $salt)) {
            $salt = preg_replace('/^\$apr1\$([^$]+)\$.*/', '\\1', $salt);
        } else {
            $salt = substr($salt, 0,8);
        }
        
        $length  = strlen($plain);
        $context = $plain . '$apr1$' . $salt;
        
        $binary = hash('md5', $plain . $salt . $plain, true);
        
        for ($i = $length; $i > 0; $i -= 16) {
            $context .= substr($binary, 0, min(16 , $i));
        }
        for ( $i = $length; $i > 0; $i >>= 1) {
            $context .= ($i & 1) ? chr(0) : $plain[0];
        }
        
        $binary = hash('md5', $context, true);
        
        for($i = 0; $i < 1000; $i++) {
            $new = ($i & 1) ? $plain : $binary;
            if ($i % 3) {
                $new .= $salt;
            }
            if ($i % 7) {
                $new .= $plain;
            }
            $new .= ($i & 1) ? $binary : $plain;
            $binary = hash('md5', $new, true);
        }
        
        $p = array();
        for ($i = 0; $i < 5; $i++) {
            $k = $i + 6;
            $j = $i + 12;
            if ($j == 16) {
                $j = 5;
            }
            $p[] = self::_64(
                (ord($binary[$i]) << 16) |
                (ord($binary[$k]) << 8) |
                (ord($binary[$j])),
                5
            );
        }
        
        return '$apr1$' . $salt . '$' . implode($p) 
             . self::_64(ord($binary[11]), 3);
    }
    
    /**
     * 
     * Convert to allowed 64 characters for encryption.
     * 
     * @author Mike Wallner <hide@address.com>
     * 
     * @author Paul M. Jones (minor modfications) <hide@address.com>
     * 
     * @param string $value The value to convert.
     * 
     * @param int $count The number of characters.
     * 
     * @return string The converted value.
     * 
     */
    protected static function _64($value, $count)
    {
        $charset = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
        $result = '';
        while(--$count) {
            $result .= $charset[$value & 0x3f];
            $value >>= 6;
        }
        return $result;
    }
}
Return current item: SolarPHP