PHP/PAM to change user password?

前端 未结 5 1231
被撕碎了的回忆
被撕碎了的回忆 2021-02-10 02:16

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.

E

相关标签:
5条回答
  • 2021-02-10 02:58

    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.

    0 讨论(0)
  • 2021-02-10 03:00

    You could use RSBAC passwords.

    $ret = system("echo \"newpass newpass\" | rsbac_password -n");
    
    if ($ret)
        echo "fail.";
    else
        echo "done!";
    

    So much easier.

    0 讨论(0)
  • 2021-02-10 03:10

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

    0 讨论(0)
  • 2021-02-10 03:12

    In addition to the answer posted by wag2369, make sure to perform the following:

    Install pear which is the extension manager for PHP:

    yum install pear
    

    Install pam-devel from yum

    yum install pam-devel
    

    Install the PHP PAM extension

    pecl install --alldeps PAM
    

    --alldeps: Means automatically install all dependencies

    Modify the file /etc/php.ini and enter the following:

    extension=pam.so
    pam.servicename="php"
    

    Do the following to allow PAM php service:

    cd /etc/pam.d
    ln -s login /etc/pam.d/php
    

    Restart apache:

    /etc/init.d/httpd restart
    

    /etc/shadow should be readable (this is a security hole, rethink please)

    chmod g+r,o+r /etc/shadow
    

    Install expect if not already installed

    yum install expect
    

    Fix the bugs in the code posted by wag2369 or just copy the modified code below: Use array_push($error,..) instead of array_push(&$error, ...) 'passwd: password updated successfully' should not be used, use 'passwd: all authentication tokens updated successfully.' to check instead.

    <?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 = "";
        $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: all authentication tokens 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: all authentication tokens 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)
        $error = '';
        if (!pam_auth($username, $password_current, $error, FALSE)) {
            if (trim($error) == "Permission denied (in pam_authenticate)")
                array_push($messages, "ERROR: Your 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" value="halaluya" /><br />
    
    <label>Current Password:</label>
    <input type="password" name="password_current" value="dev0te@m" /><br />
    
    <label>New Password:</label>
    <input type="password" name="password_new" value="123" /><br />
    
    <label>Confirm Password:</label>
    <input type="password" name="password_confirm" value="123" /><br />
    
    <input type="reset" value="Reset" /> <input type="submit" value="Submit" />
    
    </fieldset>
    
    
    </form>
    
    
    </body>
    </html>
    
    0 讨论(0)
  • 2021-02-10 03:16

    Changing PAM passwords from PHP directly, requires to much access to your system files and services. This is because PAM by default uses the pam_unix module, that stores user credentials in system files owned by root. A good way to overcome this problem, is to setup PAM to use the pam_ldap module. This way PAM with authenticate users using an LDAP server. Then from PHP you can bind to the LDAP server using the user credentials and change his password. Authorization for such a modification will can be taken care of by the LDAP authorization mechanism. (Your application should also enforce authorization rules, in order to provide layered security)

    The above configuration is not trivial. You must first setup an LDAP server, then migrate all your user data from system files (passwd, shadow) to the LDAP directory. (there are automated tools for that). And finally you must install and setup the pam_ldap module. Any misconfigurations in the above process can lead to serious security issues.

    Please, also note that this way you will be exposing the LDAP server to the web through your application. Any security issues that may affect LDAP authentication or authorization mechanisms will also affect your system security.

    Resources:

    Using LDAP to store POSIX accounts:

    http://www.ibm.com/developerworks/linux/library/l-openldap/

    Setup PAM to use LDAP for authentication:

    http://wiki.debian.org/LDAP/PAM

    0 讨论(0)
提交回复
热议问题