Как обработать возвращаемое значение PAM_AUTHTOK_RECOVERY_ERR от pam_authenticate для действительного пользователя и пароля?

0

Я пытаюсь написать какой-то сервер, который проверяет подлинность клиентов с помощью 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 do не содержат этого значения вообще. Документация говорит, что она может быть возвращена pam_chauthtok, и это означает:

PAM_AUTHTOK_RECOVERY_ERR

Модуль не смог получить старый токен аутентификации.

И еще неясно, что это означает в случае аутентификации. Я пытался запустить код как обычного пользователя, а как root, результат был таким же.

  • 0
    Вам нужно будет перечислить свой стек конфигурации PAM, но похоже, что модуль pam_unix имеет параметр use_first_pass установленный вместо try_first_pass для аутентификации, и это первая запись в стеке PAM.
  • 0
    @Petesh /etc/pam.d/system-auth содержит следующие строки: auth required pam_unix.so try_first_pass nullok и password required pam_unix.so try_first_pass nullok sha512 shadow так что похоже, что try_first_pass используется по умолчанию
Показать ещё 1 комментарий
Теги:
pam

1 ответ

1
Лучший ответ

Что происходит, так это то, что вы видите 0 как значение appData в convCallback, откуда исходит ошибка - данные ответа пустые, что означает "плохой разговор", что приводит к возврату PAM_AUTHTOK_RECOVERY_ERR. Это основано на чтении файла support.c в текущем коде для исходного кода PAM-Linux.

Хорошо, пару вопросов.

  1. После инициализации нельзя переназначить значение appdata_ptr для разговора - значение указателя следует считать константой после вызова pam_start. Вы должны передать туда значение, которое никогда не изменится. Если вы проверили функцию разговора, вы заметили бы, что значение appData равно 0.

  2. Вы должны предположить, что значение, которое помещается в ответ, принадлежит вызывающей процедуре, то есть вам придется strdup строку пароля (со всем злом, которое связано с этим).

Учитывая оба эти соображения, я немного изменил ваш код на следующее, что должно решить ваши проблемы (опять же, это упрощенный код):

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;
};

Ещё вопросы

Сообщество Overcoder
Наверх
Меню