Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

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
    
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
add comment

5 Answers

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
add comment

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: http://www.samba.org/samba/docs/using_samba/ch09.html#samba2-CHP-9-SECT-4.3

share|improve this answer
add comment

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
add comment
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)

<?php

$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!";
        exit();
        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.");    
        else
            array_push(&$messages, "ERROR: " . $error);
        return FALSE;
    }

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

}

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


?><html>
<head>


<title>Passwords</title>

<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 {
    color:#C00;
}


</style>

</head>


<body>

<h2>Change Passwords</h2>

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

<fieldset>

<? 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" />

</fieldset>


</form>


</body>
</html>

I also have this question/answer posted in http://stackoverflow.com/questions/3032785/php-pam-to-change-user-password/3067974#3067974

share|improve this answer
add comment

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: http://sylnsr.blogspot.com/2012/09/keep-unix-password-in-sync-with.html

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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