[sork] Re: passwd 2.2.1-RC1 + vpopmail + mysql
Chris Nojima
chris at toastedpixel.com
Thu Apr 8 21:33:48 PDT 2004
Hi Darren,
Been looking over the horde/passwd code a bit. I'm not familiar enough
with their coding specifications to submit a proper patch.
just to reiterate some of the things i wrote to you privately:
- the error we've been experiencing using passwd with vpopmail+mysql is
due to the way vpopmail encrypts passwords: explained in much more depth
here:
http://article.gmane.org/gmane.comp.horde.sork/1114/match=passwd+vpopmail
+patch
so, to get horde/passwd to "correctly" encrypt the user's password, we
have to update its encryption scheme to match that being used by
vpopmail. Dorneles Treméa does a tremendous job explaining/providing a
patch, so i won't bother with this point anymore.
his patch combined with a current passwd-RELENG distribution worked
perfectly for me.
on to your particulars:
> The authentication I'm trying to use is vpopmail, so the values I'm using
> are 'pw_name' and 'pw_passwd', which are correct. The problem is that
> 'pw_domain' doesn't exist in this vpopmail configuration. Neither does the
> 'vpopmail' table. Inside the vpopmail database, there are tables created for
> every domain name in this format:
right, your problem stems from the use of "--disable-many-domains" at
the configuration point of your vpopmail (which creates a table for each
virtual domain instead of storing all users in a single table). i think
you mentioned using a qmail-toaster distribution. curious that it came
this way, as iirc, that's not a default option at configuration time.
passwd currently doesn't seem to support that particular part of
vpopmail.
in the "backends.php" file, this is the default setting for using
vpopmail as the backend:
/**************************************************************/
$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' => 'user',
'password' => '******',
'encryption' => 'crypt',
'database' => 'vpopmail',
'table' => 'vpopmail',
'name' => 'pw_name',
'domain' => 'pw_domain',
'passwd' => 'pw_passwd',
'clear_passwd' => 'pw_clear_passwd',
'use_clear_passwd' => true
)
);
/**************************************************************/
it's hard-coded to use "vpopmail" as the table to retrieve/update
password information.
follow the code a bit and you find this bit, which takes the user name,
domain info, and submitted password to compare against the data in your
database. all of this is taken from
"horde/passwd/lib/Driver/vpopmail.php" and we're looking at the function
"_lookup($user, $oldPassword)":
/**************************************************************/
function _lookup($user, $oldPassword)
{
// Connect to the database
$res = $this->_connect();
if (PEAR::isError($res)) {
return $res;
}
list($name,$domain)=explode("@",$user);
// Build the SQL query.
$query = 'SELECT ' . $this->_params['passwd'] . ' FROM ' .
$this->_params['table'];
$query .= ' WHERE ' . $this->_params['name'] . ' = ' .
$this->_db->quote($name);
$query .= ' AND ' . $this->_params['domain'] . ' = ' .
$this->_db->quote($domain);
>>[ the function continues, but i've snipped it here ]
/**************************************************************/
this would result in query such as:
"SELECT pw_passwd FROM vpopmail WHERE pw_name = 'username' AND pw_domain
= 'example.com'". since your setup doesn't use the vpopmail table and
instead uses individual tables to represent what would be in the
pw_domain column, it would obviously fail every time.
for a 1-table-per-domain vpopmail setup, the pertinent change is to the
"' FROM ' . $this->_params['table'];" bit.
if you want a quick, testable fix, try replacing the stuff under
"//Build the SQL query" comment to this:
/**************************************************************/
$this->_params['table'] = strtr($this->_params['domain'], '.', '_');
$query = 'SELECT ' . $this->_params['passwd'] . ' FROM ' .
$this->_params['table'];
$query .= ' WHERE ' . $this->_params['name'] . ' = ' .
$this->_db->quote($name);
/**************************************************************/
this will return a query like:
"SELECT pw_passwd FROM example_com WHERE pw_name = 'username'"
since i don't have this particular setup, i can't test it to make sure.
instead of looking for 'table', it rewrites the passed 'domain' var
replacing '.' with underscores as vpopmail does for it's domains.
>
> +--------------------+
> | Tables_in_vpopmail |
> +--------------------+
> | dir_control |
> | dnbni_com |
> | lastauth |
> | vlog |
> +--------------------+
>
> In the dnbni_com table, there are these fields:
>
> pw_name (the username WITHOUT the domain name appended to it)
> pw_password (The encrypted "$1" type password)
> pw_uid (it's always 0)
> pw_gid (it's always 0)
> pw_gecos (User Full Name field)
> pw_dir (actual user subdirectory)
> pw_shell (quota size)
> pw_clear_password (cleartext password)
>
the other part here is to update the user's password once the check is
successful and positive. here's the default function:
/**************************************************************/
function _modify($user, $newPassword) {
// Connect to the database
$res = $this->_connect();
if (PEAR::isError($res)) {
return $res;
}
list($name,$domain)=explode("@",$user);
// Encrypt the password
$clearPassword = $newPassword;
$newPassword = $this->encryptPassword($newPassword);
// Build the SQL query.
$query = 'UPDATE ' . $this->_params['table'];
$query .= ' SET ' . $this->_params['passwd'] . ' = ' .
$this->_db->quote($newPassword);
if($this->_params['use_clear_passwd']) {
$query.= ' , ' . $this->_params['clear_passwd'] . ' = ' .
$this->_db->quote($clearPassword);
}
$query .= ' WHERE ' . $this->_params['name'] . ' = ' .
$this->_db->quote($name);
$query .= ' AND ' . $this->_params['domain'] . ' = ' .
$this->_db->quote($domain);
// Execute the query.
$result = $this->_db->query($query);
if (DB::isError($result)) {
return $result;
}
return true;
}
/**************************************************************/
this will return something like:
"UPDATE vpopmail SET pw_passwd = 'encryptedpassword' , pw_passwd_clear =
'clearpassword' WHERE pw_name = 'username' AND pw_domain = 'example.com'"
my changes, again untested:
/**************************************************************/
// Build the SQL query.
$this->_params['table'] = strtr($this->_params['domain'], '.', '_');
$query = 'UPDATE ' . $this->_params['table'];
$query .= ' SET ' . $this->_params['passwd'] . ' = ' .
$this->_db->quote($newPassword);
if($this->_params['use_clear_passwd']) {
$query.= ' , ' . $this->_params['clear_passwd'] . ' = ' .
$this->_db->quote($clearPassword);
}
$query .= ' WHERE ' . $this->_params['name'] . ' = ' .
$this->_db->quote($name);
/**************************************************************/
this will return something like:
"UPDATE example_com SET pw_passwd = 'encryptedpassword' ,
pw_clear_passwd = 'clearpassword' WHERE pw_name = 'username'"
that should update your user's password using your vpopmail setup.
if you haven't applied Dorneles Trema's patch, then all this will be
moot as it won't be comparing identically encrypted passwords, and will
hence fail every time.
I hope this helps!
-chris nojima
More information about the sork
mailing list