diff --git a/src/app-layer-ssh.c b/src/app-layer-ssh.c index 0a2bf5fa0c..a46c0f02d3 100644 --- a/src/app-layer-ssh.c +++ b/src/app-layer-ssh.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2010 Open Information Security Foundation +/* Copyright (C) 2007-2014 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -52,664 +52,312 @@ #include "util-byte.h" #include "util-memcmp.h" -#if 0 -/** - * \brief Function to parse the SSH version string of the server +/** \internal + * \brief Function to parse the SSH version string of the client + * + * The input to this function is a byte buffer starting with SSH- * * \param ssh_state Pointer the state in which the value to be stored - * \param pstate Application layer tarser state for this session * \param input Pointer the received input data * \param input_len Length in bytes of the received data - * \param output Pointer to the list of parsed output elements */ -static int SSHParseServerVersion(Flow *f, void *ssh_state, AppLayerParserState *pstate, - uint8_t *input, uint32_t input_len, - AppLayerParserResult *output) { - uint8_t *line_ptr = input; +static int SSHParseVersion(SshState *state, SshHeader *header, const uint8_t *input, uint32_t input_len) +{ + const uint8_t *line_ptr = input; uint32_t line_len = input_len; - uint32_t offset = 0; - SshState *state = (SshState *)ssh_state; - - while (input_len > 0) { - offset = 0; + /* is it the version line? */ + if (SCMemcmp("SSH-", line_ptr, 4) != 0) { + SCReturnInt(-1); + } + if (line_len > 255) { + SCLogInfo("Invalid version string, it should be less than 255 characters including "); + SCReturnInt(-1); + } - if (pstate->store_len > 0){ - const uint8_t delim[] = { 0x0a, }; - int r = AlpParseFieldByDelimiter(output, pstate, - SSH_FIELD_SERVER_VER_STATE_LINE, delim, sizeof(delim), - input, input_len, &offset); + /* ok, we have found the version line/string, skip it and parse proto version */ + line_ptr += 4; + line_len -= 4; - if (r == 0) - SCReturnInt(0); + uint8_t *proto_end = BasicSearch(line_ptr, line_len, (uint8_t*)"-", 1); + if (proto_end == NULL) { + /* Strings starting with SSH- are not allowed + * if they are not the real version string */ + SCLogDebug("Info Version String for SSH (invalid usage of SSH- prefix)"); + SCReturnInt(-1); + } + uint64_t proto_ver_len = (uint64_t)(proto_end - line_ptr); + header->proto_version = SCMalloc(proto_ver_len + 1); + if (header->proto_version == NULL) { + SCReturnInt(-1); + } + memcpy(header->proto_version, line_ptr, proto_ver_len); + header->proto_version[proto_ver_len] = '\0'; - /* process the result elements */ - AppLayerParserResultElmt *e = output->head; - line_ptr = NULL; - line_len = 0; - for (; e != NULL; e = e->next) { - SCLogDebug("e %p e->name_idx %" PRIu32 ", e->data_ptr %p, e->data_len " - "%" PRIu32, e, e->name_idx, - e->data_ptr, e->data_len); - - /* no parser defined for this field. */ - if (e->name_idx != SSH_FIELD_SERVER_VER_STATE_LINE) { - continue; - } - - line_ptr = e->data_ptr; - line_len = e->data_len; - } + /* Now lets parse the software & version */ + line_ptr += proto_ver_len + 1; + line_len -= proto_ver_len + 1; + if (line_len < 1) { + SCLogInfo("No software version specified (weird)"); + header->flags |= SSH_FLAG_VERSION_PARSED; + /* Return the remaining length */ + SCReturnInt(0); + } - /* Update for the next round */ - input_len -= offset; - input += offset; + const uint8_t *sw_end = BasicSearch(line_ptr, line_len, (uint8_t*)" ", 1); + if (sw_end == NULL) { + sw_end = BasicSearch(line_ptr, line_len, (uint8_t*)"\r", 1); + if (sw_end == NULL) { + sw_end = line_ptr + line_len; + } + } - if (line_ptr == NULL) - continue; - } else { - const uint8_t delim[] = { 0x0a, }; - int r = AlpParseFieldByDelimiter(output, pstate, - SSH_FIELD_SERVER_VER_STATE_LINE, delim, sizeof(delim), - input, input_len, &offset); + uint64_t sw_ver_len = (uint64_t)(sw_end - line_ptr); + header->software_version = SCMalloc(sw_ver_len + 1); + if (header->software_version == NULL) { + SCReturnInt(-1); + } + memcpy(header->software_version, line_ptr, sw_ver_len); + header->software_version[sw_ver_len] = '\0'; + if (header->software_version[sw_ver_len - 1] == 0x0d) + header->software_version[sw_ver_len - 1] = '\0'; - if (r == 0) - SCReturnInt(0); + header->flags |= SSH_FLAG_VERSION_PARSED; - /* Temporal pointer / len for the current line */ - line_ptr = input; - line_len = offset; + /* Return the remaining length */ + int len = input_len - (sw_end - input); + SCReturnInt(len); +} - /* Update for the next round */ - input_len -= offset; - input += offset; - } +static int SSHParseClientRecordHeader(SshState *state, SshHeader *header, uint8_t *input, uint32_t input_len) +{ + BUG_ON(input_len != 6); + + /* input and input_len now point past initial line */ + uint32_t pkt_len = 0; + int r = ByteExtractUint32(&pkt_len, BYTE_BIG_ENDIAN, + 4, input); + if (r != 4) { + SCLogInfo("xtract 4 bytes failed %d", r); + SCReturnInt(-1); + } + if (pkt_len < 2) { + SCReturnInt(-1); + } - //printf("INPUT: \n"); - //PrintRawDataFp(stdout, line_ptr, line_len); + header->pkt_len = pkt_len; + SCLogInfo("pkt len: %"PRIu32, pkt_len); - if (line_len < 5) { - SCLogDebug("This is not the version line we are searching for (probably a banner or informational messages)"); - continue; - } + input += 4; + input_len -= 4; - /* is it the version line? */ - if (SCMemcmp("SSH-", line_ptr, 4) == 0) { - if (line_len > 255) { - SCLogDebug("Invalid version string, it should be less than 255 characters including "); - SCReturnInt(-1); - } + header->padding_len = *input; - /* ok, we have found the version line/string, skip it and parse proto version */ - line_ptr += 4; - line_len -= 4; - } else { - SCLogDebug("This is not the version line we are searching for (probably a banner or informational messages)"); - continue; - } + input += 1; + input_len -= 1; - uint8_t *proto_end = BasicSearch(line_ptr, line_len, (uint8_t*)"-", 1); - if (proto_end == NULL) { - /* Strings starting with SSH- are not allowed - * if they are not the real version string */ - SCLogDebug("Invalid Version String for SSH (invalid usage of SSH- prefix)"); - SCReturnInt(-1); - } + SCLogInfo("padding: %u", header->padding_len); - uint64_t proto_ver_len = (uint64_t)(proto_end - line_ptr); - state->server_proto_version = SCMalloc(proto_ver_len + 1); - if (state->server_proto_version == NULL) { - SCReturnInt(-1); - } - memcpy(state->server_proto_version, line_ptr, proto_ver_len); - state->server_proto_version[proto_ver_len] = '\0'; - - /* Now lets parse the software & version */ - line_ptr += proto_ver_len + 1; - line_len -= proto_ver_len + 1; - if (line_len < 1) { - SCLogDebug("No software version specified (weird)"); - state->flags |= SSH_FLAG_CLIENT_VERSION_PARSED; - /* Return the remaining length */ - SCReturnInt(input_len); - } + header->msg_code = *input; - uint8_t *sw_end = BasicSearch(line_ptr, line_len, (uint8_t*)" ", 1); - if (sw_end == NULL) { - sw_end = BasicSearch(line_ptr, line_len, (uint8_t*)"\r", 1); - if (sw_end == NULL) { - sw_end = line_ptr + line_len; - } - } + input += 1; + input_len -= 1; - uint64_t sw_ver_len = (uint64_t)(sw_end - line_ptr); - state->server_software_version = SCMalloc(sw_ver_len + 1); - if (state->server_software_version == NULL) { - SCReturnInt(-1); - } - memcpy(state->server_software_version, line_ptr, sw_ver_len); - state->server_software_version[sw_ver_len] = '\0'; - if (state->server_software_version[sw_ver_len - 1] == 0x0d) - state->server_software_version[sw_ver_len - 1] = '\0'; + SCLogInfo("msg code: %u", header->msg_code); - state->flags |= SSH_FLAG_SERVER_VERSION_PARSED; - /* Return the remaining length */ - SCReturnInt(input_len); + if (header->msg_code == SSH_MSG_NEWKEYS) { + /* done */ + SCLogInfo("done"); + state->flags |= SSH_FLAG_PARSER_DONE; + } else { + /* not yet done */ + SCLogInfo("not done"); } - SCReturnInt(0); } -/** - * \brief Function to parse the SSH field in packet received from the server +/** \internal + * \brief Function to parse the SSH field in packet received from the client + * + * Input to this function is a byte buffer starting with SSH- up to at least + * a \r or \n character. * * \param ssh_state Pointer the state in which the value to be stored - * \param pstate Application layer tarser state for this session * \param input Pointer the received input data * \param input_len Length in bytes of the received data - * \param output Pointer to the list of parsed output elements */ -static int SSHParseServerRecord(Flow *f, void *ssh_state, AppLayerParserState *pstate, - uint8_t *input, uint32_t input_len, - void *local_data, AppLayerParserResult *output) +static int SSHParseRecord(SshState *state, SshHeader *header, uint8_t *input, uint32_t input_len) { - SshState *state = (SshState *)ssh_state; - if (state->flags & SSH_FLAG_PARSER_DONE) { - SCReturnInt(0); - } - SCEnter(); int ret = 0; - SCLogDebug("ssh_state %p, pstate %p, input %p,input_len %" PRIu32 "", - ssh_state, pstate, input, input_len); - //PrintRawDataFp(stdout, input,input_len); + if (state->flags & SSH_FLAG_PARSER_DONE) { + SCReturnInt(0); + } - if (pstate == NULL) - SCReturnInt(-1); + SCLogDebug("state %p, input %p,input_len %" PRIu32, + state, input, input_len); + PrintRawDataFp(stdout, input, input_len); - if ( !(state->flags & SSH_FLAG_SERVER_VERSION_PARSED)) { - ret = SSHParseServerVersion(f, ssh_state, pstate, input, input_len, output); + if (!(header->flags & SSH_FLAG_VERSION_PARSED)) { + ret = SSHParseVersion(state, header, input, input_len); if (ret < 0) { SCLogDebug("Invalid version string"); SCReturnInt(-1); - } else if (state->flags & SSH_FLAG_SERVER_VERSION_PARSED) { - SCLogDebug("Version string parsed"); + } else if (header->flags & SSH_FLAG_VERSION_PARSED) { + SCLogInfo("Version string parsed, remaining length %d", ret); input += input_len - ret; input_len -= (input_len - ret); - pstate->parse_field = 1; - ret = 1; + ret = 0; + + uint32_t u = 0; + while (u < input_len && (input[u] == '\r' || input[u] == '\n')) { + u++; + } + SCLogInfo("skipping %u EOL bytes", u); + input += u; + input_len -= u; + if (input_len == 0) - SCReturnInt(ret); + SCReturnInt(0); + } else { + BUG_ON(1);// we only call this when we have enough data SCLogDebug("Version string not parsed yet"); - pstate->parse_field = 0; - SCReturnInt(ret); + //pstate->parse_field = 0; + SCReturnInt(0); } } else { - SCLogDebug("Version string already parsed"); + SCLogDebug("Version string already parsed"); } - uint16_t max_fields = 4; - int16_t u = 0; - uint32_t offset = 0; - - //PrintRawDataFp(stdout, input,input_len); - - if (pstate == NULL) - SCReturnInt(-1); - - for (u = pstate->parse_field; u < max_fields; u++) { - SCLogDebug("u %" PRIu32 "", u); + /* skip bytes from the current record if we have to */ + if (state->cli_hdr.record_left > 0) { + SCLogInfo("skipping bytes part of the current record"); + if (state->cli_hdr.record_left > input_len) { + state->cli_hdr.record_left -= input_len; + SCLogInfo("all input skipped, %u left in record", state->cli_hdr.record_left); + SCReturnInt(0); + } else { + input_len -= state->cli_hdr.record_left; + input += state->cli_hdr.record_left; + state->cli_hdr.record_left = 0; - switch(u % 4) { - case 0: - { - continue; - } - case 1: /* TLS CONTENT TYPE */ - { - uint8_t *data = input + offset; - uint32_t data_len = input_len - offset; - - int r = AlpParseFieldBySize(output, pstate, - SSH_FIELD_SERVER_PKT_LENGTH, - /* single byte field */4, data, - data_len, &offset); - SCLogDebug("r = %" PRId32 "", r); - - if (r == 0) { - pstate->parse_field = 1; - SCReturnInt(0); - } else if (r == -1) { - SCLogError(SC_ERR_ALPARSER, "AlpParseFieldBySize failed, " - "r %d", r); - SCReturnInt(-1); - } - - uint32_t pkt_len = 0; - int ret = ByteExtractUint32(&pkt_len, BYTE_BIG_ENDIAN, - output->tail->data_len, output->tail->data_ptr); - if (ret != 4) { - SCReturnInt(-1); - } - state->srv_hdr.pkt_len = pkt_len; - SCLogDebug("pkt len: %"PRIu32, pkt_len); - - break; - } - case 2: /* TLS VERSION */ - { - uint8_t *data = input + offset; - uint32_t data_len = input_len - offset; - - int r = AlpParseFieldBySize(output, pstate, - SSH_FIELD_SERVER_PADDING_LENGTH, - /* 2 byte field */1, data, data_len, - &offset); - if (r == 0) { - pstate->parse_field = 2; - SCReturnInt(0); - } else if (r == -1) { - SCLogError(SC_ERR_ALPARSER, "AlpParseFieldBySize failed, " - "r %d", r); - SCReturnInt(-1); - } - uint8_t padding_len = 0; - if (output->tail->data_len == 1) { - padding_len = (uint8_t) *output->tail->data_ptr; - SCLogDebug("padding len: %"PRIu8, padding_len); - } - state->srv_hdr.padding_len = padding_len; - - break; - } - case 3: /* SSH_PAYLOAD */ - { - uint8_t *data = input + offset; - uint32_t data_len = input_len - offset; - - /* we add a -1 to the pkt len since the padding length is already parsed */ - int r = AlpParseFieldBySize(output, pstate, SSH_FIELD_SERVER_PAYLOAD, - state->srv_hdr.pkt_len - 1, data, data_len, - &offset); - SCLogDebug("AlpParseFieldBySize returned r %d, offset %"PRIu32, - r, offset); - if (r == 0) { - pstate->parse_field = 3; - SCReturnInt(0); - } else if (r == -1) { - SCLogError(SC_ERR_ALPARSER, "AlpParseFieldBySize failed, " - "r %d", r); - SCReturnInt(-1); - } - - uint8_t msg_code = 0; - if (output->tail->data_len >= 1) { - msg_code = (uint8_t) *output->tail->data_ptr; - SCLogDebug("msg code: %"PRIu8, msg_code); - } - state->srv_hdr.msg_code = msg_code; - - if (state->srv_hdr.msg_code == SSH_MSG_NEWKEYS) { - /* We are not going to inspect any packet more - * as the data is now encrypted */ - SCLogDebug("SSH parser done (the rest of the communication is encrypted)"); - state->flags |= SSH_FLAG_PARSER_DONE; - pstate->flags |= APP_LAYER_PARSER_DONE; - pstate->flags |= APP_LAYER_PARSER_NO_INSPECTION; - pstate->flags |= APP_LAYER_PARSER_NO_REASSEMBLY; - pstate->parse_field = 1; - SCReturnInt(1); - } - - pstate->parse_field = 1; - ret = 1; - - /* If we have remaining data, continue processing */ - if ((int)input_len - (int)offset > 0) { - u = 0; - } - break; + if (input_len == 0) { + SCLogInfo("all input skipped"); + SCReturnInt(0); } } - } - SCReturnInt(ret); -} - -/** - * \brief Function to parse the SSH version string of the client - * - * \param ssh_state Pointer the state in which the value to be stored - * \param pstate Application layer tarser state for this session - * \param input Pointer the received input data - * \param input_len Length in bytes of the received data - * \param output Pointer to the list of parsed output elements - */ -static int SSHParseClientVersion(Flow *f, void *ssh_state, AppLayerParserState *pstate, - uint8_t *input, uint32_t input_len, - AppLayerParserResult *output) { - uint8_t *line_ptr = input; - uint32_t line_len = input_len; - uint32_t offset = 0; - - SshState *state = (SshState *)ssh_state; - - while (input_len > 0) { - offset = 0; +again: + /* input is too small, even when combined with stored bytes */ + if (state->cli_hdr.buf_offset + input_len < 6) { + memcpy(state->cli_hdr.buf + state->cli_hdr.buf_offset, input, input_len); + state->cli_hdr.buf_offset += input_len; + //PrintRawDataFp(stdout, state->cli_hdr.buf, state->cli_hdr.buf_offset); + SCReturnInt(0); - if (pstate->store_len > 0){ - const uint8_t delim[] = { 0x0a, }; - int r = AlpParseFieldByDelimiter(output, pstate, - SSH_FIELD_CLIENT_VER_STATE_LINE, delim, sizeof(delim), - input, input_len, &offset); + /* we have enough bytes to parse 6 bytes, lets see if we have + * previously stored some */ + } else if (state->cli_hdr.buf_offset > 0) { + uint8_t needed = 6 - state->cli_hdr.buf_offset; - if (r == 0) - SCReturnInt(0); + SCLogInfo("parse stored"); + memcpy(state->cli_hdr.buf + state->cli_hdr.buf_offset, input, needed); + state->cli_hdr.buf_offset = 6; - /* process the result elements */ - AppLayerParserResultElmt *e = output->head; - line_ptr = NULL; - line_len = 0; - for (; e != NULL; e = e->next) { - SCLogDebug("e %p e->name_idx %" PRIu32 ", e->data_ptr %p, e->data_len " - "%" PRIu32, e, e->name_idx, - e->data_ptr, e->data_len); - - /* no parser defined for this field. */ - if (e->name_idx != SSH_FIELD_CLIENT_VER_STATE_LINE) { - continue; - } - - line_ptr = e->data_ptr; - line_len = e->data_len; - } + // parse the 6 + if (SSHParseClientRecordHeader(state, header, state->cli_hdr.buf, 6) < 0) + SCReturnInt(-1); + state->cli_hdr.buf_offset = 0; - /* Update for the next round */ - input_len -= offset; - input += offset; + uint32_t record_left = state->cli_hdr.pkt_len - 2; + input_len -= needed; + input += needed; - if (line_ptr == NULL) - continue; + if (record_left > input_len) { + state->cli_hdr.record_left = record_left - input_len; } else { - const uint8_t delim[] = { 0x0a, }; - int r = AlpParseFieldByDelimiter(output, pstate, - SSH_FIELD_CLIENT_VER_STATE_LINE, delim, sizeof(delim), - input, input_len, &offset); - - if (r == 0) + input_len -= record_left; + if (input_len == 0) SCReturnInt(0); - /* Temporal pointer / len for the current line */ - line_ptr = input; - line_len = offset; - - /* Update for the next round */ - input_len -= offset; - input += offset; - } + input += record_left; - //PrintRawDataFp(stdout, line_ptr, line_len); + SCLogDebug("we have %u left to parse", input_len); + goto again; - if (line_len < 5) { - SCLogDebug("This is not the version line we are searching for (probably a banner or informational messages)"); - continue; } - /* is it the version line? */ - if (SCMemcmp("SSH-", line_ptr, 4) == 0) { - if (line_len > 255) { - SCLogDebug("Invalid version string, it should be less than 255 characters including "); - SCReturnInt(-1); - } - - /* ok, we have found the version line/string, skip it and parse proto version */ - line_ptr += 4; - line_len -= 4; - } else { - SCLogDebug("This is not the version line we are searching for (probably a banner or informational messages)"); - continue; - } - - uint8_t *proto_end = BasicSearch(line_ptr, line_len, (uint8_t*)"-", 1); - if (proto_end == NULL) { - /* Strings starting with SSH- are not allowed - * if they are not the real version string */ - SCLogDebug("Invalid Version String for SSH (invalid usage of SSH- prefix)"); + /* nothing stored, lets parse this directly */ + } else { + // parse the 6 + SCLogInfo("parse direct"); + PrintRawDataFp(stdout, input, input_len); + if (SSHParseClientRecordHeader(state, header, input, 6) < 0) SCReturnInt(-1); - } - uint64_t proto_ver_len = (uint64_t)(proto_end - line_ptr); - state->client_proto_version = SCMalloc(proto_ver_len + 1); - if (state->client_proto_version == NULL) { - SCReturnInt(-1); - } - memcpy(state->client_proto_version, line_ptr, proto_ver_len); - state->client_proto_version[proto_ver_len] = '\0'; - - /* Now lets parse the software & version */ - line_ptr += proto_ver_len + 1; - line_len -= proto_ver_len + 1; - if (line_len < 1) { - SCLogDebug("No software version specified (weird)"); - state->flags |= SSH_FLAG_CLIENT_VERSION_PARSED; - /* Return the remaining length */ - SCReturnInt(input_len); - } + uint32_t record_left = state->cli_hdr.pkt_len - 2; + SCLogInfo("record left %u", record_left); + input_len -= 6; + input += 6; - uint8_t *sw_end = BasicSearch(line_ptr, line_len, (uint8_t*)" ", 1); - if (sw_end == NULL) { - sw_end = BasicSearch(line_ptr, line_len, (uint8_t*)"\r", 1); - if (sw_end == NULL) { - sw_end = line_ptr + line_len; - } - } + if (record_left > input_len) { + state->cli_hdr.record_left = record_left - input_len; + } else { + input_len -= record_left; + if (input_len == 0) + SCReturnInt(0); + input += record_left; + //PrintRawDataFp(stdout, input, input_len); - uint64_t sw_ver_len = (uint64_t)(sw_end - line_ptr); - state->client_software_version = SCMalloc(sw_ver_len + 1); - if (state->client_software_version == NULL) { - SCReturnInt(-1); + SCLogDebug("we have %u left to parse", input_len); + goto again; } - memcpy(state->client_software_version, line_ptr, sw_ver_len); - state->client_software_version[sw_ver_len] = '\0'; - if (state->client_software_version[sw_ver_len - 1] == 0x0d) - state->client_software_version[sw_ver_len - 1] = '\0'; - - state->flags |= SSH_FLAG_CLIENT_VERSION_PARSED; - /* Return the remaining length */ - SCReturnInt(input_len); } +#if 0 + if (state->cli_hdr.msg_code == SSH_MSG_NEWKEYS) { + /* We are not going to inspect any packet more + * as the data is now encrypted */ + SCLogDebug("SSH parser done (the rest of the communication is encrypted)"); + pstate->flags |= APP_LAYER_PARSER_DONE; + pstate->flags |= APP_LAYER_PARSER_NO_INSPECTION; + pstate->flags |= APP_LAYER_PARSER_NO_REASSEMBLY; + pstate->parse_field = 1; + SCReturnInt(1); + } +#endif SCReturnInt(0); } -/** - * \brief Function to parse the SSH field in packet received from the client - * - * \param ssh_state Pointer the state in which the value to be stored - * \param pstate Application layer tarser state for this session - * \param input Pointer the received input data - * \param input_len Length in bytes of the received data - * \param output Pointer to the list of parsed output elements - */ -static int SSHParseClientRecord(Flow *f, void *ssh_state, AppLayerParserState *pstate, - uint8_t *input, uint32_t input_len, - void *local_data, AppLayerParserResult *output) +static int EnoughData(uint8_t *input, uint32_t input_len) { - SshState *state = (SshState *)ssh_state; - if (state->flags & SSH_FLAG_PARSER_DONE) { - SCReturnInt(0); + uint32_t u; + for (u = 0; u < input_len; u++) { + if (input[u] == '\r' || input[u] == '\n') + return TRUE; } - - SCEnter(); - int ret = 0; - - SCLogDebug("ssh_state %p, pstate %p, input %p,input_len %" PRIu32 "", - ssh_state, pstate, input, input_len); - //PrintRawDataFp(stdout, input,input_len); - - if (pstate == NULL) - SCReturnInt(-1); - - if ( !(state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { - ret = SSHParseClientVersion(f, ssh_state, pstate, input, input_len, output); - if (ret < 0) { - SCLogDebug("Invalid version string"); - SCReturnInt(-1); - } else if (state->flags & SSH_FLAG_CLIENT_VERSION_PARSED) { - SCLogDebug("Version string parsed"); - input += input_len - ret; - input_len -= (input_len - ret); - pstate->parse_field = 1; - ret = 1; - } else { - SCLogDebug("Version string not parsed yet"); - pstate->parse_field = 0; - SCReturnInt(0); - } - } else { - SCLogDebug("Version string already parsed"); - } - - uint16_t max_fields = 4; - int16_t u = 0; - uint32_t offset = 0; - - //printf("INPUT: \n"); - //PrintRawDataFp(stdout, input,input_len); - - if (pstate == NULL) - SCReturnInt(-1); - - for (u = pstate->parse_field; u < max_fields; u++) { - SCLogDebug("u %" PRIu32 "", u); - - switch(u % 4) { - case 0: - { - continue; - } - case 1: /* TLS CONTENT TYPE */ - { - uint8_t *data = input + offset; - uint32_t data_len = input_len - offset; - - int r = AlpParseFieldBySize(output, pstate, - SSH_FIELD_CLIENT_PKT_LENGTH, - /* single byte field */4, data, - data_len, &offset); - SCLogDebug("r = %" PRId32 "", r); - - if (r == 0) { - pstate->parse_field = 1; - SCReturnInt(0); - } else if (r == -1) { - SCLogError(SC_ERR_ALPARSER, "AlpParseFieldBySize failed, " - "r %d", r); - SCReturnInt(-1); - } - - uint32_t pkt_len = 0; - int ret = ByteExtractUint32(&pkt_len, BYTE_BIG_ENDIAN, - output->tail->data_len, output->tail->data_ptr); - if (ret != 4) { - SCReturnInt(-1); - } - state->cli_hdr.pkt_len = pkt_len; - SCLogDebug("pkt len: %"PRIu32"\n", pkt_len); - - break; - } - case 2: /* TLS VERSION */ - { - uint8_t *data = input + offset; - uint32_t data_len = input_len - offset; - - int r = AlpParseFieldBySize(output, pstate, - SSH_FIELD_CLIENT_PADDING_LENGTH, - /* 2 byte field */1, data, data_len, - &offset); - if (r == 0) { - pstate->parse_field = 2; - SCReturnInt(0); - } else if (r == -1) { - SCLogError(SC_ERR_ALPARSER, "AlpParseFieldBySize failed, " - "r %d", r); - SCReturnInt(-1); - } - uint8_t padding_len = 0; - if (output->tail->data_len == 1) { - padding_len = (uint8_t) *output->tail->data_ptr; - SCLogDebug("padding len: %"PRIu8, padding_len); - } - state->cli_hdr.padding_len = padding_len; - - break; - } - case 3: /* SSH_PAYLOAD */ - { - uint8_t *data = input + offset; - uint32_t data_len = input_len - offset; - - /* we add a -1 to the pkt len since the padding length is already parsed */ - int r = AlpParseFieldBySize(output, pstate, SSH_FIELD_CLIENT_PAYLOAD, - /* 1 byte field */ state->cli_hdr.pkt_len - 1, data, data_len, - &offset); - SCLogDebug("AlpParseFieldBySize returned r %d, offset %"PRIu32, - r, offset); - if (r == 0) { - pstate->parse_field = 3; - SCReturnInt(0); - } else if (r == -1) { - SCLogError(SC_ERR_ALPARSER, "AlpParseFieldBySize failed, " - "r %d", r); - SCReturnInt(-1); - } - - uint8_t msg_code = 0; - if (output->tail->data_len >= 1) { - msg_code = (uint8_t) *output->tail->data_ptr; - SCLogDebug("msg code: %"PRIu8, msg_code); - } - - state->cli_hdr.msg_code = msg_code; - if (state->cli_hdr.msg_code == SSH_MSG_NEWKEYS) { - /* We are not going to inspect any packet more - * as the data is now encrypted */ - SCLogDebug("SSH parser done (the rest of the communication is encrypted)"); - state->flags |= SSH_FLAG_PARSER_DONE; - pstate->flags |= APP_LAYER_PARSER_DONE; - pstate->flags |= APP_LAYER_PARSER_NO_INSPECTION; - pstate->flags |= APP_LAYER_PARSER_NO_REASSEMBLY; - pstate->parse_field = 1; - SCReturnInt(1); - } - - pstate->parse_field = 1; - ret = 1; - - /* If we have remaining data, continue processing */ - if (input_len - offset > 0) { - u = 0; - } - - break; - } - } - - } - - SCReturnInt(ret); + return FALSE; } -#endif static int SSHParseRequest(Flow *f, void *state, AppLayerParserState *pstate, uint8_t *input, uint32_t input_len, void *local_data) { + SshState *ssh_state = (SshState *)state; + + if (ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED || EnoughData(input, input_len) == TRUE) { + SCLogInfo("enough data, parse now"); + // parse now + int r = SSHParseRecord(ssh_state, &ssh_state->cli_hdr, input, input_len); + SCReturnInt(r); + } else { + // buffer + } + PrintRawDataFp(stdout, input, input_len); return 0; } @@ -718,7 +366,7 @@ static int SSHParseResponse(Flow *f, void *state, AppLayerParserState *pstate, uint8_t *input, uint32_t input_len, void *local_data) { - PrintRawDataFp(stdout, input, input_len); +// PrintRawDataFp(stdout, input, input_len); return 0; } @@ -739,14 +387,14 @@ static void *SSHStateAlloc(void) static void SSHStateFree(void *state) { SshState *s = (SshState *)state; - if (s->client_proto_version != NULL) - SCFree(s->client_proto_version); - if (s->client_software_version != NULL) - SCFree(s->client_software_version); - if (s->server_proto_version != NULL) - SCFree(s->server_proto_version); - if (s->server_software_version != NULL) - SCFree(s->server_software_version); + if (s->cli_hdr.proto_version != NULL) + SCFree(s->cli_hdr.proto_version); + if (s->cli_hdr.software_version != NULL) + SCFree(s->cli_hdr.software_version); + if (s->srv_hdr.proto_version != NULL) + SCFree(s->srv_hdr.proto_version); + if (s->srv_hdr.software_version != NULL) + SCFree(s->srv_hdr.software_version); SCFree(s); } @@ -831,31 +479,31 @@ static int SSHParserTest01(void) { goto end; } - if ( !(ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { + if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { printf("Client version string not parsed: "); result = 0; goto end; } - if (ssh_state->client_software_version == NULL) { + if (ssh_state->cli_hdr.software_version == NULL) { printf("Client version string not parsed: "); result = 0; goto end; } - if (ssh_state->client_proto_version == NULL) { + if (ssh_state->cli_hdr.proto_version == NULL) { printf("Client version string not parsed: "); result = 0; goto end; } - if (strncmp((char*)ssh_state->client_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { printf("Client version string not parsed correctly: "); result = 0; goto end; } - if (strncmp((char*)ssh_state->client_proto_version, "2.0", strlen("2.0")) != 0) { + if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { printf("Client version string not parsed correctly: "); result = 0; goto end; @@ -904,31 +552,31 @@ static int SSHParserTest02(void) { goto end; } - if ( !(ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { + if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { printf("Client version string not parsed: "); result = 0; goto end; } - if (ssh_state->client_software_version == NULL) { + if (ssh_state->cli_hdr.software_version == NULL) { printf("Client version string not parsed: "); result = 0; goto end; } - if (ssh_state->client_proto_version == NULL) { + if (ssh_state->cli_hdr.proto_version == NULL) { printf("Client version string not parsed: "); result = 0; goto end; } - if (strncmp((char*)ssh_state->client_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { printf("Client version string not parsed correctly: "); result = 0; goto end; } - if (strncmp((char*)ssh_state->client_proto_version, "2.0", strlen("2.0")) != 0) { + if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { printf("Client version string not parsed correctly: "); result = 0; goto end; @@ -978,18 +626,18 @@ static int SSHParserTest03(void) { goto end; } - if (ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED) { + if (ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED) { printf("Client version string parsed? It's not a valid string: "); result = 0; goto end; } - if (ssh_state->client_proto_version != NULL) { + if (ssh_state->cli_hdr.proto_version != NULL) { result = 0; goto end; } - if (ssh_state->client_software_version != NULL) { + if (ssh_state->cli_hdr.software_version != NULL) { result = 0; goto end; } @@ -1041,25 +689,25 @@ static int SSHParserTest04(void) { goto end; } - if (ssh_state->server_software_version == NULL) { + if (ssh_state->srv_hdr.software_version == NULL) { printf("Client version string not parsed: "); result = 0; goto end; } - if (ssh_state->server_proto_version == NULL) { + if (ssh_state->srv_hdr.proto_version == NULL) { printf("Client version string not parsed: "); result = 0; goto end; } - if (strncmp((char*)ssh_state->server_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { printf("Client version string not parsed correctly: "); result = 0; goto end; } - if (strncmp((char*)ssh_state->server_proto_version, "2.0", strlen("2.0")) != 0) { + if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { printf("Client version string not parsed correctly: "); result = 0; goto end; @@ -1114,25 +762,25 @@ static int SSHParserTest05(void) { goto end; } - if (ssh_state->server_software_version == NULL) { + if (ssh_state->srv_hdr.software_version == NULL) { printf("Client version string not parsed: "); result = 0; goto end; } - if (ssh_state->server_proto_version == NULL) { + if (ssh_state->srv_hdr.proto_version == NULL) { printf("Client version string not parsed: "); result = 0; goto end; } - if (strncmp((char*)ssh_state->server_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { printf("Client version string not parsed correctly: "); result = 0; goto end; } - if (strncmp((char*)ssh_state->server_proto_version, "2.0", strlen("2.0")) != 0) { + if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { printf("Client version string not parsed correctly: "); result = 0; goto end; @@ -1188,12 +836,12 @@ static int SSHParserTest06(void) { goto end; } - if (ssh_state->server_proto_version != NULL) { + if (ssh_state->srv_hdr.proto_version != NULL) { result = 0; goto end; } - if (ssh_state->server_software_version != NULL) { + if (ssh_state->srv_hdr.software_version != NULL) { result = 0; goto end; } @@ -1247,27 +895,27 @@ static int SSHParserTest07(void) { goto end; } - if ( !(ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { + if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { printf("Client version string not parsed: "); goto end; } - if (ssh_state->client_software_version == NULL) { + if (ssh_state->cli_hdr.software_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (ssh_state->client_proto_version == NULL) { + if (ssh_state->cli_hdr.proto_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (strncmp((char*)ssh_state->client_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { printf("Client version string not parsed correctly: "); goto end; } - if (strncmp((char*)ssh_state->client_proto_version, "2.0", strlen("2.0")) != 0) { + if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { printf("Client version string not parsed correctly: "); goto end; } @@ -1333,27 +981,27 @@ static int SSHParserTest08(void) { goto end; } - if ( !(ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { + if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { printf("Client version string not parsed: "); goto end; } - if (ssh_state->client_software_version == NULL) { + if (ssh_state->cli_hdr.software_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (ssh_state->client_proto_version == NULL) { + if (ssh_state->cli_hdr.proto_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (strncmp((char*)ssh_state->client_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { printf("Client version string not parsed correctly: "); goto end; } - if (strncmp((char*)ssh_state->client_proto_version, "2.0", strlen("2.0")) != 0) { + if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { printf("Client version string not parsed correctly: "); goto end; } @@ -1411,22 +1059,22 @@ static int SSHParserTest09(void) { goto end; } - if (ssh_state->server_software_version == NULL) { + if (ssh_state->srv_hdr.software_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (ssh_state->server_proto_version == NULL) { + if (ssh_state->srv_hdr.proto_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (strncmp((char*)ssh_state->server_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { printf("Client version string not parsed correctly: "); goto end; } - if (strncmp((char*)ssh_state->server_proto_version, "2.0", strlen("2.0")) != 0) { + if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { printf("Client version string not parsed correctly: "); goto end; } @@ -1497,22 +1145,22 @@ static int SSHParserTest10(void) { goto end; } - if (ssh_state->server_software_version == NULL) { + if (ssh_state->srv_hdr.software_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (ssh_state->server_proto_version == NULL) { + if (ssh_state->srv_hdr.proto_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (strncmp((char*)ssh_state->server_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { printf("Client version string not parsed correctly: "); goto end; } - if (strncmp((char*)ssh_state->server_proto_version, "2.0", strlen("2.0")) != 0) { + if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { printf("Client version string not parsed correctly: "); goto end; } @@ -1529,10 +1177,89 @@ end: static int SSHParserTest11(void) { int result = 0; Flow f; - uint8_t sshbuf1[] = "Welcome to this ssh server\nSSH-"; + uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n"; uint32_t sshlen1 = sizeof(sshbuf1) - 1; - uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n"; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; + uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x03, 0x01, 21, 0x00}; + uint32_t sshlen2 = sizeof(sshbuf2); + TcpSession ssn; + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + f.protoctx = (void *)&ssn; + + StreamTcpInitConfig(TRUE); + + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + SCMutexLock(&f.m); + r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2); + if (r != 0) { + printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + SshState *ssh_state = f.alstate; + if (ssh_state == NULL) { + printf("no ssh state: "); + goto end; + } + + if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { + printf("Client version string not parsed: "); + goto end; + } + + if (ssh_state->cli_hdr.software_version == NULL) { + printf("Client version string not parsed: "); + goto end; + } + + if (ssh_state->cli_hdr.proto_version == NULL) { + printf("Client version string not parsed: "); + goto end; + } + + if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + printf("Client version string not parsed correctly: "); + goto end; + } + + if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { + printf("Client version string not parsed correctly: "); + goto end; + } + + if ( !(ssh_state->flags & SSH_FLAG_PARSER_DONE)) { + printf("Didn't detect the msg code of new keys (ciphered data starts): "); + goto end; + } + + result = 1; +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + StreamTcpFreeConfig(TRUE); + return result; +} + +/** \test Send a banner and 2 records record in four chunks. */ +static int SSHParserTest12(void) { + int result = 0; + Flow f; + uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n"; + uint32_t sshlen1 = sizeof(sshbuf1) - 1; + uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x03,0x01, 17, 0x00}; + uint32_t sshlen2 = sizeof(sshbuf2); uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00}; uint32_t sshlen3 = sizeof(sshbuf3); TcpSession ssn; @@ -1577,27 +1304,27 @@ static int SSHParserTest11(void) { goto end; } - if ( !(ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { + if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { printf("Client version string not parsed: "); goto end; } - if (ssh_state->client_software_version == NULL) { + if (ssh_state->cli_hdr.software_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (ssh_state->client_proto_version == NULL) { + if (ssh_state->cli_hdr.proto_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (strncmp((char*)ssh_state->client_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { printf("Client version string not parsed correctly: "); goto end; } - if (strncmp((char*)ssh_state->client_proto_version, "2.0", strlen("2.0")) != 0) { + if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { printf("Client version string not parsed correctly: "); goto end; } @@ -1616,19 +1343,18 @@ end: } /** \test Send a banner and 2 records record in four chunks. */ -static int SSHParserTest12(void) { +static int SSHParserTest13(void) { int result = 0; Flow f; - uint8_t sshbuf1[] = "Welcome to this ssh server\nSSH-"; + uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n"; uint32_t sshlen1 = sizeof(sshbuf1) - 1; - uint8_t sshbuf2[] = "2.0-MySSHClient-0.5.1\r\n"; - uint32_t sshlen2 = sizeof(sshbuf2) - 1; - uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x03,0x01, 17, 0x00}; + uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 17}; + uint32_t sshlen2 = sizeof(sshbuf2); + uint8_t sshbuf3[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 21}; uint32_t sshlen3 = sizeof(sshbuf3); - uint8_t sshbuf4[] = { 0x00, 0x00, 0x00, 0x03,0x01, 21, 0x00}; - uint32_t sshlen4 = sizeof(sshbuf4); TcpSession ssn; AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + uint32_t u; memset(&f, 0, sizeof(f)); memset(&ssn, 0, sizeof(ssn)); @@ -1644,7 +1370,104 @@ static int SSHParserTest12(void) { goto end; } SCMutexUnlock(&f.m); + for (u = 0; u < sshlen2; u++) { + SCMutexLock(&f.m); + r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, &sshbuf2[u], 1); + if (r != 0) { + printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + } + for (u = 0; u < sshlen3; u++) { + SCMutexLock(&f.m); + r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, &sshbuf3[u], 1); + if (r != 0) { + printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + } + SshState *ssh_state = f.alstate; + if (ssh_state == NULL) { + printf("no ssh state: "); + goto end; + } + + if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { + printf("Client version string not parsed: "); + goto end; + } + + if (ssh_state->cli_hdr.software_version == NULL) { + printf("Client version string not parsed: "); + goto end; + } + + if (ssh_state->cli_hdr.proto_version == NULL) { + printf("Client version string not parsed: "); + goto end; + } + + if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + printf("Client version string not parsed correctly: "); + goto end; + } + + if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { + printf("Client version string not parsed correctly: "); + goto end; + } + + if ( !(ssh_state->flags & SSH_FLAG_PARSER_DONE)) { + printf("Didn't detect the msg code of new keys (ciphered data starts): "); + goto end; + } + + result = 1; +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + StreamTcpFreeConfig(TRUE); + return result; +} + +/** \test Send a banner and 2 records record in four chunks. */ +static int SSHParserTest14(void) { + int result = 0; + Flow f; + uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n"; + uint32_t sshlen1 = sizeof(sshbuf1) - 1; + uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00}; + uint32_t sshlen2 = sizeof(sshbuf2); + + uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + uint32_t sshlen3 = sizeof(sshbuf3); + uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00}; + uint32_t sshlen4 = sizeof(sshbuf4); + + /* first byte of this record in sshbuf4 */ + uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 21}; + uint32_t sshlen5 = sizeof(sshbuf5); + TcpSession ssn; + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + f.protoctx = (void *)&ssn; + + StreamTcpInitConfig(TRUE); + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); SCMutexLock(&f.m); r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2); if (r != 0) { @@ -1653,7 +1476,6 @@ static int SSHParserTest12(void) { goto end; } SCMutexUnlock(&f.m); - SCMutexLock(&f.m); r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3); if (r != 0) { @@ -1662,11 +1484,129 @@ static int SSHParserTest12(void) { goto end; } SCMutexUnlock(&f.m); + SCMutexLock(&f.m); + r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4); + if (r != 0) { + printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + SCMutexLock(&f.m); + r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf5, sshlen5); + if (r != 0) { + printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + SshState *ssh_state = f.alstate; + if (ssh_state == NULL) { + printf("no ssh state: "); + goto end; + } + + if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { + printf("Client version string not parsed: "); + goto end; + } + + if (ssh_state->cli_hdr.software_version == NULL) { + printf("Client version string not parsed: "); + goto end; + } + + if (ssh_state->cli_hdr.proto_version == NULL) { + printf("Client version string not parsed: "); + goto end; + } + + if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + printf("Client version string not parsed correctly: "); + goto end; + } + + if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { + printf("Client version string not parsed correctly: "); + goto end; + } + + if ( !(ssh_state->flags & SSH_FLAG_PARSER_DONE)) { + printf("Didn't detect the msg code of new keys (ciphered data starts): "); + goto end; + } + + result = 1; +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + StreamTcpFreeConfig(TRUE); + return result; +} +/** \test Send a banner and 2 records record in four chunks. */ +static int SSHParserTest15(void) { + int result = 0; + Flow f; + uint8_t sshbuf1[] = "SSH-2.0-MySSHClient-0.5.1\r\n"; + uint32_t sshlen1 = sizeof(sshbuf1) - 1; + uint8_t sshbuf2[] = { 0x00, 0x00, 0x00, 0x10, 0x01, 17, 0x00}; + uint32_t sshlen2 = sizeof(sshbuf2); + + uint8_t sshbuf3[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + uint32_t sshlen3 = sizeof(sshbuf3); + uint8_t sshbuf4[] = { 0x09, 0x10, 0x11, 0x12, 0x13, 0x00}; + uint32_t sshlen4 = sizeof(sshbuf4); + + /* first byte of this record in sshbuf4 */ + uint8_t sshbuf5[] = { 0x00, 0x00, 0x02, 0x01, 20, 0x00, 0x00, 0x00, 0x02, 0x01, 21}; + uint32_t sshlen5 = sizeof(sshbuf5); + TcpSession ssn; + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + f.protoctx = (void *)&ssn; + + StreamTcpInitConfig(TRUE); + + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf1, sshlen1); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + SCMutexLock(&f.m); + r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf2, sshlen2); + if (r != 0) { + printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + SCMutexLock(&f.m); + r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf3, sshlen3); + if (r != 0) { + printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); SCMutexLock(&f.m); r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf4, sshlen4); if (r != 0) { - printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); + printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + SCMutexLock(&f.m); + r = AppLayerParserParse(alp_tctx, &f, ALPROTO_SSH, STREAM_TOSERVER, sshbuf5, sshlen5); + if (r != 0) { + printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); SCMutexUnlock(&f.m); goto end; } @@ -1678,27 +1618,27 @@ static int SSHParserTest12(void) { goto end; } - if ( !(ssh_state->flags & SSH_FLAG_CLIENT_VERSION_PARSED)) { + if ( !(ssh_state->cli_hdr.flags & SSH_FLAG_VERSION_PARSED)) { printf("Client version string not parsed: "); goto end; } - if (ssh_state->client_software_version == NULL) { + if (ssh_state->cli_hdr.software_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (ssh_state->client_proto_version == NULL) { + if (ssh_state->cli_hdr.proto_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (strncmp((char*)ssh_state->client_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + if (strncmp((char*)ssh_state->cli_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { printf("Client version string not parsed correctly: "); goto end; } - if (strncmp((char*)ssh_state->client_proto_version, "2.0", strlen("2.0")) != 0) { + if (strncmp((char*)ssh_state->cli_hdr.proto_version, "2.0", strlen("2.0")) != 0) { printf("Client version string not parsed correctly: "); goto end; } @@ -1717,7 +1657,7 @@ end: } /** \test Send toserver a banner and record in three chunks. */ -static int SSHParserTest13(void) { +static int SSHParserTest16(void) { int result = 0; Flow f; uint8_t sshbuf1[] = "Welcome to this ssh server\nSSH-"; @@ -1773,22 +1713,22 @@ static int SSHParserTest13(void) { goto end; } - if (ssh_state->server_software_version == NULL) { + if (ssh_state->srv_hdr.software_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (ssh_state->server_proto_version == NULL) { + if (ssh_state->srv_hdr.proto_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (strncmp((char*)ssh_state->server_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { printf("Client version string not parsed correctly: "); goto end; } - if (strncmp((char*)ssh_state->server_proto_version, "2.0", strlen("2.0")) != 0) { + if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { printf("Client version string not parsed correctly: "); goto end; } @@ -1807,7 +1747,7 @@ end: } /** \test Send toserver a banner and 2 records record in four chunks. */ -static int SSHParserTest14(void) { +static int SSHParserTest17(void) { int result = 0; Flow f; uint8_t sshbuf1[] = "Welcome to this ssh server\nSSH-"; @@ -1874,22 +1814,22 @@ static int SSHParserTest14(void) { goto end; } - if (ssh_state->server_software_version == NULL) { + if (ssh_state->srv_hdr.software_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (ssh_state->server_proto_version == NULL) { + if (ssh_state->srv_hdr.proto_version == NULL) { printf("Client version string not parsed: "); goto end; } - if (strncmp((char*)ssh_state->server_software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { + if (strncmp((char*)ssh_state->srv_hdr.software_version, "MySSHClient-0.5.1", strlen("MySSHClient-0.5.1")) != 0) { printf("Client version string not parsed correctly: "); goto end; } - if (strncmp((char*)ssh_state->server_proto_version, "2.0", strlen("2.0")) != 0) { + if (strncmp((char*)ssh_state->srv_hdr.proto_version, "2.0", strlen("2.0")) != 0) { printf("Client version string not parsed correctly: "); goto end; } @@ -1925,6 +1865,9 @@ void SSHParserRegisterTests(void) { UtRegisterTest("SSHParserTest12 - ToClient 4 chunks", SSHParserTest12, 1); UtRegisterTest("SSHParserTest13 - ToClient 4 chunks", SSHParserTest13, 1); UtRegisterTest("SSHParserTest14 - ToClient 4 chunks", SSHParserTest14, 1); + UtRegisterTest("SSHParserTest15", SSHParserTest15, 1); + UtRegisterTest("SSHParserTest16", SSHParserTest16, 1); + UtRegisterTest("SSHParserTest17", SSHParserTest17, 1); #endif /* UNITTESTS */ } diff --git a/src/app-layer-ssh.h b/src/app-layer-ssh.h index fea4fd4398..c15f792d06 100644 --- a/src/app-layer-ssh.h +++ b/src/app-layer-ssh.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2010 Open Information Security Foundation +/* Copyright (C) 2007-2014 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -19,6 +19,7 @@ * \file * * \author Pablo Rincon + * \author Victor Julien */ #ifndef __APP_LAYER_SSH_H__ @@ -31,8 +32,9 @@ client will now on sends encrypted msgs. */ -#define SSH_FLAG_CLIENT_VERSION_PARSED 0x01 +//#define SSH_FLAG_CLIENT_VERSION_PARSED 0x01 #define SSH_FLAG_SERVER_VERSION_PARSED 0x02 +#define SSH_FLAG_VERSION_PARSED 0x08 /* This flags indicate that the rest of the communication * must be ciphered, so the parsing finish here */ @@ -41,21 +43,6 @@ /* MSG_CODE */ #define SSH_MSG_NEWKEYS 21 -enum { - SSH_FIELD_NONE = 0, - SSH_FIELD_SERVER_VER_STATE_LINE, - SSH_FIELD_CLIENT_VER_STATE_LINE, - SSH_FIELD_SERVER_PKT_LENGTH, - SSH_FIELD_CLIENT_PKT_LENGTH, - SSH_FIELD_SERVER_PADDING_LENGTH, - SSH_FIELD_CLIENT_PADDING_LENGTH, - SSH_FIELD_SERVER_PAYLOAD, - SSH_FIELD_CLIENT_PAYLOAD, - - /* must be last */ - SSH_FIELD_MAX, -}; - /** From SSH-TRANSP rfc SSH Bunary packet structure: @@ -69,25 +56,22 @@ enum { the lenghts and msg_code (inside payload, if any) */ -typedef struct SSHHeader_ { +typedef struct SshHeader_ { uint32_t pkt_len; uint8_t padding_len; uint8_t msg_code; + uint8_t buf[6]; + uint8_t buf_offset; + uint8_t flags; + uint32_t record_left; + uint8_t *proto_version; + uint8_t *software_version; } SshHeader; /** structure to store the SSH state values */ typedef struct SshState_ { uint8_t flags; /**< Flags to indicate the current SSH sessoin state */ - uint8_t client_msg_code; /**< Client content type storage field */ - uint8_t server_msg_code; /**< Server content type storage field */ - - uint8_t *client_proto_version; /**< Client SSH version storage field */ - uint8_t *client_software_version; /**< Client SSH version storage field */ - - uint8_t *server_proto_version; /**< Server SSH version storage field */ - uint8_t *server_software_version; /**< Server SSH version storage field */ - SshHeader srv_hdr; SshHeader cli_hdr; } SshState;