You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在pam_sm_open_session中调用pam_prompt返回会话失败错误的问题

问题分析与解决方案

核心原因

PAM的**会话阶段(pam_sm_open_session)和认证阶段(pam_sm_authenticate)**上下文环境完全不同:

  • 认证阶段是用户交互的核心阶段,PAM服务(如ssh、login)会在此绑定有效的对话函数(PAM_CONV),确保能与用户终端进行输入输出交互。
  • 会话阶段的核心作用是初始化会话资源,多数PAM服务在认证完成后会断开或切换终端交互通道,此时pam_prompt找不到可用的输入输出渠道,因此返回PAM_SESSION_ERR;而PAM_TEXT_INFO虽调用成功,但因无输出渠道导致内容无法显示。

强行在会话阶段调用pam_authenticate或pam_open_session会破坏PAM流程上下文,触发错误是必然结果。

可行解决方案

1. 将交互逻辑移至认证阶段

这是最可靠的方案,认证阶段天然支持用户交互。修改思路:

  • 在pam_sm_authenticate中检查用户会话数与权限,触发pam_prompt让用户选择要关闭的会话。
  • 用户完成选择并清理会话后,再进入会话阶段;若用户选择退出,直接在认证阶段返回错误终止流程。

示例修改后的认证阶段代码片段:

/* PAM entry point for authentication */
DLL_EXPORT int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
    const char *user;
    int res = pam_get_user(pamh, &user, NULL);
    if (res != PAM_SUCCESS || user == NULL)
    {
        return PAM_USER_UNKNOWN;
    }

    // 获取用户会话数与权限
    int num_sessions = get_user_sessions(user);
    bool user_has_permissions = check_user_perm(user);

    if (num_sessions > MAX_ALLOWED_SESSIONS_PER_USER)
    {
        if (user_has_permissions)
        {
            char *session_id;
            // 此处调用pam_prompt可正常交互
            res = pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &session_id, "请输入要关闭的会话ID(输入exit取消登录):", NULL);
            if (res != PAM_SUCCESS)
            {
                return PAM_CONV_ERR;
            }

            if (!strcmp(session_id, "exit"))
            {
                pam_free(pamh, session_id);
                return PAM_AUTH_ERR;
            }
            else
            {
                // 执行关闭会话操作
                if (kill_session(session_id) != 0)
                {
                    pam_free(pamh, session_id);
                    return PAM_AUTH_ERR;
                }
                pam_free(pamh, session_id);
            }
        }
        else
        {
            return PAM_AUTH_ERR;
        }
    }

    return PAM_SUCCESS;
}

2. 会话阶段无交互替代方案

如果必须在会话阶段处理,放弃用户交互,改用自动策略:

  • 自动关闭用户最久未使用的会话
  • 记录日志并拒绝新会话,引导用户通过pkill、loginctl等工具手动清理

示例自动清理逻辑:

/* PAM entry point for session creation */
DLL_EXPORT int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
    const char *user;
    int res = pam_get_user(pamh, &user, NULL);
    if (res != PAM_SUCCESS || user == NULL)
    {
        return PAM_USER_UNKNOWN;
    }

    int num_sessions = get_user_sessions(user);
    bool user_has_permissions = check_user_perm(user);

    if (num_sessions > MAX_ALLOWED_SESSIONS_PER_USER)
    {
        if (user_has_permissions)
        {
            // 自动关闭最旧的会话
            char *oldest_session = get_oldest_session(user);
            if (oldest_session != NULL && kill_session(oldest_session) == 0)
            {
                pam_free(pamh, oldest_session);
                return PAM_SUCCESS;
            }
            else
            {
                pam_free(pamh, oldest_session);
                return PAM_SESSION_ERR;
            }
        }
        else
        {
            return PAM_SESSION_ERR;
        }
    }
    else
    {
        return PAM_SUCCESS;
    }
}

3. 手动绑定对话函数(不推荐)

部分PAM服务允许在会话阶段保留对话上下文,但需手动设置PAM_CONV项,兼容性极差,仅适用于特定场景:

// 仅在少数特定服务下可能生效,不具备通用性
struct pam_conv *conv;
res = pam_get_item(pamh, PAM_CONV, (const void **)&conv);
if (res == PAM_SUCCESS && conv != NULL)
{
    // 确保对话函数仍可用
    res = pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &session_id, "...", NULL);
}

内容的提问来源于stack exchange,提问作者Adrian J.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 09:10:15