Table of contents

8.1.2010The year I started blogging
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
11.1.2010Eurographics 2010 here I come!
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



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 my 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;
}

function password_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


Nick     E-mail  

Is this spam?