dnp3: recover after link framing errors

Treat bad link-header CRCs and impossible link lengths as recoverable
errors and attempt to resynchronize.

Also consume a response frame whose trusted length is too short for the
required response headers, so later responses remain available.

Ticket: 8980
pull/16213/head
Jason Ish 2 months ago committed by Victor Julien
parent 5284906338
commit 92df7e4a8e

@ -597,6 +597,30 @@ static int DNP3HasUserData(const DNP3LinkHeader *header, uint8_t direction)
}
}
/**
* \brief Advance past invalid input to the next possible DNP3 frame.
*/
static void DNP3Resync(const uint8_t **input, uint32_t *input_len, uint32_t *processed)
{
uint32_t skip = 1;
while (skip + 1 < *input_len) {
if ((*input)[skip] == DNP3_START_BYTE0 && (*input)[skip + 1] == DNP3_START_BYTE1) {
break;
}
skip++;
}
/* Consume the final byte unless the next TCP slice could complete its start marker. */
if (skip < *input_len && (*input)[skip] != DNP3_START_BYTE0) {
skip++;
}
*input += skip;
*input_len -= skip;
*processed += skip;
}
/**
* \brief Reset a DNP3Buffer.
*/
@ -1072,18 +1096,21 @@ static int DNP3HandleRequestLinkLayer(
DNP3LinkHeader *header = (DNP3LinkHeader *)input;
if (!DNP3CheckStartBytes(header)) {
goto error;
/* Terminal error condition. */
SCReturnInt(-1);
}
if (!DNP3CheckLinkHeaderCRC(header)) {
DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_BAD_LINK_CRC);
goto error;
DNP3Resync(&input, &input_len, &processed);
continue;
}
uint16_t frame_len = DNP3CalculateLinkLength(header->len);
if (frame_len == 0) {
DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_LEN_TOO_SMALL);
goto error;
DNP3Resync(&input, &input_len, &processed);
continue;
}
if (input_len < frame_len) {
/* Insufficient data, just break - will wait for more data. */
@ -1118,10 +1145,6 @@ static int DNP3HandleRequestLinkLayer(
}
SCReturnInt(processed);
error:
/* Error out. Should only happen if this doesn't look like a DNP3
* frame. */
SCReturnInt(-1);
}
/**
@ -1207,19 +1230,22 @@ static int DNP3HandleResponseLinkLayer(
DNP3LinkHeader *header = (DNP3LinkHeader *)input;
if (!DNP3CheckStartBytes(header)) {
goto error;
/* Terminal error condition. */
SCReturnInt(-1);
}
if (!DNP3CheckLinkHeaderCRC(header)) {
DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_BAD_LINK_CRC);
goto error;
DNP3Resync(&input, &input_len, &processed);
continue;
}
/* Calculate the number of bytes needed to for this frame. */
uint16_t frame_len = DNP3CalculateLinkLength(header->len);
if (frame_len == 0) {
DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_LEN_TOO_SMALL);
goto error;
DNP3Resync(&input, &input_len, &processed);
continue;
}
if (input_len < frame_len) {
/* Insufficient data, just break - will wait for more data. */
@ -1235,7 +1261,7 @@ static int DNP3HandleResponseLinkLayer(
* application headers. */
if (!DNP3HasUserData(header, STREAM_TOCLIENT)) {
DNP3SetEvent(dnp3, DNP3_DECODER_EVENT_LEN_TOO_SMALL);
goto error;
goto next;
}
if (!DNP3CheckUserDataCRCs(input + sizeof(DNP3LinkHeader),
@ -1254,10 +1280,6 @@ static int DNP3HandleResponseLinkLayer(
}
SCReturnInt(processed);
error:
/* Error out. Should only happen if the data stream no longer
* looks like DNP3. */
SCReturnInt(-1);
}
/**
@ -2768,7 +2790,7 @@ static int DNP3ParserIncorrectUserData(void)
int r = AppLayerParserParse(NULL, alp_tctx, &flow, ALPROTO_DNP3,
STREAM_TOCLIENT, packet_bytes, sizeof(packet_bytes));
FAIL_IF(r == 0);
FAIL_IF(r != 0);
AppLayerParserThreadCtxFree(alp_tctx);
StreamTcpFreeConfig(true);

Loading…
Cancel
Save