openssh认证
更新时间: 2025/12/22
在Gitcode上查看源码

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:2242

BMC中,实现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,均存在两种流程(

  1. mon_dispatch_proto20

    主流程

    c
    struct 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}
    };
  2. mon_dispatch_postauth20:

    认证成功后会执行的流程

    c
    struct 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将结果发送给主进程,例如:

c

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后的判断语句)

c

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