Я пытаюсь написать какой-нибудь сервер, который проверяет подлинность клиентов с помощью Linux PAM. Я написал следующий класс:
class Pam
{
public:
Pam(const char *module, const char *username)
{
mConv.appdata_ptr = nullptr;
mConv.conv = &convCallback;
const int res = pam_start("system-auth", username, &mConv, &mPamHandle);
if (res != PAM_SUCCESS)
throw std::runtime_error("Failed to initialize PAM");
}
bool authenticate(char *passwd)
{
pam_response *resp = static_cast<pam_response*>(malloc(sizeof(pam_response)));
resp->resp = passwd;
resp->resp_retcode = 0;
mConv.appdata_ptr = resp;
const int res = pam_authenticate(mPamHandle, 0);
log(res);
return res == PAM_SUCCESS;
}
~Pam()
{
if (mPamHandle)
pam_end(mPamHandle, PAM_SUCCESS);
mPamHandle = nullptr;
}
private:
static int convCallback (int msgId, const pam_message **msg, pam_response **resp, void *appData)
{
*resp = static_cast<pam_response*>(appData);
return PAM_SUCCESS;
}
private:
pam_handle_t *mPamHandle = nullptr;
pam_conv mConv;
};
Который затем используется как:
Pam pam("system-auth", username);
if (pam.authenticate(passwd))
return true;
// error handling code here
Я обнаружил, что pam_authenticate возвращает PAM_AUTHTOK_RECOVERY_ERR для действительного пользователя / пароля. Возможные возвращаемые значения описаны в справочной странице и на linux-pam.org. http://www.linux-pam.org/Linux-PAM-html/adg-interface-by-app-expected.html#adg-pam_authenticate не содержат это значение вообще. Документация говорит, что это может быть возвращено pam_chauthtok, и это означает:
PAM_AUTHTOK_RECOVERY_ERR
Модуль не смог получить старый токен аутентификации.
И до сих пор неясно, что это значит в случае аутентификации. Я пытался запустить код как обычного пользователя, так и root, результат был одинаковым.
То, что происходит, это то, что вы видите 0
как значение appData
в convCallback
, откуда исходит ошибка — данные ответа пустые, что означает плохой разговор, который вызывает PAM_AUTHTOK_RECOVERY_ERR
возвращаемое значение Это основано на чтении support.c
файл в текущем коде для исходного кода PAM-Linux.
Хорошо, пару вопросов.
Вы не можете переназначить значение диалога appdata_ptr после инициализации — значение указателя следует считать константой после вызова pam_start
, Вы должны передать туда значение, которое никогда не изменится. Если бы вы проверили функцию разговора, вы бы заметили, что значение appData
является 0
,
Вы должен Предположим, что значение, вводимое в ответ, принадлежит вызывающей подпрограмме, т. е. вам нужно будет ввести строку пароля (со всем злом, связанным с этим).
Имея это в виду, я немного изменил ваш код следующим образом, который должен решить ваши проблемы (опять же, это упрощенный код):
class Pam
{
public:
Pam(const char *module, const char *username)
{
mConv.appdata_ptr = (void *)(this);
mConv.conv = &convCallback;
const int res = pam_start(module, username, &mConv, &mPamHandle);
if (res != PAM_SUCCESS)
throw std::runtime_error("Failed to initialize PAM");
}
bool authenticate(char *passwd)
{
mPassword = passwd;
const int res = pam_authenticate(mPamHandle, 0);
log(res);
return res == PAM_SUCCESS;
}
~Pam()
{
if (mPamHandle)
pam_end(mPamHandle, PAM_SUCCESS);
mPamHandle = 0;
}
private:
static int convCallback (int msgId, const pam_message **msg, pam_response **resp, void *appData)
{
Pam *me = static_cast<Pam *>(appData);
pam_response *reply = static_cast<pam_response *>(calloc(1, sizeof(pam_response)));
reply->resp = strdup(me->mPassword);
reply->resp_retcode = 0;
*resp = reply;
return PAM_SUCCESS;
}
private:
pam_handle_t *mPamHandle = 0;
pam_conv mConv;
const char *mPassword = 0;
};