参考:SSH 协议基本原理及 wireshark 抓包分析https://juejin.im/post/5baaf517e51d453df0442dce
参考:pam会话函数详解https://blog.csdn.net/wzsy/article/details/37725375
参考:PAM详解(一)PAM介绍http://blog.chinaunix.net/uid-29479952-id-5761558.html
参考:PAM详解(二)PAM开发http://blog.chinaunix.net/uid-29479952-id-5761564.html
使用卡认证、key认证方式进行ssh登陆,如需要Xshell、securecrt自定义交互提示信息。需要使用键盘交互鉴权方式。
使用键盘交互模式验证卡
键盘交互模式:
解释: xshell有键盘交互模式, 可以边给出自定义提示信息, 边输入密码。
0. 默认xshell的Keyboard Interactive单选按钮无效, 需要修改sshd_config文件, 使此按钮生效
1. 修改方式: 请修改配置为"/etc/ssh/sshd_config里ChallengeResponseAuthentication yes"
2. 同时关闭密码交互方式: 请修改配置为"/etc/ssh/sshd_config里PasswordAuthentication no"
3. 同时关闭公钥交互方式: 请修改配置为"/etc/ssh/sshd_config里PubkeyAuthentication no"
问题1:是否可以禁用记住用户名、禁用记住密码?
答:
SSH登录里面Keyboard Interactive认证方式
SSH 的认证协议
常见的 SSH 协议认证方式有如下几种:
基于口令的验证方式(password authentication method),通过输入用户名和密码的方式进行远程机器的登录验证。
基于公共密钥的安全验证方式(public key authentication method),通过生成一组密钥(public key/private key)来实现用户的登录验证。
基于键盘交互的验证方式(keyboard interactive authentication method),通过服务器向客户端发送提示信息,然后由客户端根据相应的信息通过手工输入的方式发还给服务器端。
//my_log.h #define g_is_MY_debug_level 4 #define MY_DEBUG_E_CONDITION ( 1 <= g_is_MY_debug_level) #define MY_DEBUG_W_CONDITION ( 2 <= g_is_MY_debug_level) #define MY_DEBUG_L_CONDITION ( 3 <= g_is_MY_debug_level) #define MY_DEBUG_I_CONDITION ( 4 <= g_is_MY_debug_level) #define MY_DEBUG_D_CONDITION ( 5 <= g_is_MY_debug_level) #define MY_print_ln_E( fmt, arg...) { if (MY_DEBUG_E_CONDITION) { syslog(LOG_ERR, "fthsm|||-> Time(%s), (%s|%d), "fmt" ", getCurrentTime(), __func__, __LINE__, ##arg); } } #define MY_print_ln_W( fmt, arg...) { if (MY_DEBUG_W_CONDITION) { syslog(LOG_WARNING, "fthsm|||-> Time(%s), (%s|%d), "fmt" ", getCurrentTime(), __func__, __LINE__, ##arg); } } #define MY_print_ln_L( fmt, arg...) { if (MY_DEBUG_L_CONDITION) { syslog(LOG_NOTICE, "fthsm|||-> Time(%s), (%s|%d), "fmt" ", getCurrentTime(), __func__, __LINE__, ##arg); } } #define MY_print_ln_I( fmt, arg...) { if (MY_DEBUG_I_CONDITION) { syslog(LOG_INFO, "fthsm|||-> Time(%s), (%s|%d), "fmt" ", getCurrentTime(), __func__, __LINE__, ##arg); } } #define MY_print_ln_D( fmt, arg...) { if (MY_DEBUG_D_CONDITION) { syslog(LOG_DEBUG, "fthsm|||-> Time(%s), (%s|%d), "fmt" ", getCurrentTime(), __func__, __LINE__, ##arg); } }
// my_pam.main
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <pwd.h> #include <sys/stat.h> #include <fcntl.h> #include <dlfcn.h> #include <iconv.h> #include <security/pam_modules.h> #include <security/pam_ext.h> #include <dlfcn.h> /************************************* 日志查看方式: tail -f var/log/secure 默认使用配置: /etc/rsyslog.conf中的这个配置 authpriv.* var/log/secure *************************************/ #include "midcard_main.h" #include "pam_log.h" #define LIB_SECURITY_PATH "/lib64/security" typedef int (*PAMAuthFunc)(pam_handle_t *, int, int, const char **); #if 1 /* * 调用pam_unix.so.orig中的某个导出函数 */ static int call_pam_unix_foo(IN pam_handle_t *pamh, IN int flags, IN int argc, IN const char **argv, IN const char *foo_name) { //MY_print_ln_I("3333333333333333"); void *handler = dlopen(LIB_SECURITY_PATH"/pam_unix.so", RTLD_LAZY); // 如果将pam_unix.so重命名为pam_unix.so.orig则pam_sm_setcred会返回失败 if (!handler) { MY_print_ln_E("Failed to open the pam_unix.so"); return PAM_AUTH_ERR; } PAMAuthFunc auth_func = (PAMAuthFunc)dlsym(handler, foo_name); if (!auth_func) { MY_print_ln_E("There is no "%s" in symbol table.", foo_name); dlclose(handler); return PAM_AUTH_ERR; } int ret = auth_func(pamh, flags, argc, argv); /* BUGGY HERE!!! 如果这里将句柄释放掉,在某些机器上使用原有的pam_unix.so进行 * 验证时会出现段错误,导致界面无法登入。 * dlclose(handler); */ //MY_print_ln_I("ret=%d[PAM_SUCCESS=%d, PAM_AUTH_ERR=%d, PAM_CONV_ERR=%d]", ret, PAM_SUCCESS, PAM_AUTH_ERR, PAM_CONV_ERR); return ret; } /* * 使用系统原有的登录验证机制,即走pam_unix.so中的验证 * * 参数: * 同pam_sm_authenticate()的参数 * 返回值: * pam_unix.so中pam_sm_authenticate()函数的返回值。 */ static int pam_unix_authenticate(IN pam_handle_t *pamh, IN int flags, IN int argc, IN const char **argv) { return call_pam_unix_foo(pamh, flags, argc, argv, "pam_sm_authenticate"); } #endif #if 1 /*********************************** * 获取用户输入的数据 * * 使用此接口的前提是sshd启用键盘交互模式, (即使Keyboard Interactive单选按钮生效) * 请修改配置为"/etc/ssh/sshd_config里ChallengeResponseAuthentication yes" * * pamh: pam句柄 * keep_ms: 执行此操作后延时的ms, 不延时请传入0 * msg_style: PAM_PROMPT_ECHO_ON获取用户输入的用户名, * PAM_PROMPT_ECHO_OFF获取用户输入的密码 * hint_msg_str: 提示信息 * resp_str: 输出参数, 输出待读取的数据 * return : PAM_SUCCESS获取成功, PAM_AUTH_ERR获取失败 * * /usr/include/security/_pam_types.h * #define PAM_PROMPT_ECHO_OFF 1 * #define PAM_PROMPT_ECHO_ON 2 * #define PAM_ERROR_MSG 3 * #define PAM_TEXT_INFO 4 **********************************/ static int MY_hint(IN pam_handle_t *pamh, IN unsigned int keep_ms, IN int msg_style, IN const char *hint_msg_str, OUT const char **resp_str) { struct pam_message hint_msg = { .msg_style = msg_style, .msg = (NULL == hint_msg_str) ? "" : hint_msg_str, }; struct pam_conv *hint_conv = NULL; const struct pam_message *hint_msg_ptr = &hint_msg; struct pam_response *hint_resp = NULL; int ret = PAM_AUTH_ERR; // 更多参数请参考man page if (pam_get_item(pamh, PAM_CONV, (const void **)&hint_conv) != PAM_SUCCESS) { MY_print_ln_E("无法获取权限验证会话"); return PAM_AUTH_ERR; } MY_print_ln_I("msg_style=%d, hint=%s", msg_style, hint_msg_str); ret = (*hint_conv->conv)(1, &hint_msg_ptr, &hint_resp, hint_conv->appdata_ptr); if (PAM_SUCCESS != ret || NULL == hint_resp) { MY_print_ln_E("conversation_function ret Failed. ret=%d, resp=%s", ret, hint_resp->resp); return ret; } /* 请调用者释放 if (hint_resp->resp) { free(hint_resp->resp); hint_resp->resp = NULL; } */ if (NULL != resp_str){ *resp_str = hint_resp->resp; } free(hint_resp); hint_resp = NULL; if (0 < keep_ms) { pam_fail_delay(pamh, keep_ms * 1000); /* 使错误提示信息保持一定时间(第2个参数单位为微秒)(1秒==1000000微秒) */ } return PAM_SUCCESS; } /********************************** * 使用键盘交互模式验证卡 * * 键盘交互模式: * 解释: xshell有键盘交互模式, 可以边给出自定义提示信息, 边输入密码。 * 0. 默认xshell的Keyboard Interactive单选按钮无效, 需要修改sshd_config文件, 使此按钮生效 * 1. 修改方式: 请修改配置为"/etc/ssh/sshd_config里ChallengeResponseAuthentication yes" * 2. 同时关闭密码交互方式: 请修改配置为"/etc/ssh/sshd_config里PasswordAuthentication no" * 3. 同时关闭公钥交互方式: 请修改配置为"/etc/ssh/sshd_config里PubkeyAuthentication no" */ static int MYVerify(IN pam_handle_t *pamh) { const char *username = NULL; const char *password = NULL; int ret_user = PAM_AUTH_ERR; int ret_password = PAM_AUTH_ERR; int ret = -1; int index = 0; char username_in_card[128] = {'