Author Topic: RoundCube Password Plugin -- PLESK Add-on.  (Read 15240 times)

Offline AntiochWebHost

  • Newbie
  • *
  • Posts: 2
    • http://www.antiochwebhost.com
RoundCube Password Plugin -- PLESK Add-on.
« on: January 26, 2011, 02:39:46 AM »
While someone else recently posted that they got the existing password plugin to work with PLESK / Qmail - I was unable to do this.

I have a working solution that I would like to share and if anyone would like to discuss this, here is my question that I don't have time to figure out (laughing):

Am I wrong to say that PLESK needs to have both the 'accounts' table in database 'psa' updated -- as well as poppassd run?

If I'm wrong, there's a bit of redundancy below -- I used the code from the poppassd for RC v0.2.1 that was on here before to handle the poppassd aspect of this.

/.../plugins/password/config.inc.php:
Code: [Select]
$rcmail_config['password_driver'] = 'plesk';
$rcmail_config['plesk_db_host'] = 'localhost';
$rcmail_config['plesk_db_admin'] = 'admin';
$rcmail_config['plesk_db_pass'] = '[your plesk admin password here]';


/.../plugins/password/drivers/plesk.php:
Code: [Select]
/**
 * PLESK SQL+POPDPASSWD Password Driver
 *      poppassd code lifted from Crusher's Non-API RoundCube PLESK Password Plugin
 */

function password_save($curpass, $passwd) {

        $rcmail = rcmail::get_instance();


        $link = mysql_connect($rcmail->config->get('plesk_db_host'), $rcmail->config->get('plesk_db_admin'), $rcmail->config->get('plesk_db_pass'));

        if (!$link)
                return PASSWORD_ERROR;
        mysql_selectdb("psa");

        $sql = "SELECT a.id,a.password,m.mail_name,d.name
                FROM domains d, mail m, accounts a
                WHERE
                        d.name='" . $rcmail->user->get_username('domain') . "' AND
                        m.mail_name='" . $rcmail->user->get_username('local'). "' AND
                        m.dom_id=d.id AND a.id=m.account_id";
        $res = mysql_query($sql);
        if ($row = mysql_fetch_assoc($res)) {

                $driver = new Passwd_Driver_poppassd(array('host'=>'localhost', 'port'=> '106'));
                $return = $driver->changePassword($_SESSION['username'], $curpass, $passwd);
                if ($return[1] == "Success")
                        if ($row["password"] != $curpass)
                                return PASSWORD_ERROR;
                        elseif (mysql_query("UPDATE accounts set password='" . $passwd . "' where id='" . $row["id"] . "' LIMIT 1"))
                                return PASSWORD_SUCCESS;
                        else
                                return PASSWORD_ERROR;
                else
                        return PASSWORD_ERROR;
        }
        else
                return PASSWORD_ERROR;
}

class Passwd_Driver_poppassd{

    var $_fp;
    function Passwd_Driver_poppassd($params = array())
    {
        $this->_params['host'] = array_key_exists('host', $params) ? $params['host'] : 'localhost';
        $this->_params['port'] = array_key_exists('port', $params) ? $params['port'] : 106;
    }
    function _connect()
    {
        $this->_fp = fsockopen($this->_params['host'], $this->_params['port'], $errno, $errstr, 30);
        if (!$this->_fp) {
            return array(false, $errstr);
        } else {
            $res = $this->_getPrompt();
            return $res;
        }
    }
    function _disconnect()
    {
        if (isset($this->_fp)) {
            fputs($this->_fp, "quit\n");
            fclose($this->_fp);
        }
    }
    function _getPrompt()
    {
        $prompt = fgets($this->_fp, 4096);
        if (!$prompt) {
            return array(false, "No prompt returned from server.");
        }
        if (preg_match('/^[1-5][0-9][0-9]/', $prompt)) {
            $rc = substr($prompt, 0, 3);
            /* This should probably be a regex match for 2?0 or 3?0, no? */
            if ($rc == '200' || $rc == '220' || $rc == '250' || $rc == '300' ) {
                return array(true, "Success");
            } else {
                return array(false, $prompt);
            }
        } else {
            return array(true, "Success");
        }
    }

    function _sendCommand($cmd, $arg)
    {
        $line = $cmd . ' ' . $arg . "\n";
        $res_fputs = fputs($this->_fp, $line);
        if (!$res_fputs) {
            return array(false, "Cannot send command to server.");
        }
        $res = $this->_getPrompt();
        return $res;
    }

    function changePassword($username, $old_password, $new_password)
    {
        $res = $this->_connect();
        if (!$res[0]) {
            return $res;
        }

        $res = $this->_sendCommand('user', $username);
        if (!$res[0]) {
            $this->_disconnect();
            return array(false, "User not found");
        }

        $res = $this->_sendCommand('pass', $old_password);
        if (!$res[0]) {
            $this->_disconnect();
            return array(false, "Incorrect Password");
        }



        $res = $this->_sendCommand('newpass', $new_password);
        $this->_disconnect();
        if (!$res[0]) {
            return $res;
        }

        return array(true, "Success");
    }

}

?>
« Last Edit: January 26, 2011, 02:42:45 AM by AntiochWebHost »

Offline projectmyst

  • Jr. Member
  • **
  • Posts: 30
RoundCube Password Plugin -- PLESK Add-on.
« Reply #1 on: January 26, 2011, 03:04:45 AM »
I think that would only work if you have access to username and password for the email database and as i don't that would work for me.

However i used this settings and it worked for me

Code: [Select]
<?php
$rcmail_config
['password_driver'] = 'poppassd';
$rcmail_config['password_confirm_current'] = true;
$rcmail_config['password_minimum_length'] = 5;
$rcmail_config['password_require_nonalpha'] = false;
$rcmail_config['password_pop_host'] = 'localhost';
$rcmail_config['password_pop_port'] = 106;
?>

Offline AntiochWebHost

  • Newbie
  • *
  • Posts: 2
    • http://www.antiochwebhost.com
RoundCube Password Plugin -- PLESK Add-on.
« Reply #2 on: January 26, 2012, 04:08:22 PM »
Funny.  I overwrote my password driver that I forgot that I wrote (as a fork, of course).  Searched for the solution for PLESK and Passwords and discovered that I'd posted the driver.

projectmyst -- I wanted to caution you to allowing password changing if Roundcube does not have access to the PLESK Password Database.  If you change the password directly via POPPASSD, PLESK will overwrite the change when you run mchk, or upgrade PLESK or do something that causes PLESK to do an integrity check.

The 'driver' that I wrote is a fork of poppassd that also makes the needed PLESK DB changes simultaneously.  For me, it's a great solution as I run PLESK servers and have root access to make this a global application.

I can understand why this does not work well for you.

Offline nwtech

  • Newbie
  • *
  • Posts: 9
Re: RoundCube Password Plugin -- PLESK Add-on.
« Reply #3 on: May 22, 2013, 12:31:08 PM »
Antioch, thanks for the PLESK driver! However, I can't seem to get it to work. I simply get an error that it can't save the new password.

I'm using the default config.inc for the password plugin, with your configuration added to it and I've created and uploaded the PLESK driver that you created.

I do see this in the logs:

Code: [Select]
PHP Error: Password plugin: Broken driver plesk in /var/www/vhosts/roundcube.local/httpdocs/plugins/password/password.php on line 258 (POST /?_task=settings&_action=plugin.password-save?_task=&_action=)
Any suggestions?


Offline SKaero

  • Administrator
  • Hero Member
  • *****
  • Posts: 5,543
    • http://SKaero.com/
Re: RoundCube Password Plugin -- PLESK Add-on.
« Reply #4 on: May 22, 2013, 12:57:11 PM »
The base password plugin has been re-written so the password driver needs to be updated to the new style.

Offline nwtech

  • Newbie
  • *
  • Posts: 9
Re: RoundCube Password Plugin -- PLESK Add-on.
« Reply #5 on: May 22, 2013, 02:55:13 PM »
The base password plugin has been re-written so the password driver needs to be updated to the new style.

Thanks for the quick reply SKaero.

Based on the plugin that is in this thread, can you offer any suggestions on what needs to be changed in order for it to work with the new password plugin?

TIA

Offline SKaero

  • Administrator
  • Hero Member
  • *****
  • Posts: 5,543
    • http://SKaero.com/
Re: RoundCube Password Plugin -- PLESK Add-on.
« Reply #6 on: May 22, 2013, 03:57:28 PM »
I haven't played with the driver system very much so I can't give to much insight into what needs to be changed, try comparing it to the other password drivers to see whats different.

Offline activeindex

  • Newbie
  • *
  • Posts: 2
Re: RoundCube Password Plugin -- PLESK Add-on.
« Reply #7 on: September 05, 2013, 07:16:05 AM »
Here is a working solution for Plesk (should work for plesk 10 and 11). it uses the version of the plugin specified above, but adapted to work with the new driver style.

create a file at /plugins/password/drivers/plesk.php


Code: [Select]
<?php
class rcube_plesk_password
{
    function __construct($params = array())
    {
        
$this->_params['host'] = 'localhost';
        
$this->_params['port'] = 106;
    }


    function 
format_error_result($code$line)
    {
        if (
preg_match('/^\d\d\d\s+(\S.*)\s*$/'$line$matches)) {
            return array(
'code' => $code'message' => $matches[1]);
        } else {
            return 
$code;
        }
    }

function save($curpass$passwd) {

        
$rcmail rcmail::get_instance();

        
$link mysql_connect($rcmail->config->get('plesk_db_host'), $rcmail->config->get('plesk_db_admin'), $rcmail->config->get('plesk_db_pass'));

        if (!
$link)
                return 
PASSWORD_ERROR;
        
mysql_select_db("psa");

        
$sql "SELECT a.id,a.password,m.mail_name,d.name
                FROM domains d, mail m, accounts a
                WHERE
                        d.name='" 
$rcmail->user->get_username('domain') . "' AND
                        m.mail_name='" 
$rcmail->user->get_username('local'). "' AND
                        m.dom_id=d.id AND a.id=m.account_id"
;
        
$res mysql_query($sql);
        if (
$row mysql_fetch_assoc($res)) {

                
//$driver = new Passwd_Driver_poppassd(array('host'=>'localhost', 'port'=> '106'));
                
$return $this->changePassword($_SESSION['username'], $curpass$passwd);
                if (
$return[1] == "Success")
                        if (
$row["password"] != $curpass)
                                return 
PASSWORD_ERROR;
                        elseif (
mysql_query("UPDATE accounts set password='" $passwd "' where id='" $row["id"] . "' LIMIT 1"))
                                return 
PASSWORD_SUCCESS;
                        else
                                return 
PASSWORD_ERROR;
                else
                        return 
PASSWORD_ERROR;
        }
        else
                return 
PASSWORD_ERROR;
}

    function 
_connect()
    {
        
$this->_fp fsockopen($this->_params['host'], $this->_params['port'], $errno$errstr30);
        if (!
$this->_fp) {
            return array(
false$errstr);
        } else {
            
$res $this->_getPrompt();
            return 
$res;
        }
    }
    function 
_disconnect()
    {
        if (isset(
$this->_fp)) {
            
fputs($this->_fp"quit\n");
            
fclose($this->_fp);
        }
    }
    function 
_getPrompt()
    {
        
$prompt fgets($this->_fp4096);
        if (!
$prompt) {
            return array(
false"No prompt returned from server.");
        }
        if (
preg_match('/^[1-5][0-9][0-9]/'$prompt)) {
            
$rc substr($prompt03);
            
/* This should probably be a regex match for 2?0 or 3?0, no? */
            
if ($rc == '200' || $rc == '220' || $rc == '250' || $rc == '300' ) {
                return array(
true"Success");
            } else {
                return array(
false$prompt);
            }
        } else {
            return array(
true"Success");
        }
    }

    function 
_sendCommand($cmd$arg)
    {
        
$line $cmd ' ' $arg "\n";
        
$res_fputs fputs($this->_fp$line);
        if (!
$res_fputs) {
            return array(
false"Cannot send command to server.");
        }
        
$res $this->_getPrompt();
        return 
$res;
    }

    function 
changePassword($username$old_password$new_password)
    {
        
$res $this->_connect();
        if (!
$res[0]) {
            return 
$res;
        }

        
$res $this->_sendCommand('user'$username);
        if (!
$res[0]) {
            
$this->_disconnect();
            return array(
false"User not found");
        }

        
$res $this->_sendCommand('pass'$old_password);
        if (!
$res[0]) {
            
$this->_disconnect();
            return array(
false"Incorrect Password");
        }



        
$res $this->_sendCommand('newpass'$new_password);
        
$this->_disconnect();
        if (!
$res[0]) {
            return 
$res;
        }

        return array(
true"Success");
    }


  
}

Then in your config/main.inc.php add these lines:

Code: [Select]
<?php
// Password Plugin options
// -----------------------
// A driver to use for password change. Default: "sql".
// See README file for list of supported driver names.
$rcmail_config['password_driver'] = 'plesk';

// Determine whether current password is required to change password.
// Default: false.
$rcmail_config['password_confirm_current'] = true;

// Require the new password to be a certain length.
// set to blank to allow passwords of any length
$rcmail_config['password_minimum_length'] = 5;

// Require the new password to contain a letter and punctuation character
// Change to false to remove this check.
$rcmail_config['password_require_nonalpha'] = false;

// Enables logging of password changes into logs/password
$rcmail_config['password_log'] = false;

// Comma-separated list of login exceptions for which password change
// will be not available (no Password tab in Settings)
$rcmail_config['password_login_exceptions'] = null;

// Array of hosts that support password changing. Default is NULL.
// Listed hosts will feature a Password option in Settings; others will not.
// Example:
//$rcmail_config['password_hosts'] = array('mail.example.com', 'mail2.example.org');
$rcmail_config['password_hosts'] = null;

// Enables saving the new password even if it matches the old password. Useful
// for upgrading the stored passwords after the encryption scheme has changed.
$rcmail_config['password_force_save'] = false;


//Plesk DB

$rcmail_config['plesk_db_host'] = 'localhost'//psa database host
$rcmail_config['plesk_db_admin'] = ''//psa database user
$rcmail_config['plesk_db_pass'] = ''//psa db user password


Lastly remember to activate the password plugin in the config, look for this code and insert 'password' into the array:

Code: [Select]
// ----------------------------------
// PLUGINS
// ----------------------------------

// List of active plugins (in plugins/ directory)
$rcmail_config['plugins'] = array('password');

Offline projectmyst

  • Jr. Member
  • **
  • Posts: 30
Re: RoundCube Password Plugin -- PLESK Add-on.
« Reply #8 on: September 08, 2013, 07:22:00 PM »
Hi activeindex, i have tried this put can't get it to work. Is this for qmail or postfix? If added the plesk.php and copied the code to it, added the extra config!
« Last Edit: September 08, 2013, 07:32:38 PM by projectmyst »

Offline activeindex

  • Newbie
  • *
  • Posts: 2
Re: RoundCube Password Plugin -- PLESK Add-on.
« Reply #9 on: September 09, 2013, 06:34:50 AM »
I think this is for postfix.

Have you remembered to fill in your database admin username and password.

Also check that your $rcmail_config['password_driver'] declaration exists only once in the page.

Offline nwtech

  • Newbie
  • *
  • Posts: 9
Re: RoundCube Password Plugin -- PLESK Add-on.
« Reply #10 on: October 01, 2013, 01:14:45 PM »
@activeindex

Thanks for updating this thread, however, I'm having issues with this driver. I've put everything in where it should go and added the db creds.

One thing I wasn't sure of, was which db creds to use...the ones found in config/db.inc.php or if I should add dedicated creds for roundcube to the 'psa' database. I did both and I end up with the error: Could not save password.

Any ideas?

Offline Planzo

  • Newbie
  • *
  • Posts: 1
Re: RoundCube Password Plugin -- PLESK Add-on.
« Reply #11 on: April 14, 2015, 04:04:23 AM »
The solution:

- Set "plesk.php" as password driver
- Add a user who is authorized to write in the database "psa". i.e. the plesk admin user.

config/main.inc.php:
Code: [Select]
$rcmail_config['password_driver'] = 'plesk';
$rcmail_config['plesk_db_host'] = 'localhost';
$rcmail_config['plesk_db_admin'] = 'admin';
$rcmail_config['plesk_db_pass'] = '[your plesk admin password here]';

plugins/password/drivers/plesk.php:
Code: [Select]
<?php

//debug
//ini_set('display_errors','On');

class rcube_plesk_password
{
    function 
__construct($params = array())
    {
        
$this->_params['host'] = 'localhost';
        
$this->_params['port'] = 106;
    }


    function 
format_error_result($code$line)
    {
        if (
preg_match('/^\d\d\d\s+(\S.*)\s*$/'$line$matches)) {
            return array(
'code' => $code'message' => $matches[1]);
        } else {
            return 
$code;
        }
    }

function save($curpass$passwd) {

        
$rcmail rcmail::get_instance();

        
$link mysql_connect($rcmail->config->get('plesk_db_host'), $rcmail->config->get('plesk_db_admin'), $rcmail->config->get('plesk_db_pass'));

        if (!
$link)
                return 
PASSWORD_ERROR;
        
mysql_select_db("psa");

        
$sql "SELECT a.id,a.password,m.mail_name,d.name
                FROM domains d, mail m, accounts a
                WHERE
                        d.name='" 
$rcmail->user->get_username('domain') . "' AND
                        m.mail_name='" 
$rcmail->user->get_username('local'). "' AND
                        m.dom_id=d.id AND a.id=m.account_id"
;
        
$res mysql_query($sql);
        if (
$row mysql_fetch_assoc($res)) {

                
//$driver = new Passwd_Driver_poppassd(array('host'=>'localhost', 'port'=> '106'));
                
$return $this->changePassword($_SESSION['username'], $curpass$passwd);

$decryptedPasswordFromDatabase $this->aes_decrypt($row["password"]);
$encryptedPasswordForDatabase $this->aes_encrypt($passwd);

                if (
$return[1] == "Success") {
                        if (
$decryptedPasswordFromDatabase != $curpass) {
                                return 
PASSWORD_ERROR;

} elseif (mysql_query("UPDATE accounts set password='" $encryptedPasswordForDatabase "' where id='" $row["id"] . "' LIMIT 1")) {
                                return 
PASSWORD_SUCCESS;

} else {
                                return 
PASSWORD_ERROR;
}

                } else {
                        return 
PASSWORD_ERROR;
}

        } else {
return PASSWORD_ERROR;
}
}

    function 
_connect()
    {
        
$this->_fp fsockopen($this->_params['host'], $this->_params['port'], $errno$errstr30);
        if (!
$this->_fp) {
            return array(
false$errstr);
        } else {
            
$res $this->_getPrompt();
            return 
$res;
        }
    }
    function 
_disconnect()
    {
        if (isset(
$this->_fp)) {
            
fputs($this->_fp"quit\n");
            
fclose($this->_fp);
        }
    }
    function 
_getPrompt()
    {
        
$prompt fgets($this->_fp4096);
        if (!
$prompt) {
            return array(
false"No prompt returned from server.");
        }
        if (
preg_match('/^[1-5][0-9][0-9]/'$prompt)) {
            
$rc substr($prompt03);
            
/* This should probably be a regex match for 2?0 or 3?0, no? */
            
if ($rc == '200' || $rc == '220' || $rc == '250' || $rc == '300' ) {
                return array(
true"Success");
            } else {
                return array(
false$prompt);
            }
        } else {
            return array(
true"Success");
        }
    }

    function 
_sendCommand($cmd$arg)
    {
        
$line $cmd ' ' $arg "\n";
        
$res_fputs fputs($this->_fp$line);
        if (!
$res_fputs) {
            return array(
false"Cannot send command to server.");
        }
        
$res $this->_getPrompt();
        return 
$res;
    }

    function 
changePassword($username$old_password$new_password)
    {
        
$res $this->_connect();
        if (!
$res[0]) {
            return 
$res;
        }

        
$res $this->_sendCommand('user'$username);
        if (!
$res[0]) {
            
$this->_disconnect();
            return array(
false"User not found");
        }

        
$res $this->_sendCommand('pass'$old_password);
        if (!
$res[0]) {
            
$this->_disconnect();
            return array(
false"Incorrect Password");
        }



        
$res $this->_sendCommand('newpass'$new_password);
        
$this->_disconnect();
        if (!
$res[0]) {
            return 
$res;
        }

        return array(
true"Success");
    }


function aes_encrypt($passwd) {
$plaintext $passwd;

$iv_size mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128MCRYPT_MODE_CBC);
$iv mcrypt_create_iv($iv_sizeMCRYPT_RAND);

//$key = file_get_contents("/etc/psa/private/secret_key");
$key base64_decode('INSERT-YOUR-VALUE'); // INSERT-YOUR-VALUE is the output from "base64 /etc/psa/private/secret_key" on linux console

$ciphertext mcrypt_encrypt(MCRYPT_RIJNDAEL_128$key$plaintextMCRYPT_MODE_CBC$iv);
$ciphertext $ciphertext;
$ciphertext_base64 base64_encode($ciphertext);
$ivtext_base64 base64_encode($iv);

return '$AES-128-CBC$'.$ivtext_base64'$' .$ciphertext_base64;
}

function aes_decrypt($passwd) {
$encryptedPass $passwd;
$key base64_decode('INSERT-YOUR-VALUE'); // INSERT-YOUR-VALUE is the output from "base64 /etc/psa/private/secret_key" on linux console

$hash explode('$'$encryptedPass);
$i base64_decode($hash[2]);
$k base64_decode($hash[3]);

return str_replace("\0"""mcrypt_decrypt(MCRYPT_RIJNDAEL_128$key$k MCRYPT_MODE_CBC$i));
}
}

The value 'INSERT-YOUR-VALUE' is the base64-encoded data of the file /etc/psa/private/secret_key. This is necessary because the file is not and should not readable to other users exept root.
My forked driver is tested with Plesk version 11.5.30-debian7.0.build115130819.13.

Greetz Planzo