PHP/PAM для изменения пароля пользователя?

Существуют ли какие-либо рабочие пакеты для изменения паролей пользователей Linux с помощью PHP?

Я пытался использовать PECL:PAM, но при попытке изменить пароль возникает ошибка.

Редактировать:

PHP-код:

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

Вывод PHP (эхо):

Permission denied (in pam_authenticate)

Из /var/log/auth (на самом деле они были раньше, журнал, похоже, не работает с банкоматом по какой-то причине, которую еще предстоит определить):

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

Другой:

Извините за отсутствие подробностей раньше, я очень устал, когда задавал вопрос, но это все равно дерьмовое оправдание.


person wag2639    schedule 13.06.2010    source источник
comment
возникает ошибка при попытке изменить пароль - какая ошибка? Вы только что пошли в кабинет врача и сказали ему, что это больно, но не сказали ему, где.   -  person Charles    schedule 14.06.2010


Ответы (5)


После нескольких часов поиска в Интернете я не смог найти супер хороший вариант, поэтому я реализовал этот хак. Он использует эту статью для смены паролей с помощью PHP.

Я также использую пакет PECL:PAM, чтобы добавить небольшую проверку.

Эта страница находится в защищенной папке 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

person wag2639    schedule 18.06.2010
comment
Пожалуйста, прочитайте мои примечания ниже, чтобы иметь возможность правильно настроить PAM с PHP и справиться со всеми проблемами, которые могут возникнуть при попытке запустить приведенный выше код. - person Basil Musa; 02.03.2011
comment
Обе версии сценария требуют, чтобы у пользователей, чьи пароли должны быть изменены, были действительные оболочки. Из-за использования su скрипты не работают для пользователей с отключенными оболочками. - person xebeche; 21.11.2013

В дополнение к ответу, опубликованному wag2369, обязательно выполните следующее:

Установите pear, который является менеджером расширений для PHP:

yum install pear

Установите pam-devel из yum

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

Перезапустите апач:

/etc/init.d/httpd restart

/etc/shadow должен быть доступен для чтения (это дыра в безопасности, переосмыслите, пожалуйста)

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

Установить ожидаем, если он еще не установлен

yum install expect

Исправьте ошибки в коде, опубликованном wag2369, или просто скопируйте измененный код ниже: Используйте array_push($error,..) вместо array_push(&$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>
person Community    schedule 02.03.2011
comment
+1 за настройку php.ini и chmod, эта статья - самая подробная из тех, что я искал в Интернете. - person J.C; 11.05.2017

Изменение паролей PAM напрямую из PHP требует большого доступа к вашим системным файлам и службам. Это связано с тем, что PAM по умолчанию использует модуль pam_unix, который хранит учетные данные пользователя в системных файлах, принадлежащих пользователю root. Хороший способ решить эту проблему — настроить PAM на использование модуля pam_ldap. Таким образом, PAM аутентифицирует пользователей с помощью сервера LDAP. Затем из PHP вы можете привязаться к серверу LDAP, используя учетные данные пользователя, и изменить его пароль. Авторизация для такой модификации может быть обеспечена механизмом авторизации LDAP. (Ваше приложение также должно применять правила авторизации, чтобы обеспечить многоуровневую безопасность)

Приведенная выше конфигурация не является тривиальной. Сначала вы должны настроить сервер LDAP, а затем перенести все свои пользовательские данные из системных файлов (passwd, shadow) в каталог LDAP. (для этого есть автоматизированные инструменты). И, наконец, вы должны установить и настроить модуль pam_ldap. Любые неправильные настройки в описанном выше процессе могут привести к серьезным проблемам с безопасностью.

Также обратите внимание, что таким образом вы будете предоставлять доступ к серверу LDAP из Интернета через свое приложение. Любые проблемы безопасности, которые могут повлиять на механизмы аутентификации или авторизации LDAP, также повлияют на безопасность вашей системы.

Ресурсы:

Использование LDAP для хранения учетных записей POSIX:

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

Настройте PAM для использования LDAP для аутентификации:

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

person Charalampos Serenis    schedule 19.05.2012
comment
Вы упомянули, что существуют автоматизированные инструменты для перехода на LDAP. У вас есть ссылки на них? - person Azmisov; 09.08.2014

Существуют ли какие-либо рабочие пакеты для изменения паролей пользователей Linux с помощью PHP?

Это действительно очень опасно. Предполагая, что вы понимаете риски, вы поймете, что вам необходимо создать ряд ограничений, прежде чем применять изменение, которое должно быть реализовано на уровне привилегий, позволяющем изменять пароли, т.е. код для запуска должен быть автономным исполняемым файлом с либо setuid executoin, либо вызывается через sudo из вашего php-кода.

Конечно, нет никакой причины, по которой отдельный код нельзя было бы написать на PHP, кроме того факта, что (по крайней мере, в последний раз, когда я смотрел на это) привязки PAM в PHP были довольно незрелыми,

Возможно, вы захотите взглянуть на программу chpasswd (доступную в Redhat и некоторых других дистрибутивах) или использовать proc_open('/usr/bin/passwd'... и правильно читать запросы и отвечать на них.

ХТН

C.

person symcbean    schedule 13.06.2010
comment
Я искал что-то, чтобы пользователь мог изменить свой пароль. Разве это не использование разрешений этого собственного пользователя для попытки изменить логин и пароль? - person wag2639; 14.06.2010
comment
Эрк! Если вы не понимаете, почему это не работает, то вам, вероятно, не стоит возиться с этим, пока вы не прочитаете намного больше о том, как аутентификация и разрешения работают в системе posix. И вы НЕ ДОЛЖНЫ разрешать веб-форме привилегии, необходимые для смены паролей. Ребята, которые написали webmin, являются компетентными программистами, и они несколько раз ошибались. Только root может изменить чужой пароль. Чтобы изменить свой собственный пароль, ему все равно нужно начать как root, а затем seteuid(). - person symcbean; 14.06.2010
comment
Тогда, думаю, я выберу proc_open. Я просто хотел проверить получше. - person wag2639; 15.06.2010
comment
Но я до сих пор не понимаю, почему proc_open лучше, чем привязка PAM. - person wag2639; 15.06.2010
comment
Не могли бы вы пояснить, как будет работать proc_open? Я не могу понять его часть каналов, так как passwd не использует стандартный ввод или стандартный вывод. - person wag2639; 18.06.2010
comment
passwd не использует стандартный ввод или стандартный вывод — кто вам это сказал? - person symcbean; 18.06.2010

Вы можете использовать пароли RSBAC.

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

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

Так намного проще.

person joly    schedule 19.07.2010