Server Fault is a question and answer site for system and network administrators. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm not sure if this is a stackoverflow question or serverfault but here goes:

I have an Ubuntu 10.04 file server (Samba/FTP/HTTP) and I would like to have the ability to give users the ability to change their password to the server using their web browser.

I've written a similar script before using PHP and a mess of exec but I believe that isn't secure because it can be listened to by someone looking at the list of processes on the server.

Is there some kind of plugin (PHP or Python or other) that can do this easily?

I rather not use something like webmin/usermin as it's overkill for this.

share|improve this question

closed as off-topic by HopelessN00b Apr 3 '15 at 13:40

  • This question does not appear to be about server, networking, or related infrastructure administration within the scope defined in the help center.
If this question can be reworded to fit the rules in the help center, please edit the question.

If you do not want exec() arguments to be visible in process list, do not put them in exec(). Use popen() with stdin/stdout. – grawity Jun 11 '10 at 10:55
@grawity, is proc_open() safe? – wag2639 Jun 11 '10 at 16:07
It depends on your definition of "safe". But yes, it is safer than exec(), because nobody (except for the open'ed program itself) can see what's written to stdin – grawity Jun 11 '10 at 18:28

web-chpass can change passwords through PAM.

share|improve this answer
This looked very promising, but I don't really know how to use Perl-CGI and it looks a bit dated. – wag2639 Jun 18 '10 at 7:54
@wag2639: I don't think "dated" is a problem; the PAM interface is still the same. – grawity Jun 18 '10 at 11:10

If you are running Samba as a PDC you could use it to allow users to change their password using ctrl+alt+delete. I am sorry if the this anwser does not suit you but i cannot post commets or so yet...

    unix password sync = Yes

    passwd program = /usr/bin/php -f /my_folder/my_own_script.php %u
    passwd chat = "password:" %n\n "changed"
    passwd chat debug = yes

or the system program:

passwd program = /usr/bin/passwd %u
passwd chat = *New*UNIX*password* %n\n *ReType*new*UNIX*password* %n\n  *passwd:*all*authentication*tokens*updated*successfully*

search for "Password Synchronization" it is by the middle of the document and explains better how it works:

share|improve this answer

Probably the best solution would be to use some kind of LDAP interface (eg. from the programming framework), or out of the box frontend, like Webmin.

share|improve this answer
I wanted to use usermin, but it wouldn't work and I don't know how to diagnose. Are there any alternatives? – wag2639 Jun 18 '10 at 3:23
up vote 1 down vote accepted

After hours of research online, I wasn't able to find a super good option so I'm implemented this hack. It uses this article for changing passwords using PHP.

I'm also using the PECL:PAM package to add a little verification.

This page is on a secure HTTPS folder (automatic redirect via .htaccess)


$messages = array();

function change_password ($user, $currpwd, $newpwd) {

    // Open a handle to expect in write mode
    $p = popen('/usr/bin/expect','w');

    // Log conversation for verification
    $log = '/tmp/passwd_' . md5($user . time());
    $cmd .= "log_file -a \"$log\"; ";

    // Spawn a shell as $user
    $cmd .= "spawn /bin/su $user; ";
    $cmd .= "expect \"Password:\"; ";
    $cmd .= "send \"$currpwd\\r\"; ";
    $cmd .= "expect \"$user@\"; ";

    // Change the unix password
    $cmd .= "send \"/usr/bin/passwd\\r\"; ";
    $cmd .= "expect \"(current) UNIX password:\"; ";
    $cmd .= "send \"$currpwd\\r\"; ";
    $cmd .= "expect \"Enter new UNIX password:\"; ";
    $cmd .= "send \"$newpwd\\r\"; ";
    $cmd .= "expect \"Retype new UNIX password:\"; ";
    $cmd .= "send \"$newpwd\\r\"; ";
    $cmd .= "expect \"passwd: password updated successfully\"; ";

    // Commit the command to expect & close
    fwrite($p, $cmd); pclose ($p);

    // Read & delete the log
    $fp = fopen($log,r);
    $output = fread($fp, 2048);
    fclose($fp); unlink($log);
    $output = explode("\n",$output);

    return (trim($output[count($output)-2]) == 'passwd: password updated successfully') ? true : false;

function process_post() {

    if ((!isset($_SERVER['HTTP_REFERER'])) 
        || (strpos($_SERVER['HTTP_REFERER'], $_SERVER['SCRIPT_NAME']) === FALSE)) {

        echo "GO AWAY!";
        return FALSE;


    global $messages;

    $username           = trim($_POST['username']);
    $password_current   = trim($_POST['password_current']);
    $password_new       = trim($_POST['password_new']);
    $password_confirm   = trim($_POST['password_confirm']);

    // Check for blanks
    if ($username == '' || $password_current == '' || $password_new == '' || $password_confirm == '') {
        array_push(&$messages, "ERROR: You cannot leave any field empty.");
        return FALSE;

    // Check username
    if (!ctype_alnum($username)) {
        array_push(&$messages, "ERROR: You've entered an invalid username.");
        return FALSE;

    // Check to see if new password is correctly typed
    if ($password_new != $password_confirm) {       
        array_push(&$messages, "ERROR: New Password and Confirmation do not match.");
        return FALSE;

    // Check if current password is valid (not really neccessary)
    if (!pam_auth($username, $password_current, &$error, FALSE)) {
        if (trim($error) == "Permission denied (in pam_authenticate)")
            array_push(&$messages, "ERROR: You've username/password was not accepted.");    
            array_push(&$messages, "ERROR: " . $error);
        return FALSE;

    if (change_password ($username, $password_current, $password_new))
        array_push(&$messages, "Password Successfully Changed");
        array_push(&$messages, "ERROR: Password change failed.");


if ($_SERVER['REQUEST_METHOD'] == 'POST') process_post();



<style type="text/css">

body {
    font-family: Verdana, Arial, sans-serif;
    font-size: 12px;

label {
    width: 150px;
    display: block;
    float: left;

input {
    float: left;

br {
    clear: both;

.message {
    font-size: 11px;
    font-weight: bold;

.error {




<h2>Change Passwords</h2>

<form action="<?= $_SERVER['SCRIPT_NAME'] ?>" method="post">


<? if (count($messages) != 0) { 

    foreach ($messages as $message) { ?>

<p class="message<?= ((strpos($message, 'ERROR:') === FALSE) ? '' : ' error') ?>"><?= $message ?></p>

<? } } ?>

<label>Username: </label>
<input type="text" name="username" /><br />

<label>Current Password:</label>
<input type="password" name="password_current" /><br />

<label>New Password:</label>
<input type="password" name="password_new" /><br />

<label>Confirm Password:</label>
<input type="password" name="password_confirm" /><br />

<input type="reset" value="Reset" /> <input type="submit" value="Submit" />




I also have this question/answer posted in

share|improve this answer

I prefer to use 2 separate processes. One process makes a request by dropping a special file into a special folder. Second, a cron job loops through the folder and fulfills the password change requests.

All the calling script needs to do is drop the file into the folder (provided it has permissions to do so.

Please see this link for more details and to get the scripts:

share|improve this answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.