卡認證、key認證的Xshell、securecrt自定義交互提示信息


參考: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"\r\n", 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"\r\n", 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"\r\n", 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"\r\n", 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"\r\n", 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] = {'\0'}; char hint_buff[256] = {'\0'}; char err_hint_buff[256] = {'\0'}; static char *err_hint = ""; char *service_name = NULL; int MaxAuthTries = 1; // 1. 讀取用戶輸入的用戶名 ret_user = pam_get_user(pamh, &username, "Card Username:"); // username不需要釋放 MY_print_ln_I("#######################user=%s, ret=%d", username, ret_user); if (PAM_SUCCESS != ret_user || NULL == username || 0 == strlen(username)) { MY_print_ln_E("無法獲取用戶輸入的用戶名, ret=%d", ret_user); return PAM_AUTH_ERR; } // 2. 讀取服務名稱 ret = pam_get_item(pamh, PAM_SERVICE, (const void **)&service_name); if (PAM_SUCCESS != ret || NULL == service_name) { MY_print_ln_E("無法獲取服務名稱"); return PAM_AUTH_ERR; } MY_print_ln_I("#######################user=%s, service_name=%s", username, service_name); // 3. 讀取用戶輸入的密碼 // sshd服務的sshpam_thread_cleanup線程結束方式: // 1. 獲取用戶輸入密碼, 如果點擊取消 // 2. 用戶長時間未鑒權成功, sshd主動關閉鏈接, 默認2分鍾, 由sshd_config配置文件LoginGraceTime字段決定 // sshd無限循環即可: 可以不斷的給出提示, 如果用戶需要切換用戶名, 點擊取消即可 MaxAuthTries = (0 == strcmp("sshd", service_name)) ? 0x7fffffff : 1; for (index = 0; index < MaxAuthTries; index++) { memset((void *)hint_buff, 0, sizeof(hint_buff)); snprintf(hint_buff, sizeof(hint_buff) - 1, "###Please plugin MYCard for %s###\r\n%sUserCard PIN:", username, err_hint); // The pam_get_authtok function returns the cached authentication token, or prompts the user if no token is currently cached. // ret_password = pam_get_authtok(pamh, PAM_AUTHTOK, &password, "UserKey PIN:"); // 此函數會緩存: 與需求不符 // ret_password = MY_hint(pamh, 0, PAM_PROMPT_ECHO_OFF, hint_buff, &password); // ok1, password需要釋放 ret_password = pam_prompt(pamh,PAM_PROMPT_ECHO_OFF, (char **)&password,"%s", hint_buff); // ok2, password需要釋放 if (PAM_SUCCESS != ret_password || NULL == password) { MY_print_ln_E("無法獲取用戶輸入的PIN碼, ret=%d", ret_password); return PAM_AUTH_ERR; } // TODO: please close debug level (modify g_is_MY_debug_level in pam_log.h)!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! MY_print_ln_D("user=%s,pwd=%s\n", username, password); MY_COMMON_print_byte("password", (unsigned char *)password, strlen(password)); // 4. 讀取卡里存儲的用戶名 ret = get_username_from_card(password, strlen(password), username_in_card, sizeof(username_in_card)); free((char *)password); password = NULL; if (0 == ret) { int username_cmp_ret = strcmp(username, username_in_card); if (0 == username_cmp_ret) { MY_print_ln_I("登陸成功"); return PAM_SUCCESS; } else { memset((void *)err_hint_buff, 0, sizeof(err_hint_buff)); snprintf(err_hint_buff, sizeof(err_hint_buff) - 1, "###Current Card is not for %s###\r\n", username); err_hint = err_hint_buff; MY_print_ln_I("卡的用戶名與輸入的用戶名不一致:%s", err_hint); } } else { memset((void *)err_hint_buff, 0, sizeof(err_hint_buff)); snprintf(err_hint_buff, sizeof(err_hint_buff) - 1, "###%s###\r\n", get_strerr(ret)); err_hint = err_hint_buff; MY_print_ln_I("%s", err_hint); } } // end for MY_print_ln_I("########### MYVerify exit ##########"); return PAM_AUTH_ERR; } #endif PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { MY_print_ln_I("#############################pam_sm_authenticate in"); return MYVerify(pamh); } PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) { MY_print_ln_I("#############################pam_sm_setcred in"); return call_pam_unix_foo(pamh, flags, argc, argv, "pam_sm_setcred"); } PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) { MY_print_ln_I("#############################pam_sm_acct_mgmt in"); return call_pam_unix_foo(pamh, flags, argc, argv, "pam_sm_acct_mgmt"); } PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { MY_print_ln_I("#############################pam_sm_open_session in"); return call_pam_unix_foo(pamh, flags, argc, argv, "pam_sm_open_session"); } PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { MY_print_ln_I("#############################pam_sm_close_session in"); return call_pam_unix_foo(pamh, flags, argc, argv, "pam_sm_close_session"); } PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) { MY_print_ln_I("#############################pam_sm_chauthtok in"); return call_pam_unix_foo(pamh, flags, argc, argv, "pam_sm_chauthtok"); } /* * 該結構為, 程序被編譯成靜態模塊所需的一個模塊數據結構 */ #ifdef PAM_STATIC struct pam_module _pam_pwdb_modstruct={ "pam_fthsm", pam_sm_authenticate, pam_sm_setcred, pam_sm_acct_mgmt, pam_sm_open_session, pam_sm_close_session, pam_sm_chauthtok }; #endif

 

// Makefile

MIDCARD_DIR=./midcard
srclist=pam_my.c \
        pam_log.c \



all:pam_my.so
pam_my.so: $(srclist)
        gcc -I. -I$(MIDCARD_DIR) -I/usr/include/ -I/usr/include/PCSC/ -lpcsclite -lcrypto $^ -fPIC -shared -o $@
        strip $@
clean:
        rm -f *.so midcard/*.o
copy:
        cp -f pam_fthsm.so /lib64/security/

 

 

/etc/ssh/sshd_config的關鍵配置

Port 22 Subsystem sftp internal-sftp AllowUsers root test PubkeyAuthentication no PasswordAuthentication no ChallengeResponseAuthentication yes UsePAM yes

 

/etc/pam.d/sshd  的關鍵配置

#%PAM-1.0 #auth required pam_sepermit.so #auth substack password-auth #auth include postlogin
account    required     pam_nologin.so
account    include      password-auth
password   include      password-auth
# pam_selinux.so close should be the first session rule
session    required     pam_selinux.so close
session    required     pam_loginuid.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session    required     pam_selinux.so open env_params
session    optional     pam_keyinit.so force revoke
session    include      password-auth
session    include      postlogin
auth required pam_my.so

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM