SSH连接达MaxAuthTries后程序挂起,如何处理SSH_MSG_DISCONNECT?
问题背景
出于安全扫描需求,需验证本地主机是否按/etc/ssh/sshd_config中的配置(如MaxAuthTries限制、PAM设置)处理SSH会话。测试场景为:程序使用不存在的用户及错误密码建立连接,测量每次认证耗时。但基于libssh的C语言测试代码在达到MaxAuthTries限制后会永久挂起;未设置日志级别时无相关输出,提升至SSH_LOG_WARNING后可看到服务器返回SSH_MSG_DISCONNECT,但无法通过ssh_get_error(session)获取该消息。
1. 如何提取SSH_MSG_DISCONNECT供程序使用?
有两种可靠方式捕获SSH_MSG_DISCONNECT事件:
方式一:注册断开回调函数
libssh提供ssh_set_disconnect_callback接口,可在会话初始化后注册自定义回调,服务器发送断开消息时会自动触发该回调,直接获取断开原因与描述:
#include <libssh/libssh.h> // 自定义断开回调函数 void on_disconnect(ssh_session session, int reason_code, const char *desc, void *user_data) { printf("服务器主动断开:原因码[%d],描述:%s\n", reason_code, desc); // 通过user_data传递标志,通知主逻辑处理重连 *(int*)user_data = 1; } // 在会话初始化后注册回调 int main() { ssh_session session = ssh_new(); int need_reconnect = 0; ssh_set_disconnect_callback(session, on_disconnect, &need_reconnect); // ... 连接、认证逻辑 ... ssh_free(session); return 0; }
方式二:手动处理消息循环
如果使用libssh低阶API,可在认证循环中主动获取并解析消息,判断是否为断开类型:
ssh_message msg; while ((msg = ssh_message_get(session)) != NULL) { if (ssh_message_get_type(msg) == SSH_REQUEST_DISCONNECT) { int reason = ssh_message_disconnect_get_reason(msg); const char *desc = ssh_message_disconnect_get_description(msg); printf("收到断开消息:%d - %s\n", reason, desc); // 设置重连标志 need_reconnect = 1; } ssh_message_free(msg); if (need_reconnect) break; }
注意:
ssh_get_error无法获取该消息的原因是,服务器发送SSH_MSG_DISCONNECT后会话直接进入断开状态,libssh不会将此作为常规错误存储,需通过上述主动方式捕获。
2. 如何处理SSH_MSG_DISCONNECT:关闭当前会话并新建是否可行?
完全可行,但需严格遵循资源清理流程:
第一步:彻底销毁当前会话
收到断开消息后,必须依次调用ssh_disconnect(session)和ssh_free(session),释放会话占用的所有资源,避免内存泄漏:if (need_reconnect) { ssh_disconnect(session); ssh_free(session); session = NULL; }第二步:重建会话并重新执行流程
销毁旧会话后,重新调用ssh_new()初始化新会话,重复之前的连接、认证逻辑即可。
注意:禁止复用已断开的会话对象,必须完全销毁后再新建,否则会出现未定义行为。
内容的提问来源于stack exchange,提问作者AbreQueVoy

