diff options
author | Thomas Winter <Thomas.Winter@alliedtelesis.co.nz> | 2023-08-21 10:56:55 +1200 |
---|---|---|
committer | Ivan Nardi <12729895+IvanNardi@users.noreply.github.com> | 2023-09-12 13:12:14 +0200 |
commit | 1e9517ab74678e06d3ca607dd30d4ccaf8835864 (patch) | |
tree | aba0268c1ec62a4ceb0414761895d2bd058f526b | |
parent | 1bf7e5face91e0fb103ac19dcacab65738e112be (diff) |
tftp: rework request checking to account for options
Read and write requests were not handling options.
The existing code inspecting request messages assumed that the string
before the end of the payload was the mode and that the filename
length depended on the mode length.
However the first two strings are the filename and mode which can be
followed by any number of option strings.
Rework the checking of the filename and mode to just search the first
two strings which should always exist. Any options after that are
ignored.
Absence of the filename or mode is now excludes the TFTP protocol.
Absence of the filename no longer is considered malformed packet
because it can match other protocols falsely.
-rw-r--r-- | src/lib/protocols/tftp.c | 70 |
1 files changed, 48 insertions, 22 deletions
diff --git a/src/lib/protocols/tftp.c b/src/lib/protocols/tftp.c index dfdd60517..135d1bea2 100644 --- a/src/lib/protocols/tftp.c +++ b/src/lib/protocols/tftp.c @@ -65,43 +65,69 @@ static void ndpi_search_tftp(struct ndpi_detection_module_struct *ndpi_struct, } { - char const * const possible_modes[] = { "netascii", "octet", "mail" }; - uint8_t mode_found = 0, mode_idx; + uint8_t mode_found = 0; size_t mode_len; - - for(mode_idx = 0; mode_idx < NDPI_ARRAY_LENGTH(possible_modes); ++mode_idx) + int i; + size_t filename_len = 0; + const char *string; + size_t len = 0; + bool first = true; + + /* Skip 2 byte opcode. */ + for (i = 2; i < packet->payload_packet_len; i++) { - mode_len = strlen(possible_modes[mode_idx]); - - if (packet->payload_packet_len < mode_len + 1 /* mode is a nul terminated string */) + /* Search through the payload until we find a NULL terminated string. */ + if (packet->payload[i] != '\0') { + len++; continue; } - if (strncasecmp((char const *)&packet->payload[packet->payload_packet_len - 1 - mode_len], - possible_modes[mode_idx], mode_len) == 0) + string = (const char *)&packet->payload[i - len]; + + /* Filename should be immediately after opcode followed by the mode. */ + if (first) { - mode_found = 1; - break; + filename_len = len; + len = 0; + first = false; + continue; } + + char const * const possible_modes[] = { "netascii", "octet", "mail" }; + uint8_t mode_idx; + + /* Check the string in the payload against the possible TFTP modes. */ + for(mode_idx = 0; mode_idx < NDPI_ARRAY_LENGTH(possible_modes); ++mode_idx) + { + mode_len = strlen(possible_modes[mode_idx]); + /* Both are now null terminated */ + if (len != mode_len) + { + continue; + } + if (strncasecmp(string, possible_modes[mode_idx], mode_len) == 0) + { + mode_found = 1; + break; + } + } + + /* Second string searched must've been the mode, break out before any following options. */ + break; } - if (mode_found == 0) + /* Exclude the flow as TFPT if there was no filename and mode in the first two strings. */ + if (filename_len == 0 || ndpi_is_printable_buffer(&packet->payload[2], filename_len) == 0 || + mode_found == 0) { NDPI_EXCLUDE_PROTO(ndpi_struct, flow); return; } /* Dissect RRQ/WWQ filename. */ - size_t filename_len = packet->payload_packet_len - 2 /* Opcode */ - mode_len - 1 /* NUL */; - - if (filename_len == 0 || packet->payload[2] == '\0' || ndpi_is_printable_buffer(&packet->payload[2], filename_len - 1) == 0) - { - ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid TFTP RR/WR header: Source/Destination file missing"); - } else { - filename_len = ndpi_min(filename_len, sizeof(flow->protos.tftp.filename) - 1); - memcpy(flow->protos.tftp.filename, &packet->payload[2], filename_len); - flow->protos.tftp.filename[filename_len] = '\0'; - } + filename_len = ndpi_min(filename_len, sizeof(flow->protos.tftp.filename) - 1); + memcpy(flow->protos.tftp.filename, &packet->payload[2], filename_len); + flow->protos.tftp.filename[filename_len] = '\0'; /* We have seen enough and do not need any more TFTP packets. */ NDPI_LOG_INFO(ndpi_struct, "found tftp (RRQ/WWQ)\n"); |