如何处理来自pam_authenticate的PAM_AUTHTOK_RECOVERY_ERR返回值以获取有效的用户和密码?

时间:2014-08-15 08:44:32

标签: c++ linux pam

我正在尝试编写一些使用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身份运行代码,结果是一样的。

1 个答案:

答案 0 :(得分:1)

正在发生的事情是,0appData的值为convCallback,这是错误发生的地方 - 回复数据为空,这意味着错误会话,导致PAM_AUTHTOK_RECOVERY_ERR返回值。这是基于读取PAM-Linux源代码的当前代码中的support.c文件。

好的,有几个问题。

  1. 初始化后无法重新分配对话appdata_ptr值 - 在调用pam_start后,指针的值应视为常量。你应该传递一个永远不会改变的值。如果您选中了对话功能,则会注意到appData的值为0

  2. 这一点)。

  3. 考虑到这两点,我稍微改变了您的代码,以解决您的问题(再次,这是简化的代码):

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