Linux下C++程序实时检测用户登录认证成功/失败事件的方案咨询
登录成功/失败事件实时检测方案
方案1:基于PAM模块原生特性实现(无延迟,兼容性好)
你之前的PAM方案存在误区:无需依赖会话启动事件判定认证结果,直接在**认证阶段(auth stack)**即可拿到实时的认证状态:
- 将你的自定义PAM
.so模块以optional类型添加到系统通用认证栈末尾(一般修改/etc/pam.d/common-auth,Debian系)或者对应服务(sshd/login/su等)的auth栈末尾,确保前面所有认证逻辑执行完成后才会调用你的模块。 - 在模块的
pam_sm_authenticate回调函数中,通过pam_get_item接口获取PAM_AUTH_STATUS属性,该属性值即为整个认证栈的最终执行结果:- 若值为
PAM_SUCCESS,代表当前认证请求成功 - 其余返回值均对应认证失败,可进一步判断返回码区分密码错误、用户不存在等具体失败原因
- 若值为
- 同时可通过
pam_get_item获取PAM_USER(用户名)、PAM_RHOST(远程登录IP)、PAM_SERVICE(发起认证的服务名,如sshd/login)等上下文信息,直接在回调中完成事件上报/记录即可,完全无延迟。
核心代码示例:
#include <security/pam_modules.h> #include <security/pam_ext.h> PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { const char *username = NULL; int auth_res; // 获取认证用户名 pam_get_item(pamh, PAM_USER, (const void **)&username); // 获取认证结果 if (pam_get_item(pamh, PAM_AUTH_STATUS, (const void **)&auth_res) == PAM_SUCCESS) { if (auth_res == PAM_SUCCESS) { // 记录认证成功事件 } else { // 记录认证失败事件 } } // 必须返回PAM_SUCCESS,不影响原有认证逻辑 return PAM_SUCCESS; }
注:
PAM_AUTH_STATUS在Linux-PAM 0.99.7.0(2007年发布)及以上版本支持,目前所有主流发行版均兼容该特性。如果需要适配更早的古董系统,可以选择第二种方案。
方案2:基于utmp/btmp二进制日志实现(侵入性最低,兼容性最优)
该方案无需修改系统PAM配置,也不依赖auditd组件,兼容几乎所有Linux/Unix系统:
- Linux系统默认会将登录成功事件写入
/var/log/wtmp,登录失败事件写入/var/log/btmp,两个文件均为POSIX标准定义的二进制格式,不存在文本日志的格式兼容性问题,解析效率远高于文本解析。 - 用
inotify监听两个文件的IN_MODIFY事件,一旦有写入即可实时读取新增条目:- 使用
<utmp.h>头文件中定义的getutxent/getutxid等标准接口解析条目,结构体utmpx的ut_type字段为USER_PROCESS即为有效登录记录。 - 读
btmp得到所有登录失败事件,读wtmp得到所有登录成功事件。
- 使用
注:可以通过
<paths.h>中的_PATH_WTMP、_PATH_BTMP宏自动适配不同发行版的日志路径,无需硬编码。
两种方案选型建议
- 如果需要获取认证的详细上下文(如认证失败的具体原因、自定义扩展字段),选择PAM模块方案
- 如果希望尽量降低对系统的侵入性、适配极旧版本系统,选择utmp/btmp+inotify方案
内容的提问来源于stack exchange,提问作者Gabriel Ciubotaru
相关产品推荐
相关产品推荐

