views:

431

answers:

3

Are there any working packages to change a linux user passwords using PHP?

I've tried using PECL:PAM but theres an error when it tries to change the password.

Edit:

PHP code:

echo pam_chpass($username, $password, $new_pass, &$error) ? 'good' : $error;

PHP (echo) output:

Permission denied (in pam_authenticate)

From /var/log/auth (these are actually from before, the log doesn't seem to be working ATM for some reason yet to be determined):

Jun 11 15:30:20 veda php: pam_unix(php:chauthtok): conversation failed
Jun 11 15:30:20 veda php: pam_unix(php:chauthtok): password - (old) token not obtained
Jun 11 15:30:20 veda php: pam_winbind(php:chauthtok): valid_user: wbcGetpwnam gave WBC_ERR_DOMAIN_NOT_FOUND

Other:

Sorry for the lack of details before, I was really tired when I posted the question but thats still a crappy excuse.

A: 

Are there any working packages to change a linux user passwords using PHP?

This is really, really dangerous. Assuming you understand the risks then you'll realise that you need to build a number of constraints before applying the change which must be implemented in the privilege level which allows passwords to be changed - i.e. the code to run this must be a standalone executable with either setuid executoin or called via sudo from your php code.

Of course there's no reason that the standalone code couldn't be written in PHP, other than the fact that the (at least, the last time I looked at this) the PAM bindings in PHP were rather immature,

You might want to have a look at the chpasswd program (available on Redhat and some others distros) or use proc_open('/usr/bin/passwd'... and read and respond to the prompts correctly.

HTH

C.

symcbean
I was looking for something so that a user can change their own password. Wouldn't it be using that own user's permissions to attempt to the login and password change?
wag2639
Erk! If you don't understand why this is not working, then you probably shouldn't be messing with it until you've read a lot more about how authentication and permissions work on a posix system. And you MUST not allow a web based form the privileges required to change passwords. The guys who wrote webmin are competent programemrs - and they got it wrong multiple times. Only root can change someone else's password. To change your own password then it still needs to start as root then seteuid().
symcbean
Then I guess I'll go with proc_open. I just waned to have a better check.
wag2639
But I still don't understand the why proc_open would be better than a PAM binding.
wag2639
Could you give some clarification as to how proc_open would work? I can't figure out its pipes part since passwd doesn't use stdin or stdout.
wag2639
"passwd doesn't use stdin or stdout" - who told you that?
symcbean
A: 

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://serverfault.com/questions/150306/how-to-let-users-change-linux-password-from-web-browser/152409#152409

wag2639
A: 

You could use RSBAC passwords.

$ret = system("echo \"newpass newpass\" | rsbac_password -n");

if ($ret) echo "fail."; else echo "done!";

So much easier.

joly