如何在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
相关产品推荐
相关产品推荐

