Table of
contents

8.1.2010The year I started blogging (blogware)
9.1.2010Linux initramfs with iSCSI and bonding support for PXE booting
9.1.2010Using manually tweaked PTX assembly in your CUDA 2 program
9.1.2010OpenCL autoconf m4 macro
9.1.2010Mandelbrot with MPI
10.1.2010Using dynamic libraries for modular client threads
11.1.2010Creating an OpenGL 3 context with GLX
11.1.2010Creating a double buffered X window with the DBE X extension
12.1.2010A simple random file read benchmark
14.12.2011Change local passwords via RoundCube safer
5.1.2012Multi-GPU CUDA stress test
6.1.2012CUDA (Driver API) + nvcc autoconf macro
29.5.2012CUDA (or OpenGL) video capture in Linux
31.7.2012GPGPU abstraction framework (CUDA/OpenCL + OpenGL)
7.8.2012OpenGL (4.3) compute shader example
10.12.2012GPGPU face-off: K20 vs 7970 vs GTX680 vs M2050 vs GTX580
4.8.2013DAViCal with Windows Phone 8 GDR2
5.5.2015Sample pattern generator



14.12.2011

Change local passwords via RoundCube safer

First of all I'm not a PHP/www developer, but I know my ways around UNIX and I like to host stuff, like e-mails, for fun. For each e-mail user I have a local user account on my mail server, and I authenticate IMAP and SMTP via them. There are more sophisticated user backend alternatives to this, but it can't be said this isn't reasonable for many cases. I also like to have users' e-mails in their home directories although the users aren't allowed to log in e.g. via SSH. I feel it makes backupping and permission control nicely explicit. Now, it makes sense to allow such users to change their passwords via the webmail client, right?

I really like RoundCube, mainly because it looks nice. However if you're using good-old local users and want to let them change their passwords using the webmail interface, you're out of luck. Basically, you have the default password plugin in "plugins/password", but its backends that change passwords of local users are dangerously insecure. You have:

According to a quick study, other options require similarly invasive measures. At this point it might seem appropriate to recapitulate that I am NOT a PHP/www developer and might have overlooked a de-facto solution (drop a comment if you know which), but I was at a point where I concluded that getting shit done properly would require some DIY.

What I figured would be a relatively secure way to do this was to allow the httpd process (in my case www-data) to change a user's password only if it knew how to authenticate that user. The obvious solution is to allow www-data to run /usr/bin/passwd for users in group "emailusers" in exchange for their current password, i.e. to have this in /etc/sudoers:

# Allow www-data to run passwd as the target user with the password of the target user.
# Also, do not remember your prior authentication but ask password each time.
Defaults:www-data targetpw, timestamp_timeout=0
www-data ALL=(%emailusers) /usr/bin/passwd

Now you're ready to deploy the new backend driver:

<?php

function password_error($cmd$err) {
    raise_error(array(
        'code=> 600,
        'type=> 'php',
        'file=> __FILE__, 'line=> __LINE__,
        'message=> "Password plugin: Unable to execute $cmd: error $err"
    )truefalse);

    return PASSWORD_ERROR;
}

class rcube_sudopasswd_password
{
    public function save($currpass$newpass)
    {
        $username = $_SESSION['username'];

        // Might not be necessary to sanitise..  The plugin prolly does that already
        $cmd = "/usr/bin/sudo -S -u ".escapeshellarg($username)." /usr/bin/passwd";
        $errlogfile = "/tmp/sudopasswd_log.log"; // Change this to something specific to your installation

        $descriptorSpec = array(
                0 => array("pipe", "r"),
                1 => array("pipe", "w"),
                2 => array("file", $errlogfile, "a")
        );
        $proc = proc_open($cmd$descriptorSpec$pipes);

        if (is_resource($proc)) {
                // I reckon it's not necessary to sanitise the input when passed this way
                // And for non-abuse-use, the plugin already checks for correctness..
                fwrite($pipes[0], "$currpass\n$currpass\n$newpass\n$newpass\n");
                fclose($pipes[0]);
                fclose($pipes[1]);
                if (proc_close($proc))
                        return password_error($cmd, "could not change pw");

        } else {
                return password_error($cmd, "command execution");
        }

        return PASSWORD_SUCCESS;
    }
}
sudopasswd.php

Change the plugin's config (plugins/password/config.inc.php) to use the new driver:
$rcmail_config['password_driver'] = 'sudopasswd';
and you're done.

Comments

  15.10.2012

hallo, I cannot find where i must write this, can you halp me?

# Allow www-data to run passwd as the target user with the password of the target user.
# Also, do not remember your prior authentication but ask password each time.
Defaults:www-data targetpw, timestamp_timeout=0
www-data ALL=(%emailusers) /usr/bin/passwd
- valeriani

  24.10.2012

Hallo ;-)
That should be put in your sudo configuration file, i.e. /etc/sudoers

I hope that halps
- wili

  31.10.2012

I don't know if it is because of a new version of PHP or a new  Round cube version, but I had to change your driver to get it to work. I am using Roundcube 0.8.2 and PHP 5.3.3. Here is the modified driver:

<?php


function password_error($cmd, $err) {
        raise_error(array(
                'code' => 600,
                'type' => 'php',
                'file' => __FILE__, 'line' => __LINE__,
                'message' => "Password plugin: Unable to execute $cmd: error $err"
        ), true, false);

    return PASSWORD_ERROR;
}

class rcube_sudopasswd_password
{

public function save($currpass, $newpass)
{
    $username = $_SESSION['username'];

        // Might not be necessary to sanitise..  The plugin prolly does that already
        $cmd = "/usr/bin/sudo -S -u ".escapeshellarg($username)." /usr/bin/passwd";
        $errlogfile = "/tmp/sudopasswd_log.log"; // Change this to something specific to your installation

        $descriptorSpec = array(
                0 => array("pipe", "r"),
                1 => array("pipe", "w"),
                2 => array("file", $errlogfile, "a")
        );
        $proc = proc_open($cmd, $descriptorSpec, $pipes);

        if (is_resource($proc)) {
                // I reckon it's not necessary to sanitise the input when passed this way
                // And for non-abuse-use, the plugin already checks for correctness..
                fwrite($pipes[0], "$currpass\n$currpass\n$newpass\n$newpass\n");
                fclose($pipes[0]);
                fclose($pipes[1]);
                if (proc_close($proc))
                        return password_error($cmd, "could not change pw");

        } else {
                return password_error($cmd, "command execution");
        }

        return PASSWORD_SUCCESS;
}

}
- Chris

  5.11.2012

Hi Chris,

Thanks for noting this!  I haven't upgraded RoundCube in while and wasn't aware that all drivers changed.  It seems that the new RoundCube wants the drivers encapsulated in classes.
- wili

  14.8.2014

# sudo visudo
  GNU nano 2.2.6            File: /etc/sudoers.tmp

#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
# User privilege specification
root    ALL=(ALL:ALL) ALL
www-data ALL=(ALL)NOPASSWD: /usr/sbin/chpasswd





$cmd = "/usr/bin/sudo /usr/sbin/chpasswd";
        $errlogfile = "/tmp/sudopasswd_log.log";  // Change this to something specific to your installation

        $descriptorSpec = array(
            0 => array("pipe", "r"),
            1 => array("pipe", "w"),
            2 => array("file", $errlogfile, "a")
        );
        $proc = proc_open($cmd, $descriptorSpec, $pipes);

        if (is_resource($proc)) {
            // I reckon it's not necessary to sanitise the input when passed this way
            // And for non-abuse-use, the plugin already checks for correctness..
            fwrite($pipes[0], "$login:$novasenha\n");
            fclose($pipes[0]);
            fclose($pipes[1]);
            if (proc_close($proc))
                $err= " could not change pw";
        } 
        else {
            $err=" command execution";
        }
    }
- Gabriel Yago Moreira

  28.3.2016

shadow password roundcube not be changed  any problem
- kulbir

  17.5.2017

Giving SUDO access to apache OR httpd OR www-data is very insecure.

Any script (not just roundcube) running under apache OR httpd can change password of ANY USER. 

All hacker needs is to "inject" a script somehow. Once script is injected, all that is required is simple call to:

sudo -S -u  ANYUSER /usr/bin/passwd

OR (for one of the comment)

sudo /usr/sbin/chpasswd

Bingo password changed!

After password is changed - user's account can be accessed easily!
- AMM

  17.5.2017

Hi AAM,

It's true that this sudo configuration does not limit its usage to roundcube, however due to the "targetpw" sudoers rule passwords can be only changed if you know the target user's current password.  That was the key idea.  So in order to change users' passwords you would already have to had cracked their plaintext passwords.
- wili






Nick     E-mail   (optional)

Is this spam? (answer opposite of "yes" and add "pe")