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

如何在PAM模块中记录SSH登录失败?现有代码实现遇阻求指导

解决PAM模块记录SSH登录失败的问题

核心问题分析

你当前的代码仅实现了会话打开/关闭阶段的日志记录,而pam_sm_open_session仅在认证成功后才会被调用。登录失败事件发生在认证阶段,因此必须实现pam_sm_authenticate函数来捕获这类事件。

关键函数说明

  • pam_sm_authenticate:PAM认证流程的核心入口,无论认证成功或失败都会被触发。你需要根据它的返回值判断认证状态,记录对应日志。
  • pam_get_user:即使认证失败,只要客户端提供了用户名,通常也能通过该函数获取到(需处理获取失败的情况)。

修改后的完整代码

#include <security/pam_modules.h>
#include <security/pam_ext.h>
#include <stdio.h>
#include <syslog.h>
#include <time.h>

// 日志记录函数,修正换行符并处理空用户名场景
void log_event(const char *username, const char *event) {
    const char *safe_user = (username != NULL) ? username : "unknown_user";
    FILE *log_file = fopen("/var/log/pam_ssh_logins.log", "a");
    if (log_file) {
        time_t now = time(NULL);
        // 去除ctime自带的换行符,避免日志格式混乱
        char *time_str = ctime(&now);
        time_str[strcspn(time_str, "\n")] = '\0';
        fprintf(log_file, "%s - %s: %s\n", time_str, safe_user, event);
        fclose(log_file);
    } else {
        syslog(LOG_ERR, "Failed to open log file for SSH login tracking");
    }
}

// 认证函数:捕获登录成功/失败事件
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) {
    const char *username;
    int pam_result = pam_get_user(pamh, &username, NULL);
    
    // 先尝试获取用户名,失败则记录未知用户日志
    if (pam_result != PAM_SUCCESS) {
        log_event(NULL, "Authentication failed: Unable to retrieve username");
        return pam_result;
    }

    // 调用PAM默认认证逻辑,保留原有认证流程
    pam_result = pam_authenticate(pamh, flags);
    
    // 根据返回码区分不同失败场景
    switch(pam_result) {
        case PAM_SUCCESS:
            log_event(username, "Authentication succeeded");
            break;
        case PAM_AUTH_ERR:
            log_event(username, "Authentication failed: Invalid password");
            break;
        case PAM_USER_UNKNOWN:
            log_event(username, "Authentication failed: User does not exist");
            break;
        case PAM_MAXTRIES:
            log_event(username, "Authentication failed: Too many attempts");
            break;
        default:
            log_event(username, "Authentication failed: Unknown error");
            break;
    }

    return pam_result;
}

// 会话打开:记录登录成功后的会话启动
PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) {
    const char *username;
    if (pam_get_user(pamh, &username, NULL) == PAM_SUCCESS) {
        log_event(username, "Logged in via SSH");
    }
    return PAM_SUCCESS;
}

// 会话关闭:记录注销事件
PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) {
    const char *username;
    if (pam_get_user(pamh, &username, NULL) == PAM_SUCCESS) {
        log_event(username, "Logged out from SSH");
    }
    return PAM_SUCCESS;
}

PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) {
    return PAM_IGNORE;
}

PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) {
    return PAM_IGNORE;
}

重要配置注意事项

  • 修改SSH的PAM配置:在/etc/pam.d/sshd中添加你的模块,确保auth阶段的配置能捕获认证事件,示例:
    auth    required    /path/to/your/pam_module.so
    session required    /path/to/your/pam_module.so
    
    注意模块顺序,确保你的模块在其他认证模块之前或按需求排列。
  • 日志文件权限:创建日志文件后设置权限chmod 600 /var/log/pam_ssh_logins.log,避免普通用户读取敏感登录信息,同时保证PAM进程(通常为root或sshd用户)可写入。
  • 避免重复日志:若系统自带SSH日志已记录部分内容,可调整日志内容格式,减少冗余。

内容的提问来源于stack exchange,提问作者moses

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 14:59:59