From 7f973525f70fd23ee2dfa716624862e45e5ae96b Mon Sep 17 00:00:00 2001 From: Iru Cai Date: Mon, 21 May 2018 14:03:44 +0800 Subject: parse_ssh{1,2}_packet C code --- src/protocol/internal/fqterm_ssh1_packet.cpp | 88 +++++------------- src/protocol/internal/fqterm_ssh1_packet.h | 1 + src/protocol/internal/fqterm_ssh2_packet.cpp | 120 ++++++------------------ src/protocol/internal/fqterm_ssh2_packet.h | 5 +- src/protocol/internal/ssh_error.h | 10 +- src/protocol/internal/ssh_packet.c | 134 ++++++++++++++++++++++++++- src/protocol/internal/ssh_packet.h | 4 + 7 files changed, 194 insertions(+), 168 deletions(-) diff --git a/src/protocol/internal/fqterm_ssh1_packet.cpp b/src/protocol/internal/fqterm_ssh1_packet.cpp index 7510194..eda4fd1 100644 --- a/src/protocol/internal/fqterm_ssh1_packet.cpp +++ b/src/protocol/internal/fqterm_ssh1_packet.cpp @@ -35,73 +35,27 @@ namespace FQTerm { cipher = new_3des_ssh1(0); } -void FQTermSSH1PacketReceiver::parseData(buffer *input) { - u_int mycrc, gotcrc; - u_char *buf = NULL; - u_char *targetData = NULL; - u_char *sourceData = NULL; - - // Get the length of the packet. - while (buffer_len(input) > 0) { - if (buffer_len(input) < 4) { - FQ_TRACE("ssh1packet", 3) << "The packet is too small."; - return ; - } - buf = buffer_data(input); - real_data_len_ = ntohu32(buf); - - if (real_data_len_ > SSH_BUFFER_MAX) { - emit packetError(tr("parseData: The packet is too big")); - return ; - } - - u_int total_len = (real_data_len_ + 8) &~7; - u_int padding_len = total_len - real_data_len_; - - real_data_len_ -= 5; - buffer_clear(&recvbuf); - - // Get the data of the packet. - if (buffer_len(input) - 4 < (long)total_len) { - FQ_TRACE("ssh1packet", 3) << "The packet is too small"; - return ; - } - - real_data_len_ = buffer_get_u32(input) - 5; - targetData = new u_char[total_len]; - sourceData = new u_char[total_len]; - memset(targetData, 0, total_len); - memset(sourceData, 0, total_len); - - buffer_get(input, sourceData, total_len); - if (cipher->started) { - cipher->crypt(cipher, sourceData, targetData, total_len); - } else { - memcpy(targetData, sourceData, total_len); - } - - buffer_append(&recvbuf, targetData, total_len); - - // Check the crc32. - buf = buffer_data(&recvbuf) + total_len - 4; - mycrc = ntohu32(buf); - gotcrc = ssh_crc32(buffer_data(&recvbuf), total_len - 4); - - if (mycrc != gotcrc) { - emit packetError(tr("parseData: bad CRC32")); - break; - } - - // Drop the padding. - buffer_consume(&recvbuf, padding_len); - - packet_type_ = buffer_get_u8(&recvbuf); - - emit packetAvaliable(packet_type_); - - delete [] sourceData; - delete [] targetData; - } +void FQTermSSH1PacketReceiver::parseData(buffer *input) +{ + while (buffer_len(input) > 0) { + int res = parse_ssh1_packet(input, &recvbuf, cipher); + switch (res) { + case -ETOOBIG: + emit packetError("Packet too big!"); + return; + case -ETOOSMALL: + return; + case -ECRC32: + emit packetError("CRC32 error!"); + return; + default: + real_data_len_ = res; + } + + packet_type_ = buffer_get_u8(&recvbuf); + + emit packetAvaliable(packet_type_); + } } } // namespace FQTerm diff --git a/src/protocol/internal/fqterm_ssh1_packet.h b/src/protocol/internal/fqterm_ssh1_packet.h index e8a0806..f15ea57 100644 --- a/src/protocol/internal/fqterm_ssh1_packet.h +++ b/src/protocol/internal/fqterm_ssh1_packet.h @@ -22,6 +22,7 @@ #define FQTERM_SSH1_PACKET_H #include "fqterm_ssh_packet.h" +#include "ssh_error.h" namespace FQTerm { diff --git a/src/protocol/internal/fqterm_ssh2_packet.cpp b/src/protocol/internal/fqterm_ssh2_packet.cpp index 0345883..edcee34 100644 --- a/src/protocol/internal/fqterm_ssh2_packet.cpp +++ b/src/protocol/internal/fqterm_ssh2_packet.cpp @@ -24,102 +24,36 @@ #include "ssh_endian.h" #include "buffer.h" #include "ssh_packet.h" +#include "ssh_error.h" namespace FQTerm { -//============================================================================== -//FQTermSSH2PacketReceiver -//============================================================================== -void FQTermSSH2PacketReceiver::parseData(buffer *input) { - FQ_TRACE("ssh2packet", 9) << "----------------------------Receive " - << (cipher->started ? "Encrypted": "plain") - << " Packet----<<<<<<<"; - while (buffer_len(input) > 0) { - // 1. Check the ssh packet - if (buffer_len(input) < 16 - || (cipher->started && buffer_len(input) < cipher->blkSize) - || buffer_len(input) < last_expected_input_length_ - ) { - FQ_TRACE("ssh2packet", 3) - << "Got an incomplete packet. Wait for more data."; - return ; - } - if (last_expected_input_length_ == 0) { - if (cipher->started) { - // decrypte the first block to get the packet_length field. - FQ_VERIFY(cipher->crypt(cipher, buffer_data(input), buffer_data(input), cipher->blkSize)==1); - } - } else { - // last_expected_input_length_ != 0 - // indicates an incomplete ssh2 packet received last time, - // the first block of data is already decrypted at that time, - // so it must not be decrypted again. - } - - int packet_len = ntohu32(buffer_data(input)); - - if (packet_len > SSH_BUFFER_MAX) { - emit packetError(tr("parseData: packet too big")); - return ; - } - - int expected_input_len = 4 + packet_len + (is_mac_ ? mac->dgstSize : 0); - - if (buffer_len(input) < (long)expected_input_len) { - FQ_TRACE("ssh2packet", 3) - << "The packet is too small. Wait for more data."; - last_expected_input_length_ = expected_input_len; - return ; - } else { - last_expected_input_length_ = 0; - } - - // 2. decrypte data. - if (cipher->started) { - // decrypte blocks left. - unsigned char *tmp = buffer_data(input) + cipher->blkSize; - int left_len = expected_input_len - cipher->blkSize - mac->dgstSize; - FQ_VERIFY(cipher->crypt(cipher, tmp, tmp, left_len)==1); - } - - // 3. check MAC - if (is_mac_) { - int digest_len = mac->dgstSize; - uint8_t digest[MAX_DGSTLEN]; - - buffer mbuf; - buffer_init(&mbuf); - buffer_append_be32(&mbuf, sequence_no_); - buffer_append(&mbuf, (const uint8_t*)buffer_data(input), - expected_input_len - digest_len); - mac->getmac(mac, buffer_data(&mbuf), buffer_len(&mbuf), digest); - buffer_deinit(&mbuf); - - u_char *received_digest = buffer_data(input) + expected_input_len - digest_len; - - if (memcmp(digest, received_digest, digest_len) != 0) { - emit packetError("incorrect MAC."); - return ; - } - } - - // 4. get every field of the ssh packet. - packet_len = buffer_get_u32(input); - uint8_t padding_len = buffer_get_u8(input); - real_data_len_ = packet_len - 1 - padding_len; - buffer_clear(&recvbuf); - buffer_append(&recvbuf, buffer_data(input), real_data_len_); - buffer_consume(input, packet_len - 1); - if (is_mac_) - buffer_consume(input, mac->dgstSize); - - // 5. notify others a ssh packet is parsed successfully. - packet_type_ = buffer_get_u8(&recvbuf); - real_data_len_ -= 1; - emit packetAvaliable(packet_type_); - - ++sequence_no_; - } +void FQTermSSH2PacketReceiver::parseData(buffer *input) +{ + while (buffer_len(input) > 0) { + int res = parse_ssh2_packet(input, &recvbuf, cipher, + mac, is_mac_, &decrypted, &sequence_no_); + switch (res) { + case -ETOOSMALL: + return; + case -ETOOBIG: + emit packetError("Packet too big!"); + return; + case -ECRYPT: + emit packetError("Decrypt error!"); + return; + case -EMAC: + emit packetError("MAC error!"); + return; + default: + real_data_len_ = res; + } + + // 5. notify others a ssh packet is parsed successfully. + packet_type_ = buffer_get_u8(&recvbuf); + real_data_len_ -= 1; + emit packetAvaliable(packet_type_); + } } } // namespace FQTerm diff --git a/src/protocol/internal/fqterm_ssh2_packet.h b/src/protocol/internal/fqterm_ssh2_packet.h index c670203..f79f7cf 100644 --- a/src/protocol/internal/fqterm_ssh2_packet.h +++ b/src/protocol/internal/fqterm_ssh2_packet.h @@ -29,10 +29,9 @@ namespace FQTerm { class FQTermSSH2PacketReceiver: public FQTermSSHPacketReceiver { private: - // greater than 0 if last time an incomplete ssh2 packet received. - int last_expected_input_length_; + uint32_t decrypted; public: -FQTermSSH2PacketReceiver() : last_expected_input_length_(0) { } +FQTermSSH2PacketReceiver() : decrypted(0) { } virtual void parseData(buffer *input); }; diff --git a/src/protocol/internal/ssh_error.h b/src/protocol/internal/ssh_error.h index 1a0fd32..97d814d 100644 --- a/src/protocol/internal/ssh_error.h +++ b/src/protocol/internal/ssh_error.h @@ -2,9 +2,13 @@ #define SSH_ERROR_H enum { - ESECRET = 1, - ERSA, - ECRYPT, + ESECRET = 1, /* error comupting DH secret */ + ERSA, /* RSA error */ + ECRYPT, /* encrypt/decrypt error */ + ETOOSMALL, /* buffer too small */ + ETOOBIG, /* buffer too big */ + ECRC32, /* CRC32 error */ + EMAC, /* MAC error */ }; #endif diff --git a/src/protocol/internal/ssh_packet.c b/src/protocol/internal/ssh_packet.c index 6aad6d9..724eb61 100644 --- a/src/protocol/internal/ssh_packet.c +++ b/src/protocol/internal/ssh_packet.c @@ -20,14 +20,15 @@ #include "ssh_packet.h" #include "crc32.h" #include "ssh_error.h" +#include "ssh_endian.h" //============================================================================== // // SSH1 Packet Structure: // -------------------------------------------------------------------------- -// | length | padding | type | data | crc32 | +// | length | padding | type | data | crc32 | // -------------------------------------------------------------------------- -// | uint32 | 1-7bytes | uchar | | 4bytes| +// | uint32 | 1-8 bytes | uchar | | 4bytes| // -------------------------------------------------------------------------- // encrypt = padding + type + data + crc32 // length = type + data + crc32 @@ -61,6 +62,58 @@ void make_ssh1_packet(buffer *orig_data, buffer *data_to_send, } } +int parse_ssh1_packet(buffer *input, buffer *recvbuf, SSH_CIPHER *cipher) +{ + uint32_t mycrc, gotcrc; + uint8_t *targetData = NULL; + uint8_t *sourceData = NULL; + int real_data_len; + + // Get the length of the packet. + if (buffer_len(input) < 4) + return -ETOOSMALL; + + uint8_t *buf = buffer_data(input); + real_data_len = ntohu32(buf); + + if (real_data_len > SSH_BUFFER_MAX) + return -ETOOBIG; + + uint32_t total_len = (real_data_len + 8) & ~7; + uint32_t padding_len = total_len - real_data_len; + + // Get the data of the packet. + if (buffer_len(input) - 4 < (long)total_len) + return -ETOOSMALL; + + buffer_consume(input, 4); + real_data_len -= 5; + + buffer_clear(recvbuf); + uint8_t *recv_ptr = buffer_data(recvbuf); + buffer_append(recvbuf, NULL, total_len); + + if (cipher->started) { + cipher->crypt(cipher, buffer_data(input), recv_ptr, total_len); + } else { + memcpy(recv_ptr, buffer_data(input), total_len); + } + + buffer_consume(input, total_len); + + // Check the crc32. + buf = buffer_data(recvbuf) + total_len - 4; + mycrc = ntohu32(buf); + gotcrc = ssh_crc32(buffer_data(recvbuf), total_len - 4); + + if (mycrc != gotcrc) + return -ECRC32; + + // Drop the padding. + buffer_consume(recvbuf, padding_len); + return real_data_len; +} + //============================================================================== // SSH2 Packet Structure: // uint32 packet_length @@ -140,3 +193,80 @@ int make_ssh2_packet(buffer *orig_data, buffer *data_to_send, *seq = *seq + 1; return 0; } + +int parse_ssh2_packet(buffer *input, buffer *recvbuf, SSH_CIPHER *cipher, + SSH_MAC *mac, bool is_mac_, uint32_t *decrypted, uint32_t *seq) +{ + int packet_len; + uint8_t *input_data = buffer_data(input); + + // 1. Check the ssh packet + if (*decrypted == 0) { + if (buffer_len(input) < 16) + return -ETOOSMALL; + if (cipher->started && buffer_len(input) < cipher->blkSize) + return -ETOOSMALL; + } + + if (*decrypted == 0 && cipher->started) { + if (cipher->crypt(cipher, input_data, input_data, + cipher->blkSize) != 1) + return -ECRYPT; + packet_len = ntohu32(input_data); + *decrypted = cipher->blkSize; + } else { + packet_len = ntohu32(input_data); + } + + if (packet_len > SSH_BUFFER_MAX) + return -ETOOBIG; + + int expected_input_len = 4 + packet_len + (is_mac_ ? mac->dgstSize : 0); + + if (buffer_len(input) < (long)expected_input_len) + return -ETOOSMALL; + + // 2. decrypte data. + if (cipher->started) { + /* TODO: what about having cipher but no MAC? */ + unsigned char *tmp = input_data + *decrypted; + int left_len = expected_input_len - *decrypted; + if (is_mac_) + left_len -= mac->dgstSize; + if (cipher->crypt(cipher, tmp, tmp, left_len) != 1) + return -ECRYPT; + } + *decrypted = 0; + + // 3. check MAC + if (is_mac_) { + int digest_len = mac->dgstSize; + uint8_t digest[MAX_DGSTLEN]; + + buffer mbuf; + buffer_init(&mbuf); + buffer_append_be32(&mbuf, *seq); + buffer_append(&mbuf, (const uint8_t *)buffer_data(input), + expected_input_len - digest_len); + mac->getmac(mac, buffer_data(&mbuf), buffer_len(&mbuf), digest); + buffer_deinit(&mbuf); + + uint8_t *received_digest = + input_data + expected_input_len - digest_len; + + if (memcmp(digest, received_digest, digest_len) != 0) + return -EMAC; + } + + // 4. get every field of the ssh packet. + packet_len = buffer_get_u32(input); + uint8_t padding_len = buffer_get_u8(input); + int real_data_len = packet_len - 1 - padding_len; + buffer_clear(recvbuf); + buffer_append(recvbuf, buffer_data(input), real_data_len); + buffer_consume(input, packet_len - 1); + if (is_mac_) + buffer_consume(input, mac->dgstSize); + *seq = *seq + 1; + return real_data_len; +} diff --git a/src/protocol/internal/ssh_packet.h b/src/protocol/internal/ssh_packet.h index d8cdaf2..f2ca6e1 100644 --- a/src/protocol/internal/ssh_packet.h +++ b/src/protocol/internal/ssh_packet.h @@ -31,6 +31,10 @@ extern "C" { void make_ssh1_packet(buffer *src, buffer *dest, SSH_CIPHER *); int make_ssh2_packet(buffer *src, buffer *dest, SSH_CIPHER *, SSH_MAC *, bool is_mac_, uint32_t *seq); +/* parse_ssh{1,2}_packet: return the length of the received data */ +int parse_ssh1_packet(buffer *input, buffer *output, SSH_CIPHER *cipher); +int parse_ssh2_packet(buffer *input, buffer *recvbuf, SSH_CIPHER *cipher, + SSH_MAC *mac, bool is_mac, uint32_t *decrypted, uint32_t *seq); #ifdef __cplusplus } -- cgit v1.2.3