diff --git a/server/tp_core/protocol/ssh/ssh_session.cpp b/server/tp_core/protocol/ssh/ssh_session.cpp index 15656b4..3eda329 100644 --- a/server/tp_core/protocol/ssh/ssh_session.cpp +++ b/server/tp_core/protocol/ssh/ssh_session.cpp @@ -15,6 +15,7 @@ TP_SSH_CHANNEL_PAIR::TP_SSH_CHANNEL_PAIR() { db_id = 0; channel_id = 0; + win_width = 0; is_first_server_data = true; server_ready = false; @@ -630,7 +631,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; @@ -666,10 +667,10 @@ int SshSession::_on_client_channel_data(ssh_session session, ssh_channel channel _this->_process_ssh_command(cp, TP_SSH_CLIENT_SIDE, (ex_u8*)data, _len); - ex_astr str(cp->cmd_char_list.begin(), cp->cmd_char_list.end()); - ex_replace_all(str, "\r", ""); - ex_replace_all(str, "\n", ""); - EXLOGD("[ssh] -- [%s]\n", str.c_str()); +// ex_astr str(cp->cmd_char_list.begin(), cp->cmd_char_list.end()); +// ex_replace_all(str, "\r", ""); +// ex_replace_all(str, "\n", ""); +// EXLOGD("[ssh] -- [%s]\n", str.c_str()); } else { _this->_process_sftp_command(cp, (ex_u8*)data, _len); @@ -742,7 +743,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; @@ -781,10 +782,10 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel } _this->_process_ssh_command(cp, TP_SSH_SERVER_SIDE, (ex_u8*)data, len); - ex_astr str(cp->cmd_char_list.begin(), cp->cmd_char_list.end()); - ex_replace_all(str, "\r", ""); - ex_replace_all(str, "\n", ""); - EXLOGD("[ssh] -- [%s]\n", str.c_str()); +// ex_astr str(cp->cmd_char_list.begin(), cp->cmd_char_list.end()); +// ex_replace_all(str, "\r", ""); +// ex_replace_all(str, "\n", ""); +// EXLOGD("[ssh] -- [%s]\n", str.c_str()); cp->rec.record(TS_RECORD_TYPE_SSH_DATA, (unsigned char *)data, len); } @@ -811,29 +812,15 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel int w = min(cp->win_width, 128); ex_astr line(w, '='); -// char line[129] = { 0 }; -// for (int i = 0; i < w; ++i) -// line[i] = '='; -// snprintf(buf, sizeof(buf), -// "\r\n\r\n"\ -// "=============================================\r\n"\ -// "Welcome to Teleport-SSH-Server...\r\n"\ -// " - teleport to %s:%d\r\n"\ -// " - authroized by %s\r\n"\ -// "=============================================\r\n"\ -// "\r\n", -// _this->m_conn_ip.c_str(), -// _this->m_conn_port, auth_mode -// ); snprintf(buf, sizeof(buf), - "\r\n\r\n"\ + "\r\n"\ "%s\r\n"\ - "Welcome to Teleport-SSH-Server...\r\n"\ + "Teleport SSH Bastion Server...\r\n"\ " - teleport to %s:%d\r\n"\ " - authroized by %s\r\n"\ "%s\r\n"\ - "\r\n", + "\r\n\r\n", line.c_str(), _this->m_conn_ip.c_str(), _this->m_conn_port, auth_mode, @@ -854,15 +841,94 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel } #endif +#if 1 + // 直接转发数据到客户端 if (is_stderr) ret = ssh_channel_write_stderr(cp->cli_channel, data, len); else ret = ssh_channel_write(cp->cli_channel, data, len); +#else + // 分析收到的服务端数据包,如果包含类似 \033]0;AABB\007 这样的数据,客户端会根据此改变窗口标题 + // 我们需要替换这部分数据,使之显示类似 \033]0;TP#ssh://remote-ip\007 这样的标题。 + // 但是这样会降低一些性能,因此目前不启用,保留此部分代码备用。 + if (is_stderr) { + ret = ssh_channel_write_stderr(cp->cli_channel, data, len); + } + else if (cp->type != TS_SSH_CHANNEL_TYPE_SHELL) { + ret = ssh_channel_write(cp->cli_channel, data, len); + } + else { + if (len > 5 && len < 256) { + const ex_u8* _begin = ex_memmem((const ex_u8*)data, len, (const ex_u8*)"\033]0;", 4); + if (NULL != _begin) { + size_t len_before = _begin - (const ex_u8*)data; + const ex_u8* _end = ex_memmem(_begin + 4, len - len_before, (const ex_u8*)"\007", 1); + if (NULL != _end) + { + _end++; + + // 这个包中含有改变标题的数据,将标题换为我们想要的 + EXLOGD("-- found title\n"); + size_t len_end = len - (_end - (const ex_u8*)data); + MemBuffer mbuf; + + if (len_before > 0) + mbuf.append((ex_u8*)data, len_before); + + mbuf.append((ex_u8*)"\033]0;TP#ssh://", 13); + mbuf.append((ex_u8*)_this->m_conn_ip.c_str(), _this->m_conn_ip.length()); + mbuf.append((ex_u8*)"\007", 1); + + if (len_end > 0) + mbuf.append((ex_u8*)_end, len_end); + + if (mbuf.size() > 0) + { + for(;;){ + ret = ssh_channel_write(cp->cli_channel, mbuf.data(), mbuf.size()); + if (ret == SSH_ERROR) + break; + if (ret == mbuf.size()) { + ret = len; // 表示我们已经处理了所有的数据了。 + break; + } + else { + mbuf.pop(ret); + ex_sleep_ms(100); + } + } +// if (ret <= 0) +// EXLOGE("[ssh] send to client failed (1).\n"); +// else +// ret = len; + } + else + { + ret = ssh_channel_write(cp->cli_channel, data, len); + } + } + else + { + ret = ssh_channel_write(cp->cli_channel, data, len); + } + } + else { + ret = ssh_channel_write(cp->cli_channel, data, len); + } + } + else { + ret = ssh_channel_write(cp->cli_channel, data, len); + } + } +#endif if (ret == SSH_ERROR) { EXLOGE("[ssh] send data(%dB) to client failed. [%d][cli:%s][srv:%s]\n", len, ret, ssh_get_error(_this->m_cli_session), ssh_get_error(_this->m_srv_session)); ssh_channel_close(channel); } + else if (ret != len) { + EXLOGW("[ssh] received server data, got %dB, processed %dB.\n", len, ret); + } _this->m_recving_from_srv = false; return ret; @@ -909,8 +975,8 @@ void SshSession::_process_ssh_command(TP_SSH_CHANNEL_PAIR* cp, int from, const e // 客户端输入回车时,可能时执行了一条命令,需要根据服务端返回的数据进行进一步判断 cp->maybe_cmd = (data[len - 1] == 0x0d); - if (cp->maybe_cmd) - EXLOGD("[ssh] maybe cmd.\n"); +// if (cp->maybe_cmd) +// EXLOGD("[ssh] maybe cmd.\n"); // 有时在执行类似top命令的情况下,输入一个字母'q'就退出程序,没有输入回车,可能会导致后续记录命令时将返回的命令行提示符作为命令 // 记录下来了,要避免这种情况,排除的方式是:客户端单个字母,后续服务端如果收到的是控制序列 1b 5b xx xx,就不计做命令。 @@ -1065,13 +1131,13 @@ void SshSession::_process_ssh_command(TP_SSH_CHANNEL_PAIR* cp, int from, const e case 0x0d: { if (offset + 1 < len && data[offset + 1] == 0x0a) { - if (cp->maybe_cmd) - EXLOGD("[ssh] maybe cmd.\n"); +// if (cp->maybe_cmd) +// EXLOGD("[ssh] maybe cmd.\n"); if (cp->maybe_cmd) { if (cp->cmd_char_list.size() > 0) { ex_astr str(cp->cmd_char_list.begin(), cp->cmd_char_list.end()); - EXLOGD("[ssh] --==--==-- save cmd: [%s]\n", str.c_str()); +// EXLOGD("[ssh] --==--==-- save cmd: [%s]\n", str.c_str()); cp->rec.record_command(0, str); } @@ -1113,6 +1179,12 @@ void SshSession::_process_sftp_command(TP_SSH_CHANNEL_PAIR* cp, const ex_u8* dat // SFTP protocol: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13 //EXLOG_BIN(data, len, "[sftp] client channel data"); + // TODO: 根据客户端的请求和服务端的返回,可以进一步判断用户是如何操作文件的,比如读、写等等,以及操作的结果是成功还是失败。 + // 记录格式: time-offset,flag,action,result,file-path,[file-path] + // 其中,flag目前总是为0,可以忽略(为保证与ssh-cmd格式一致),time-offset/action/result 都是数字 + // file-path是被操作的对象,规格为 长度:实际内容,例如, 13:/root/abc.txt + + if (len < 9) return; @@ -1133,7 +1205,7 @@ void SshSession::_process_sftp_command(TP_SSH_CHANNEL_PAIR* cp, const ex_u8* dat // pkg_len + cmd + req_id + string( length + content...) if (len < 14) return; - + ex_u8* str1_ptr = (ex_u8*)data + 9; int str1_len = (int)((str1_ptr[0] << 24) | (str1_ptr[1] << 16) | (str1_ptr[2] << 8) | str1_ptr[3]); // if (str1_len + 9 != pkg_len) @@ -1142,11 +1214,9 @@ void SshSession::_process_sftp_command(TP_SSH_CHANNEL_PAIR* cp, const ex_u8* dat int str2_len = 0;// (int)((data[9] << 24) | (data[10] << 16) | (data[11] << 8) | data[12]); - const char* act = NULL; switch (sftp_cmd) { case 0x03: // 0x03 = 3 = SSH_FXP_OPEN - act = "open file"; break; // case 0x0b: // // 0x0b = 11 = SSH_FXP_OPENDIR @@ -1154,27 +1224,22 @@ void SshSession::_process_sftp_command(TP_SSH_CHANNEL_PAIR* cp, const ex_u8* dat // break; case 0x0d: // 0x0d = 13 = SSH_FXP_REMOVE - act = "remove file"; break; case 0x0e: // 0x0e = 14 = SSH_FXP_MKDIR - act = "create dir"; break; case 0x0f: // 0x0f = 15 = SSH_FXP_RMDIR - act = "remove dir"; break; case 0x12: // 0x12 = 18 = SSH_FXP_RENAME // rename操作数据中包含两个字符串 - act = "rename"; str2_ptr = str1_ptr + str1_len + 4; str2_len = (int)((str2_ptr[0] << 24) | (str2_ptr[1] << 16) | (str2_ptr[2] << 8) | str2_ptr[3]); break; case 0x15: // 0x15 = 21 = SSH_FXP_LINK // link操作数据中包含两个字符串,前者是新的链接文件名,后者是现有被链接的文件名 - act = "create link"; str2_ptr = str1_ptr + str1_len + 4; str2_len = (int)((str2_ptr[0] << 24) | (str2_ptr[1] << 16) | (str2_ptr[2] << 8) | str2_ptr[3]); break; @@ -1191,12 +1256,12 @@ void SshSession::_process_sftp_command(TP_SSH_CHANNEL_PAIR* cp, const ex_u8* dat char msg[2048] = { 0 }; if (str2_len == 0) { ex_astr str1((char*)((ex_u8*)data + 13), str1_len); - ex_strformat(msg, 2048, "%d:%s:%s:\r\n", sftp_cmd, act, str1.c_str()); + ex_strformat(msg, 2048, "%d,%d,%s", sftp_cmd, 0, str1.c_str()); } else { ex_astr str1((char*)(str1_ptr + 4), str1_len); ex_astr str2((char*)(str2_ptr + 4), str2_len); - ex_strformat(msg, 2048, "%d:%s:%s:%s\r\n", sftp_cmd, act, str1.c_str(), str2.c_str()); + ex_strformat(msg, 2048, "%d,%d,%s:%s", sftp_cmd, 0, str1.c_str(), str2.c_str()); } cp->rec.record_command(0, msg); diff --git a/server/www/teleport/view/audit/record-sftp-cmd.mako b/server/www/teleport/view/audit/record-sftp-cmd.mako index ef73d30..11bde0d 100644 --- a/server/www/teleport/view/audit/record-sftp-cmd.mako +++ b/server/www/teleport/view/audit/record-sftp-cmd.mako @@ -8,9 +8,13 @@ <%block name="extend_css"> +