From 711cb139e79f6fa31b5edf6c8df2a713f3f8dc67 Mon Sep 17 00:00:00 2001 From: Apex Liu Date: Thu, 30 Nov 2017 01:25:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E5=AF=B9SSHv1=E7=9A=84?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=8C=E4=BE=8B=E5=A6=82=E4=B8=8D=E5=85=81?= =?UTF-8?q?=E8=AE=B8=E6=89=93=E5=BC=80SFTP=E3=80=81=E4=B8=8D=E5=85=81?= =?UTF-8?q?=E8=AE=B8=E5=BB=BA=E7=AB=8B=E5=A4=9A=E4=B8=AA=E9=80=9A=E9=81=93?= =?UTF-8?q?=E7=AD=89=E7=AD=89=E3=80=82=E4=B8=8E=E5=AE=A2=E6=88=B7=E7=AB=AF?= =?UTF-8?q?=E7=9B=B4=E8=BF=9E=E8=BF=9C=E7=A8=8B=E4=B8=BB=E6=9C=BA=E4=BE=9D?= =?UTF-8?q?=E7=84=B6=E6=9C=89=E4=B8=80=E4=BA=9B=E5=B7=AE=E5=BC=82=EF=BC=8C?= =?UTF-8?q?=E5=9B=A0=E4=B8=BA=E5=AE=A2=E6=88=B7=E7=AB=AF=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?TP=E6=97=B6=E6=80=BB=E6=98=AF=E4=BD=BF=E7=94=A8=E7=9A=84SSHv2?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=EF=BC=8C=E6=89=80=E4=BB=A5=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E5=87=86=E7=A1=AE=E5=88=A4=E6=96=AD=E8=BF=9C=E7=A8=8B=E4=B8=BB?= =?UTF-8?q?=E6=9C=BA=E7=9A=84=E5=8D=8F=E8=AE=AE=E7=89=88=E6=9C=AC=E4=BB=8E?= =?UTF-8?q?=E8=80=8C=E5=AF=BC=E8=87=B4=E4=B8=80=E4=BA=9B=E8=A1=8C=E4=B8=BA?= =?UTF-8?q?=E4=B8=8A=E7=9A=84=E5=B7=AE=E5=BC=82=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/tp_core/protocol/ssh/ssh_session.cpp | 108 +++++++++--------- .../teleport/static/js/audit/replay-ssh.js | 4 + 2 files changed, 55 insertions(+), 57 deletions(-) diff --git a/server/tp_core/protocol/ssh/ssh_session.cpp b/server/tp_core/protocol/ssh/ssh_session.cpp index 939d9cb..10bc46b 100644 --- a/server/tp_core/protocol/ssh/ssh_session.cpp +++ b/server/tp_core/protocol/ssh/ssh_session.cpp @@ -214,24 +214,6 @@ void SshSession::_check_channels() { m_channels.erase(it++); } else { - // if ((*it)->type == TS_SSH_CHANNEL_TYPE_SHELL) { - // // 尝试从服务器读取点东西 - // ex_u8 buf[4096] = { 0 }; - // int len = 0; - // - // len = ssh_channel_read_nonblocking(srv, buf, 4096, 0); - // if (len < 0) { - // EXLOGE("[ssh] -- try read failed. srv:err:%s\n", ssh_get_error(m_srv_session)); - // EXLOGE("[ssh] -- try read failed. cli:err:%s\n", ssh_get_error(m_cli_session)); - // ssh_channel_close(srv); - // } - // else if(len > 0) { - // EXLOGD("[ssh] -- try read server ok.\n"); - // EXLOG_BIN(buf, len, "RECV\n"); - // _on_server_channel_data(m_srv_session, srv, buf, len, 0, this); - // } - // } - ++it; } } @@ -342,13 +324,10 @@ void SshSession::_run(void) { - // 如果是SSHv2 - //if (m_ssh_ver == 2) { - // 现在双方的连接已经建立好了,开始转发 + // 现在双方的连接已经建立好了,开始转发 ssh_event_add_session(event_loop, m_srv_session); do { err = ssh_event_dopoll(event_loop, 5000); - //EXLOGD("ssh_event_dopoll() return %d.\n", r); if (err == SSH_ERROR) { if (0 != ssh_get_error_code(m_cli_session)) { @@ -380,14 +359,13 @@ void SshSession::_run(void) { ssh_event_remove_session(event_loop, m_cli_session); ssh_event_remove_session(event_loop, m_srv_session); ssh_event_free(event_loop); - //} - // TODO: 这里还是有问题,如果一边是走SSHv1,另一边是SSHv2,放在同一个event_loop时,SSHv1会收不到数据,放到循环中时,SSHv2得不到数据 + // 如果一边是走SSHv1,另一边是SSHv2,放在同一个event_loop时,SSHv1会收不到数据,放到循环中时,SSHv2得不到数据 + // 所以,当SSHv1的远程主机连接后,到建立好shell环境之后,就进入另一种读取数据的循环,不再使用ssh_event_dopoll()了。 if (m_ssh_ver == 1) { - tp_channels::iterator it = m_channels.begin(); // SSHv1只能打开一个channel ssh_channel cli = (*it)->cli_channel; ssh_channel srv = (*it)->srv_channel; @@ -399,31 +377,32 @@ void SshSession::_run(void) { if (ok) { len = ssh_channel_read_nonblocking(cli, buf, 4096, 0); - if (len < 0) { - EXLOGE("[ssh] -- try read failed. srv:err:%s\n", ssh_get_error(m_srv_session)); - EXLOGE("[ssh] -- try read failed. cli:err:%s\n", ssh_get_error(m_cli_session)); + if (len < 0) ok = false; - } - else if (len > 0) { - EXLOGD("[ssh] -- try read client ok.\n"); - //EXLOG_BIN(buf, len, "RECV\n"); + else if (len > 0) _on_client_channel_data(m_cli_session, cli, buf, len, 0, this); - } + + len = ssh_channel_read_nonblocking(cli, buf, 4096, 1); + if (len < 0) + ok = false; + else if (len > 0) + _on_client_channel_data(m_cli_session, cli, buf, len, 1, this); len = ssh_channel_read_nonblocking(srv, buf, 4096, 0); - if (len < 0) { - EXLOGE("[ssh] -- try read failed. srv:err:%s\n", ssh_get_error(m_srv_session)); - EXLOGE("[ssh] -- try read failed. cli:err:%s\n", ssh_get_error(m_cli_session)); + if (len < 0) ok = false; - } - else if (len > 0) { - EXLOGD("[ssh] -- try read server ok.\n"); - //EXLOG_BIN(buf, len, "RECV\n"); + else if (len > 0) _on_server_channel_data(m_srv_session, srv, buf, len, 0, this); - } - if (!ok) + len = ssh_channel_read_nonblocking(srv, buf, 4096, 1); + if (len < 0) + ok = false; + else if (len > 0) + _on_server_channel_data(m_srv_session, srv, buf, len, 1, this); + + if (!ok) { _close_channels(); + } } if (!ok) { @@ -434,6 +413,8 @@ void SshSession::_run(void) { ex_sleep_ms(30); } while (m_channels.size() > 0); + + EXLOGV("[ssh] [%s:%d] all channel in this session are closed.\n", m_client_ip.c_str(), m_client_port); } } @@ -501,7 +482,7 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user, // int val = 0; // ssh_options_set(_this->m_srv_session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &val); - + int rc = 0; rc = ssh_connect(_this->m_srv_session); if (rc != SSH_OK) { @@ -514,7 +495,7 @@ int SshSession::_on_auth_password_request(ssh_session session, const char *user, _timeout = 10; // 10 sec. ssh_options_set(_this->m_srv_session, SSH_OPTIONS_TIMEOUT, &_timeout); - // TODO: 获取服务端ssh版本,是v1还是v2 + // 获取服务端ssh版本,是v1还是v2 _this->m_ssh_ver = ssh_get_version(_this->m_srv_session); EXLOGW("[ssh] real host is SSHv%d\n", _this->m_ssh_ver); @@ -668,6 +649,13 @@ ssh_channel SshSession::_on_new_channel_request(ssh_session session, void *userd SshSession *_this = (SshSession *)userdata; + // TODO: 客户端与TP连接使用的总是SSHv2协议,因为最开始连接时还不知道真正的远程主机是不是SSHv1。 + // 因此此处行为与客户端直连远程主机有些不一样。直连时,SecureCRT的克隆会话功能会因为以为连接的是SSHv1而自动重新连接,而不是打开新通道。 + if (_this->m_ssh_ver == 1 && _this->m_channels.size() != 0) { + EXLOGE("[ssh] SSH1 supports only one execution channel. One has already been opened.\n"); + return NULL; + } + ssh_channel cli_channel = ssh_channel_new(session); if (cli_channel == NULL) { EXLOGE("[ssh] can not create channel for client.\n"); @@ -803,7 +791,7 @@ void SshSession::_on_client_channel_close(ssh_session session, ssh_channel chann int SshSession::_on_client_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata) { - //EXLOG_BIN((ex_u8*)data, len, "on_client_channel_data [is_stderr=%d]:", is_stderr); + //EXLOG_BIN((ex_u8*)data, len, " ---> on_client_channel_data [is_stderr=%d]:", is_stderr); SshSession *_this = (SshSession *)userdata; @@ -885,6 +873,12 @@ int SshSession::_on_client_channel_subsystem_request(ssh_session session, ssh_ch EXLOGD("[ssh] on_client_channel_subsystem_request(): %s\n", subsystem); SshSession *_this = (SshSession *)userdata; + if (_this->m_ssh_ver == 1) { + // SSHv1 not support subsystem, so some client like WinSCP will use shell-mode instead. + EXLOGE("[ssh] real host running on SSHv1, does not support subsystem `%s`.\n", subsystem); + return SSH_ERROR; + } + TP_SSH_CHANNEL_PAIR* cp = _this->_get_channel_pair(TP_SSH_CLIENT_SIDE, channel); if (NULL == cp) { EXLOGE("[ssh] when request channel subsystem, not found channel pair.\n"); @@ -915,7 +909,7 @@ int SshSession::_on_client_channel_exec_request(ssh_session session, ssh_channel } int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata) { - //EXLOG_BIN((ex_u8*)data, len, "on_server_channel_data [is_stderr=%d]:", is_stderr); + //EXLOG_BIN((ex_u8*)data, len, " <--- on_server_channel_data [is_stderr=%d]:", is_stderr); SshSession *_this = (SshSession *)userdata; @@ -931,17 +925,17 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel return SSH_ERROR; } -#ifdef EX_OS_WIN32 - // TODO: hard code not good... :( - // 偶尔,某次操作会导致ssh_session->session_state为SSH_SESSION_STATE_ERROR - // 但是将其强制改为SSH_SESSION_STATE_AUTHENTICATED,后续操作仍然能成功(主要在向客户端发送第一包数据时) - ex_u8* _t = (ex_u8*)(ssh_channel_get_session(cp->cli_channel)); - if (_t[1116] == 9) // SSH_SESSION_STATE_AUTHENTICATED = 8, SSH_SESSION_STATE_ERROR = 9 - { - EXLOGW(" --- [ssh] hard code to fix client connect session error state.\n"); - _t[1116] = 8; - } -#endif +// #ifdef EX_OS_WIN32 +// // TODO: hard code not good... :( +// // 偶尔,某次操作会导致ssh_session->session_state为SSH_SESSION_STATE_ERROR +// // 但是将其强制改为SSH_SESSION_STATE_AUTHENTICATED,后续操作仍然能成功(主要在向客户端发送第一包数据时) +// ex_u8* _t = (ex_u8*)(ssh_channel_get_session(cp->cli_channel)); +// if (_t[1116] == 9) // SSH_SESSION_STATE_AUTHENTICATED = 8, SSH_SESSION_STATE_ERROR = 9 +// { +// EXLOGW(" --- [ssh] hard code to fix client connect session error state.\n"); +// _t[1116] = 8; +// } +// #endif _this->m_recving_from_srv = true; diff --git a/server/www/teleport/static/js/audit/replay-ssh.js b/server/www/teleport/static/js/audit/replay-ssh.js index 17e4abf..a2823f2 100644 --- a/server/www/teleport/static/js/audit/replay-ssh.js +++ b/server/www/teleport/static/js/audit/replay-ssh.js @@ -78,6 +78,10 @@ $app.on_init = function (cb_stack) { function (ret) { if (ret.code === TPE_OK) { g_header = ret.data; + if(g_header.width === 0) + g_header.width = 80; + if(g_header.height === 0) + g_header.height = 24; console.log('header', g_header); $('#recorder-info').html(tp_format_datetime(g_header.start) + ': ' + g_header.user_name + '@' + g_header.client_ip + ' 璁块棶 ' + g_header.account + '@' + g_header.conn_ip + ':' + g_header.conn_port);