Openssh认证
openssh有权限分离策略,主进程进行权限校验逻辑
密码认证阶段
使用PAM进行认证时,函数栈如下:
sshpam_auth_passwd auth-pam.c:1425
auth_password auth-passwd.c:116
mm_answer_authpassword monitor.c:1150
monitor_read monitor.c:583
monitor_child_preauth monitor.c:368
privsep_preauth sshd.c:506
main sshd.c:2242BMC中,实现Openldap登录时,在mm_answer_authpassword中增加了认证流程
源码流程分析--带pam认证
分发逻辑在monitor_read中:
涉及的monitor如下:
/* Please keep *_REQ_* values on even numbers and *_ANS_* on odd numbers */
enum monitor_reqtype {
MONITOR_REQ_MODULI = 0, MONITOR_ANS_MODULI = 1,
MONITOR_REQ_FREE = 2,
MONITOR_REQ_AUTHSERV = 4,
MONITOR_REQ_SIGN = 6, MONITOR_ANS_SIGN = 7,
MONITOR_REQ_PWNAM = 8, MONITOR_ANS_PWNAM = 9,
MONITOR_REQ_AUTH2_READ_BANNER = 10, MONITOR_ANS_AUTH2_READ_BANNER = 11,
MONITOR_REQ_AUTHPASSWORD = 12, MONITOR_ANS_AUTHPASSWORD = 13,
MONITOR_REQ_BSDAUTHQUERY = 14, MONITOR_ANS_BSDAUTHQUERY = 15,
MONITOR_REQ_BSDAUTHRESPOND = 16, MONITOR_ANS_BSDAUTHRESPOND = 17,
MONITOR_REQ_KEYALLOWED = 22, MONITOR_ANS_KEYALLOWED = 23,
MONITOR_REQ_KEYVERIFY = 24, MONITOR_ANS_KEYVERIFY = 25,
MONITOR_REQ_KEYEXPORT = 26,
MONITOR_REQ_PTY = 28, MONITOR_ANS_PTY = 29,
MONITOR_REQ_PTYCLEANUP = 30,
MONITOR_REQ_SESSKEY = 32, MONITOR_ANS_SESSKEY = 33,
MONITOR_REQ_SESSID = 34,
MONITOR_REQ_RSAKEYALLOWED = 36, MONITOR_ANS_RSAKEYALLOWED = 37,
MONITOR_REQ_RSACHALLENGE = 38, MONITOR_ANS_RSACHALLENGE = 39,
MONITOR_REQ_RSARESPONSE = 40, MONITOR_ANS_RSARESPONSE = 41,
MONITOR_REQ_GSSSETUP = 42, MONITOR_ANS_GSSSETUP = 43,
MONITOR_REQ_GSSSTEP = 44, MONITOR_ANS_GSSSTEP = 45,
MONITOR_REQ_GSSUSEROK = 46, MONITOR_ANS_GSSUSEROK = 47,
MONITOR_REQ_GSSCHECKMIC = 48, MONITOR_ANS_GSSCHECKMIC = 49,
MONITOR_REQ_TERM = 50,
MONITOR_REQ_PAM_START = 100,
MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103,
MONITOR_REQ_PAM_INIT_CTX = 104, MONITOR_ANS_PAM_INIT_CTX = 105,
MONITOR_REQ_PAM_QUERY = 106, MONITOR_ANS_PAM_QUERY = 107,
MONITOR_REQ_PAM_RESPOND = 108, MONITOR_ANS_PAM_RESPOND = 109,
MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
};
# 认证方式PAM,登录后输入错误3次密码的整个流程
debug3: mm_request_send: entering, type 6 [preauth]
debug3: mm_request_receive_expect: entering, type 7 [preauth]
debug3: monitor_read: checking request 6
debug3: mm_request_send: entering, type 7
debug3: mm_request_send: entering, type 8 [preauth]
debug3: mm_request_receive_expect: entering, type 9 [preauth]
debug3: monitor_read: checking request 8
debug3: mm_request_send: entering, type 9
debug3: mm_request_send: entering, type 100 [preauth]
debug3: monitor_read: checking request 100
debug3: mm_request_send: entering, type 4 [preauth]
debug3: mm_request_send: entering, type 10 [preauth]
debug3: mm_request_receive_expect: entering, type 11 [preauth]
debug3: monitor_read: checking request 4
debug3: monitor_read: checking request 10
debug3: mm_request_send: entering, type 11
debug3: mm_request_send: entering, type 22 [preauth]
debug3: mm_request_receive_expect: entering, type 23 [preauth]
debug3: monitor_read: checking request 22
debug3: mm_request_send: entering, type 23
debug3: mm_request_send: entering, type 22 [preauth]
debug3: mm_request_receive_expect: entering, type 23 [preauth]
debug3: monitor_read: checking request 22
debug3: mm_request_send: entering, type 23
debug3: mm_request_send: entering, type 12 [preauth]
debug3: mm_request_receive_expect: entering, type 13 [preauth]
debug3: monitor_read: checking request 12
debug3: mm_request_send: entering, type 13
debug3: mm_request_send: entering, type 12 [preauth]
debug3: mm_request_receive_expect: entering, type 13 [preauth]
debug3: monitor_read: checking request 12
debug3: mm_request_send: entering, type 13
debug3: mm_request_send: entering, type 12 [preauth]
debug3: monitor_read: checking request 12
debug3: mm_request_send: entering, type 13
debug3: mm_request_receive_expect: entering, type 13 [preauth]流程1:在輸入密碼時認證失敗:
debug3: mm_request_send: entering, type 6 [preauth] MONITOR_REQ_SIGN
debug3: mm_request_receive_expect: entering, type 7 [preauth] MONITOR_ANS_SIGN
debug3: monitor_read: checking request 6 MONITOR_REQ_SIGN
debug3: mm_request_send: entering, type 7 MONITOR_ANS_SIGN
debug3: mm_request_send: entering, type 8 [preauth] MONITOR_REQ_PWNAM
debug3: mm_request_receive_expect: entering, type 9 [preauth] MONITOR_ANS_PWNAM
debug3: monitor_read: checking request 8 MONITOR_REQ_PWNAM
debug3: mm_request_send: entering, type 9 MONITOR_ANS_PWNAM
debug3: mm_request_send: entering, type 100 [preauth] MONITOR_REQ_PAM_START
debug3: monitor_read: checking request 100 MONITOR_REQ_PAM_START
debug3: mm_request_send: entering, type 4 [preauth] MONITOR_REQ_AUTHSERV
debug3: mm_request_send: entering, type 10 [preauth] MONITOR_REQ_AUTH2_READ_BANNER
debug3: mm_request_receive_expect: entering, type 11 [preauth] MONITOR_ANS_AUTH2_READ_BANNER
debug3: monitor_read: checking request 4 MONITOR_REQ_AUTHSERV
debug3: monitor_read: checking request 10 MONITOR_REQ_AUTH2_READ_BANNER
debug3: mm_request_send: entering, type 11 MONITOR_ANS_AUTH2_READ_BANNER
debug3: mm_request_send: entering, type 22 [preauth] MONITOR_REQ_KEYALLOWED
debug3: mm_request_receive_expect: entering, type 23 [preauth] MONITOR_ANS_KEYALLOWED
debug3: monitor_read: checking request 22 MONITOR_REQ_KEYALLOWED 这里等待客户端输入密码
debug3: mm_request_send: entering, type 23 MONITOR_ANS_KEYALLOWED
debug3: mm_request_send: entering, type 22 [preauth] MONITOR_REQ_KEYALLOWED
debug3: mm_request_receive_expect: entering, type 23 [preauth] MONITOR_ANS_KEYALLOWED
debug3: monitor_read: checking request 22 MONITOR_REQ_KEYALLOWED
debug3: mm_request_send: entering, type 23 MONITOR_ANS_KEYALLOWED
debug3: mm_request_send: entering, type 12 [preauth] MONITOR_REQ_AUTHPASSWORD 密码校验流程1
debug3: mm_request_receive_expect: entering, type 13 [preauth] MONITOR_ANS_AUTHPASSWORD
debug3: monitor_read: checking request 12 MONITOR_REQ_AUTHPASSWORD 这里开始校验密码
debug3: mm_request_send: entering, type 13 MONITOR_ANS_AUTHPASSWORD
debug3: mm_request_send: entering, type 12 [preauth] MONITOR_REQ_AUTHPASSWORD 密码校验流程2
debug3: mm_request_receive_expect: entering, type 13 [preauth] MONITOR_ANS_AUTHPASSWORD
debug3: monitor_read: checking request 12 MONITOR_REQ_AUTHPASSWORD
debug3: mm_request_send: entering, type 13 MONITOR_ANS_AUTHPASSWORD
debug3: mm_request_send: entering, type 12 [preauth] MONITOR_REQ_AUTHPASSWORD 密码校验流程3
debug3: monitor_read: checking request 12 MONITOR_REQ_AUTHPASSWORD
debug3: mm_request_send: entering, type 13 MONITOR_ANS_AUTHPASSWORD
debug3: mm_request_receive_expect: entering, type 13 [preauth] MONITOR_ANS_AUTHPASSWORD流程2:密码认证成功,pam认证失败
debug3: mm_request_send: entering, type 6 [preauth] MONITOR_REQ_SIGN
debug3: mm_request_receive_expect: entering, type 7 [preauth] MONITOR_ANS_SIGN
debug3: monitor_read: checking request 6 MONITOR_REQ_SIGN
debug3: mm_request_send: entering, type 7 MONITOR_ANS_SIGN
debug3: mm_request_send: entering, type 8 [preauth] MONITOR_REQ_PWNAM
debug3: mm_request_receive_expect: entering, type 9 [preauth] MONITOR_ANS_PWNAM
debug3: monitor_read: checking request 8 MONITOR_REQ_PWNAM
debug3: mm_request_send: entering, type 9 MONITOR_ANS_PWNAM
debug3: mm_request_send: entering, type 100 [preauth] MONITOR_REQ_PAM_START
debug3: mm_request_send: entering, type 4 [preauth] MONITOR_REQ_AUTHSERV
debug3: mm_request_send: entering, type 10 [preauth] MONITOR_REQ_AUTH2_READ_BANNER
debug3: mm_request_receive_expect: entering, type 11 [preauth] MONITOR_ANS_AUTH2_READ_BANNER
debug3: monitor_read: checking request 100 MONITOR_REQ_PAM_START
debug3: monitor_read: checking request 4 MONITOR_REQ_AUTHSERV
debug3: monitor_read: checking request 10 MONITOR_REQ_AUTH2_READ_BANNER
debug3: mm_request_send: entering, type 11 MONITOR_ANS_AUTH2_READ_BANNER
debug3: mm_request_send: entering, type 22 [preauth] MONITOR_REQ_KEYALLOWED 这里等待客户端输入密码
debug3: mm_request_receive_expect: entering, type 23 [preauth] MONITOR_ANS_KEYALLOWED
debug3: monitor_read: checking request 22 MONITOR_REQ_KEYALLOWED
debug3: mm_request_send: entering, type 23 MONITOR_ANS_KEYALLOWED
debug3: mm_request_send: entering, type 22 [preauth] MONITOR_REQ_KEYALLOWED
debug3: mm_request_receive_expect: entering, type 23 [preauth] MONITOR_ANS_KEYALLOWED
debug3: monitor_read: checking request 22 MONITOR_REQ_KEYALLOWED
debug3: mm_request_send: entering, type 23 MONITOR_ANS_KEYALLOWED
debug3: mm_request_send: entering, type 12 [preauth] MONITOR_REQ_AUTHPASSWORD 密码校验流程1
debug3: mm_request_receive_expect: entering, type 13 [preauth] MONITOR_ANS_AUTHPASSWORD
debug3: monitor_read: checking request 12 MONITOR_REQ_AUTHPASSWORD 这里开始校验密码
debug3: mm_request_send: entering, type 13 MONITOR_ANS_AUTHPASSWORD
debug3: mm_request_receive_expect: entering, type 102 MONITOR_REQ_PAM_ACCOUNT
debug3: mm_request_send: entering, type 103 MONITOR_ANS_PAM_ACCOUNT
debug3: mm_request_receive_expect: entering, type 26 MONITOR_REQ_KEYEXPORT
debug3: mm_request_send: entering, type 102 [preauth] MONITOR_REQ_PAM_ACCOUNT
debug3: mm_request_receive_expect: entering, type 103 [preauth] MONITOR_ANS_PAM_ACCOUNT
debug3: mm_request_send: entering, type 26 [preauth] MONITOR_REQ_KEYEXPORT对于每种monitor,均存在两种流程(
mon_dispatch_proto20
主流程
cstruct mon_table mon_dispatch_proto20[] = { #ifdef WITH_OPENSSL {MONITOR_REQ_MODULI, MON_ONCE, mm_answer_moduli}, #endif {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv}, {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner}, {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, #ifdef USE_PAM {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start}, {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account}, {MONITOR_REQ_PAM_INIT_CTX, MON_ONCE, mm_answer_pam_init_ctx}, {MONITOR_REQ_PAM_QUERY, 0, mm_answer_pam_query}, {MONITOR_REQ_PAM_RESPOND, MON_ONCE, mm_answer_pam_respond}, {MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx}, #endif #ifdef SSH_AUDIT_EVENTS {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, #endif #ifdef BSD_AUTH {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, {MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH, mm_answer_bsdauthrespond}, #endif {MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed}, {MONITOR_REQ_KEYVERIFY, MON_AUTH, mm_answer_keyverify}, #ifdef GSSAPI {MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx}, {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, #endif {0, 0, NULL} };mon_dispatch_postauth20:
认证成功后会执行的流程
cstruct mon_table mon_dispatch_postauth20[] = { #ifdef WITH_OPENSSL {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, #endif {MONITOR_REQ_SIGN, 0, mm_answer_sign}, {MONITOR_REQ_PTY, 0, mm_answer_pty}, {MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup}, {MONITOR_REQ_TERM, 0, mm_answer_term}, #ifdef SSH_AUDIT_EVENTS {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT, mm_answer_audit_command}, #endif {0, 0, NULL} };privsep_postauth->monitor_child_postauth->monitor_read
主流程
每个认证流程会将结果通过mm_request_send将结果发送给主进程,例如:
int
mm_answer_sign(struct ssh *ssh, int sock, struct sshbuf *m)
{
xxx
// 将MONITOR_ANS_SIGN发给主流程
mm_request_send(sock, MONITOR_ANS_SIGN, m);
/* Turn on permissions for getpwnam */
monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1);
return (0);
}会话阶段
在认证完成后,sshd会进入创建会话阶段,由于fork进程的原因,子进程会用来处理会话,此时如果需要监控子进程,可以在sshd.c中的authenticated标签后下断点,同时设置gdb参数:set follow-fork-mode child,用来监控子进程调用逻辑(也可以修改privsep_postauth函数中的fork后的判断语句)
static void
privsep_postauth(struct ssh *ssh, Authctxt *authctxt)
{
#ifdef DISABLE_FD_PASSING
if (1) {
#else
if (authctxt->pw->pw_uid == 0) {
#endif
/* File descriptor passing is broken or root login */
use_privsep = 0;
goto skip;
}
/* New socket pair */
monitor_reinit(pmonitor);
pmonitor->m_pid = fork();
if (pmonitor->m_pid == -1)
fatal("fork of unprivileged child failed");
else if (pmonitor->m_pid != 0) { // 这里改为pmonitor->m_pid == 0,可以在主进程启动会话
verbose("User child is on pid %ld", (long)pmonitor->m_pid);
sshbuf_reset(loginmsg);
monitor_clear_keystate(ssh, pmonitor);
monitor_child_postauth(ssh, pmonitor);
/* NEVERREACHED */
exit(0);
}
/* child */
close(pmonitor->m_sendfd);
pmonitor->m_sendfd = -1;参考资料
https://www.cnblogs.com/wangliangblog/p/8677619.html
1、客户端保活: options.client_alive_interval options.client_alive_count_max 在wait_until_can_do_something()函数中实现
2、主进程监听客户端连接请求
main()
server_accept_loop()
3、接受客户端连接请求后协商:
1)版本号协商:
main
sshd_exchange_identification
2)算法密钥协商:
main
do_ssh2_kex
3)用户名密码验证:
main
do_authentication2 input_service_request input_service_request authmethod_lookup authmethod_lookup中遍历全局变量 authmethods authmethods->method_passwd method_passwd->userauth_passwd userauth_passwd auth_password sys_auth_passwd
4、验证通过后shell启动流程
main()
do_authenticated
do_authenticated2
server_loop2
debug1: server_init_dispatch debug1: server_input_channel_open: ctype session rchan 0 win 131072 max 32768 debug1: input_session_request debug1: channel 0: new [server-session] debug1: session_new: session 0 debug1: session_open: channel 0 debug1: session_open: session 0: link with channel 0 debug1: server_input_channel_open: confirm session debug1: server_input_channel_req: channel 0 request pty-req reply 1 debug1: session_by_channel: session 0 channel 0 debug1: session_input_channel_req: session 0 req pty-req debug1: Allocating pty. debug1: session_pty_req: session 0 alloc /dev/pts/4 debug1: server_input_channel_req: channel 0 request shell reply 1 debug1: session_by_channel: session 0 channel 0 debug1: session_input_channel_req: session 0 req shell Starting session: shell on pts/4 for root from 10.1.5.200 port 54717 id 0
session_input_channel_req
session_shell_req
do_exec
do_exec_pty
do_child
execve()
5、启动shell过程会写入 /var/run/utmp文件(linux通过w或who命令查看登录用户就是读取的该文件)
do_exec_pty
do_login
record_login
login_login
login_write
utm_write_entry
utmp_perform_logout
utmp_perform_login
utmp_write_library
pututline