[sork] Vpopmail + Mysql + Passwd + Crypt md5 Fix Summary

Ashvin Babu / Surya Technologi a32aw11sc at yahoo.com
Wed Nov 26 00:59:04 PST 2003


Here is a summary of this fix. All this information
was found in the archives, but I thought it would be
useful to include it in one summary post.
 
I am using the latest stable Horde/IMP and this
version of passwd:
passwd-RELENG-2003-11-25.tar.gz (latest snapshot)
 
I am also using vpopmail/qmail/mysql.
 
To get passwd to work, these are the backend file
settings for vpopmail:
 
 $backends['vpopmail'] = array (
    'name' => 'vpopmail Authentication',
    'preferred' => '',
    'password policy' => array(
        'minLength' => 3,
        'maxLength' => 8,
        'maxSpace' => 0,
        'minUpper' => 0,
        'minLower' => 0,
        'minNumeric' => 0
    ),
    'driver' => 'vpopmail',
    'params' => array(
        'phptype'    => 'mysql',
        'hostspec'   => 'localhost',
        'username'   => 'your mysql username',
        'password'   => 'your mysql password',
        'encryption' => 'crypt-md5',
        'database'   => 'vpopmail',
        'table'      => 'table name for your domain',
        'name'    => 'pw_name',
        'domain'  => 'pw_domain',
        'passwd' =>  'pw_passwd',
        'clear_passwd' => 'pw_clear_passwd',
        'use_clear_passwd' => 'true',
        'socket' => '/var/lib/mysql.sock',
        'protocol' => 'tcp'
    )
);


The Crypt-md5 patch for Driver.php is posted here:
http://lists.horde.org/archives/sork/Week-of-Mon-20030324/000887.html

I have tried to attach the modified Driver.php to this
posting (don't know if we can attach files).

You also have to modify your mysql database to add the
pw_domain field (then populate the field with your
domain name). I have a standard vpopmail/mysql install
and it was missing this field:

Database changed
mysql> ALTER TABLE suryatechnologies_com ADD COLUMN
pw_domain varchar(100
);
Query OK, 9 rows affected (0.03 sec)
Records: 9  Duplicates: 0  Warnings: 0

mysql> UPDATE suryatechnologies_com SET
pw_domain='suryatechnologies.com'
    -> ;
Query OK, 9 rows affected (0.01 sec)
Rows matched: 9  Changed: 9  Warnings: 0

mysql>

Thanks to everyone on this list who created the above
information. It is good to have password finally
working with vpopmail/mysql.

Ashvin / suryatechnologies.com




__________________________________
Do you Yahoo!?
Free Pop-Up Blocker - Get it now
http://companion.yahoo.com/
-------------- next part --------------
<?php
/**
 * $Horde: passwd/lib/Driver.php,v 1.3.2.6 2003/02/15 20:16:26 ericr Exp $
 *
 * Passwd_Driver:: defines an API for implementing password change
 * systems for Passwd.
 *
 * @author   Mike Cochrane <mike at graftonhall.co.nz>
 * @author   Eric Rostetter <eric.rostetter at physics.utexas.edu>
 * @version  $Revision: 1.3.2.6 $
 * @since    Passwd 2.1
 * @package  passwd
 */

class Passwd_Driver {

    /** error string returned to user if an eror occurs. */
    var $err_str;

    /**
     * Attempts to return a concrete Passwd_Driver instance based on $driver.
     *
     * @param string    $driver     The type of concrete passwd_Driver subclass
     *                              to return.  The is based on the passwd
     *                              driver ($driver).  The code is dynamically
     *                              included.
     *
     * @param array     $params     (optional) A hash containing any additional
     *                              configuration or connection parameters a
     *                              subclass might need.
     *
     * @return mixed    The newly created concrete Passwd_Driver instance, or
     *                  false on an error.
     */
    function &factory($driver, $params = array())
    {
        $driver = strtolower(basename($driver));
        @include_once dirname(__FILE__) . '/Driver/' . $driver . '.php';
        $class = 'Passwd_Driver_' . $driver;
        if (class_exists($class)) {
            return new $class($params);
        } else {
            return false;
        }
    }

    /**
     * Attempts to return a reference to a concrete Passwd_Driver instance
     * based on $driver.  It will only create a new instance if no
     * Passwd_Driver instance with the same parameters currently exists.
     *
     * This should be used if multiple storage sources are required.
     *
     * This method must be invoked as: $var = &Passwd_Driver::singleton()
     *
     * @param string    $driver     The type of concrete Passwd_Driver subclass
     *                              to return.  The is based on the passwd
     *                              driver ($driver).  The code is dynamically
     *                              included.
     *
     * @param array     $params     (optional) A hash containing any additional
     *                              configuration or connection parameters a
     *                              subclass might need.
     *
     * @return mixed    The created concrete Passwd_Driver instance, or false
     *                  on error.
     */
    function &singleton($driver, $params = array())
    {
        static $instances;

        if (!isset($instances)) {
            $instances = array();
	}

        $signature = serialize(array($driver, $params));
        if (!isset($instances[$signature])) {
            $instances[$signature] = &Passwd_Driver::factory($driver, $params);
        }

        return $instances[$signature];
    }

    /**
     * Compare a plaintext password with an encrypted password.
     *
     * @return Boolean  True is they match, False if they differ
     */
    function comparePasswords($encrypted, $plaintext)
    {
        switch ($this->_params['encryption']) {
            case "plain":
                if ($encrypted == $plaintext) {
                    return true;
                }
                break;
            case 'md5-hex':
                if ($encrypted == md5($plaintext)) {
                    return true;
                }
                break;
            case 'md5-base64':
                if ($encrypted == base64_encode(mHash(MHASH_MD5, $plaintext))) {                   return true;
                }
               break;
	case 'crypt-md5':
		$salt = substr($encrypted , 0, 8);
		if ($encrypted == crypt($plaintext, $salt)) {
			return true;
		}
		break;		
            case 'crypt':
                $encrypted = substr($encrypted, 7);
                $salt = substr($encrypted , 0, 2);
                if ($encrypted == crypt($plaintext, $salt)) {
                    return true;
                }
                break;
            case 'sha':
                $encrypted = substr($encrypted, 5);
                if ($encrypted == base64_encode(mHash(MHASH_SHA1, $plaintext)))
{
                    return true;
                }
                break;
            case 'ssha':
                $encrypted = substr($encrypted, 6);
                $hash = base64_decode($encrypted);
               $salt = substr($hash, 20);
                if ($hash == mHash(MHASH_SHA1, $plaintext . $salt)) {
                    return true;
                }
                break;
            case 'smd5':
                $encrypted = substr($encrypted, 6);
                $hash = base64_decode($encrypted);
                $salt = substr($hash, 16);
                if ($hash == mHash(MHASH_MD5, $plaintext . $salt)) {
                   return true;
                }
                break;
            default:
                return PEAR::raiseError($this->_params['encryption'] . ' Encryption not implemented yet');
                break;
       }
        return PEAR::raiseError('Incorrect Password');
    }

    /**
     * Format a password using the current encryption.
     *
     * @param  $newPassword  The plaintext password to encrypt.
     *
     * @return String        The formated password.
     */
    function encryptPassword($newPassword)
    {
        // Encrypt the password
        switch ($this->_params['encryption']) {
            case "plain":
                break;
            case "sha":
                $newPassword = "{SHA}" . base64_encode(mHash(MHASH_SHA1, $newPassword));
                break;
	 case "crypt-md5":
		function randltr() {
                   $retval = ord('a'); 
                   $randnum = rand() % 64; 
                   if ($randnum < 26) $retval = $randnum + ord('a');
                   if ($randnum > 25) $retval = $randnum - 26 + ord('A');
                   if ($randnum > 51) $retval = $randnum - 52 + ord('0');
                   if ($randnum == 62) $retval = ord(';');
                   if ($randnum == 63) $retval = ord('.');
                   return chr($retval);
                }
                $salt = '$1$' . randltr() . randltr() . randltr() . randltr(); 
                $newPassword = crypt($newPassword,$salt);
                break;	
            case "crypt":
                // The salt is left out, generated by php
                $newPassword = "{crypt}" . crypt($newPassword);
                break;
            case "md5-hex":
                $newPassword = md5($newPassword);
                break;
            case "md5-base64":
                $newPassword = "{MD5}" . base64_encode(mHash(MHASH_MD5,
                         $newPassword));
                 break;
            case "ssha":
                $salt = mhash_keygen_s2k(MHASH_SHA1,$newPassword,substr(pack("h*",md5(mt_rand())),0,8),4);
                $newPassword = "{SSHA}" . base64_encode(mHash(MHASH_SHA1, $newPassword . $salt) . $salt);
                break;
            case "smd5":
                $salt = mhash_keygen_s2k(MHASH_MD5,$newPassword,substr(pack("h*",md5(mt_rand())),0,8),4);
                $newPassword = "{SMD5}" . base64_encode(mHash(MHASH_SMD5, $newPassword . $salt) . $salt);
                break;
            default:
                return PEAR::raiseError(_("Password module is not properly configured"));
                break;
        }
        return $newPassword;
    }

    /**
     * Change the user's password.
     *
     * @param   $username     The user for which to change the password.
     * @param   $oldpassword  The old (current) user password.
     * @param   $newpassword  The new user password to set.
     *
     * @return  boolean       True or false based on success of the change.
     */

    function change_password($user_name, $old_password, $new_password) {
        return new PEAR_Error(_("Backend not correctly implemented."));
    }

    /**
     * Change the Horde/IMP cached credentials.  Should be called only
     * after a successful change of the password in the actual backend
     * storage.  This routine is the same for all backends and should
     * not be implemented in the backend classes.
     *
     * @param   $username     The username we're changing.
     * @param   $oldpassword  The old user password.
     * @param   $newpassword  The new user password to set.
     *
     * @return  none
     */
    function reset_credentials($username, $oldpassword, $newpassword) {
        if (Auth::getAuth() == $username &&
            Auth::getCredential('password') == $oldpassword) {
            $credentials['transparent'] = Auth::getCredential('transparent');
            $credentials['password'] = $newpassword;
            Auth::setAuth(Auth::getAuth(), $credentials);
            if (isset($_SESSION['imp']['pass']) && 
               $oldpassword == $_SESSION['imp']['pass']) {
               $_SESSION['imp']['pass'] = Secret::write(Secret::getKey('imp'),
                       $newpassword);
            }
        }
    }

}


More information about the sork mailing list