PHP / PAM更改用户密码?

时间:2010-06-13 15:33:42

标签: php linux passwords pam

是否有任何工作包可以使用PHP更改linux用户密码?

我尝试过使用PECL:PAM,但尝试更改密码时出现错误。

编辑:

PHP代码:

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

PHP(echo)输出:

Permission denied (in pam_authenticate)

来自/ var / log / auth(这些实际上来自之前,由于某些原因,日志似乎没有工作ATM):

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

其他:

对于之前缺乏细节感到抱歉,当我发布问题时我真的很累,但这仍然是一个糟糕的借口。

5 个答案:

答案 0 :(得分:2)

在线研究了几个小时后,我无法找到一个超级好的选项,所以我实施了这个黑客。它使用this article来使用PHP更改密码。

我也在使用PECL:PAM package添加一些验证。

此页面位于安全的HTTPS文件夹(通过.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>

我也在https://serverfault.com/questions/150306/how-to-let-users-change-linux-password-from-web-browser/152409#152409

中发布了这个问题/答案

答案 1 :(得分:2)

除了wag2369发布的答案外,请务必执行以下操作:

安装pear,它是PHP的扩展管理器:

yum install pear

从yum安装pam-devel

yum install pam-devel

安装PHP PAM扩展

pecl install --alldeps PAM

- alldeps:表示自动安装所有依赖项

修改文件/etc/php.ini并输入以下内容:

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

执行以下操作以允许PAM php服务:

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

重启apache:

/etc/init.d/httpd restart

/ etc / shadow应该是可读的(这是一个安全漏洞,请重新考虑)

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

如果尚未安装,请安装expect

yum install expect

修复wag2369发布的代码中的错误,或者只复制下面修改后的代码: 使用array_push($ error,..)而不是array_push(&amp; $ error,...) 不应该使用'passwd:密码更新成功',使用 'passwd:所有身份验证令牌都已成功更新。改为检查。

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

答案 2 :(得分:1)

直接从PHP更改PAM密码,需要对系统文件和服务进行大量访问。这是因为PAM默认使用pam_unix模块,该模块将用户凭据存储在root拥有的系统文件中。解决此问题的一种好方法是设置PAM以使用pam_ldap模块。这样PAM就可以使用LDAP服务器对用户进行身份验证。然后从PHP中,您可以使用用户凭据绑定到LDAP服务器并更改其密码。 LDAP授权机制可以处理对此类修改的授权。 (您的应用程序还应强制执行授权规则,以提供分层安全性)

上述配置并非无足轻重。您必须首先设置LDAP服务器,然后将所有用户数据从系统文件(passwd,shadow)迁移到LDAP目录。 (有自动化工具)。最后,您必须安装并设置pam_ldap模块。上述过程中的任何错误配置都可能导致严重的安全问题。

请注意,通过这种方式,您将通过应用程序将LDAP服务器暴露给Web。任何可能影响LDAP身份验证或授权机制的安全问题也会影响您的系统安全性。

资源:

使用LDAP存储POSIX帐户:

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

设置PAM以使用LDAP进行身份验证:

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

答案 3 :(得分:0)

  

是否有任何工作包可以使用PHP更改linux用户密码?

这真的非常危险。假设您了解风险,那么您将意识到在应用必须在允许更改密码的权限级别中实现的更改之前需要构建一些约束 - 即运行此代码的代码必须是独立的可执行文件要么是setuid executoin,要么是通过你的php代码通过sudo调用。

当然没有理由不能用PHP编写独立代码,除了事实上(至少,我最后一次看到这个)PHP中的PAM绑定还不成熟,

您可能希望查看chpasswd程序(可在Redhat和其他一些发行版上获得)或使用proc_open('/ usr / bin / passwd'...并正确读取并响应提示。

HTH

下进行。

答案 4 :(得分:0)

您可以使用RSBAC密码。

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

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

这么容易。