tls: use incomplete API to get full TLS records

The TLS record header is parsed in streaming mode still, but once the
record size is known we tell the app-layer API to give us the full
record.

Ticket: #5481
pull/7896/head
Victor Julien 4 years ago
parent 2d308c000d
commit 129fcb5c72

@ -209,6 +209,26 @@ SslConfig ssl_config;
#define HAS_SPACE(n) ((uint64_t)(input - initial_input) + (uint64_t)(n) <= (uint64_t)(input_len)) #define HAS_SPACE(n) ((uint64_t)(input - initial_input) + (uint64_t)(n) <= (uint64_t)(input_len))
struct SSLDecoderResult {
int retval; // nr bytes consumed from input, or < 0 on error
uint32_t needed; // more bytes needed
};
#define SSL_DECODER_ERROR(e) \
(struct SSLDecoderResult) \
{ \
(e), 0 \
}
#define SSL_DECODER_OK(c) \
(struct SSLDecoderResult) \
{ \
(c), 0 \
}
#define SSL_DECODER_INCOMPLETE(c, n) \
(struct SSLDecoderResult) \
{ \
(c), (n) \
}
static inline int SafeMemcpy(void *dst, size_t dst_offset, size_t dst_size, static inline int SafeMemcpy(void *dst, size_t dst_offset, size_t dst_size,
const void *src, size_t src_offset, size_t src_size, size_t src_tocopy) WARN_UNUSED; const void *src, size_t src_offset, size_t src_size, size_t src_tocopy) WARN_UNUSED;
@ -2316,8 +2336,9 @@ static int SSLv2Decode(uint8_t direction, SSLState *ssl_state, AppLayerParserSta
} }
} }
static int SSLv3Decode(uint8_t direction, SSLState *ssl_state, AppLayerParserState *pstate, static struct SSLDecoderResult SSLv3Decode(uint8_t direction, SSLState *ssl_state,
const uint8_t *input, const uint32_t input_len, const StreamSlice stream_slice) AppLayerParserState *pstate, const uint8_t *input, const uint32_t input_len,
const StreamSlice stream_slice)
{ {
uint32_t parsed = 0; uint32_t parsed = 0;
uint32_t record_len; /* slice of input_len for the current record */ uint32_t record_len; /* slice of input_len for the current record */
@ -2328,7 +2349,7 @@ static int SSLv3Decode(uint8_t direction, SSLState *ssl_state, AppLayerParserSta
DEBUG_VALIDATE_BUG_ON(retval > (int)input_len); DEBUG_VALIDATE_BUG_ON(retval > (int)input_len);
SCLogDebug("SSLv3ParseRecord returned %d", retval); SCLogDebug("SSLv3ParseRecord returned %d", retval);
SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_TLS_HEADER); SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_TLS_HEADER);
return -1; return SSL_DECODER_ERROR(-1);
} }
SCLogDebug("%s input %p record_length %u", (direction == 0) ? "toserver" : "toclient", SCLogDebug("%s input %p record_length %u", (direction == 0) ? "toserver" : "toclient",
input, ssl_state->curr_connp->record_length); input, ssl_state->curr_connp->record_length);
@ -2349,15 +2370,22 @@ static int SSLv3Decode(uint8_t direction, SSLState *ssl_state, AppLayerParserSta
SCLogDebug("record length %u processed %u got %u", SCLogDebug("record length %u processed %u got %u",
ssl_state->curr_connp->record_length, ssl_state->curr_connp->bytes_processed, record_len); ssl_state->curr_connp->record_length, ssl_state->curr_connp->bytes_processed, record_len);
if (ssl_state->curr_connp->record_length > input_len - parsed) {
uint32_t needed = ssl_state->curr_connp->record_length;
SCLogDebug("record len %u input_len %u parsed %u: need %u bytes more data",
ssl_state->curr_connp->record_length, input_len, parsed, needed);
return SSL_DECODER_INCOMPLETE(parsed, needed);
}
if (record_len == 0) { if (record_len == 0) {
return parsed; return SSL_DECODER_OK(parsed);
} }
/* record_length should never be zero */ /* record_length should never be zero */
if (ssl_state->curr_connp->record_length == 0) { if (ssl_state->curr_connp->record_length == 0) {
SCLogDebug("SSLv3 Record length is 0"); SCLogDebug("SSLv3 Record length is 0");
SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_TLS_HEADER); SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_TLS_HEADER);
return -1; return SSL_DECODER_ERROR(-1);
} }
AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input + parsed, AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input + parsed,
ssl_state->curr_connp->record_length, direction, TLS_FRAME_DATA); ssl_state->curr_connp->record_length, direction, TLS_FRAME_DATA);
@ -2434,7 +2462,7 @@ static int SSLv3Decode(uint8_t direction, SSLState *ssl_state, AppLayerParserSta
SSLParserReset(ssl_state); SSLParserReset(ssl_state);
SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD); SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
SCLogDebug("record len < 4 => %u", ssl_state->curr_connp->record_length); SCLogDebug("record len < 4 => %u", ssl_state->curr_connp->record_length);
return -1; return SSL_DECODER_ERROR(-1);
} }
int retval = SSLv3ParseHandshakeProtocol(ssl_state, input + parsed, int retval = SSLv3ParseHandshakeProtocol(ssl_state, input + parsed,
@ -2446,7 +2474,7 @@ static int SSLv3Decode(uint8_t direction, SSLState *ssl_state, AppLayerParserSta
SSLSetEvent(ssl_state, SSLSetEvent(ssl_state,
TLS_DECODER_EVENT_INVALID_SSL_RECORD); TLS_DECODER_EVENT_INVALID_SSL_RECORD);
SCLogDebug("SSLv3ParseHandshakeProtocol returned %d", retval); SCLogDebug("SSLv3ParseHandshakeProtocol returned %d", retval);
return -1; return SSL_DECODER_ERROR(-1);
} }
SCLogDebug("retval %d", retval); SCLogDebug("retval %d", retval);
@ -2468,7 +2496,7 @@ static int SSLv3Decode(uint8_t direction, SSLState *ssl_state, AppLayerParserSta
SCLogDebug("trigger RAW! (post HS)"); SCLogDebug("trigger RAW! (post HS)");
AppLayerParserTriggerRawStreamReassembly(ssl_state->f, AppLayerParserTriggerRawStreamReassembly(ssl_state->f,
direction == 0 ? STREAM_TOSERVER : STREAM_TOCLIENT); direction == 0 ? STREAM_TOSERVER : STREAM_TOCLIENT);
return parsed; return SSL_DECODER_OK(parsed);
} }
case SSLV3_HEARTBEAT_PROTOCOL: { case SSLV3_HEARTBEAT_PROTOCOL: {
AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input + parsed, AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input + parsed,
@ -2477,7 +2505,7 @@ static int SSLv3Decode(uint8_t direction, SSLState *ssl_state, AppLayerParserSta
record_len, direction); record_len, direction);
if (retval < 0) { if (retval < 0) {
SCLogDebug("SSLv3ParseHeartbeatProtocol returned %d", retval); SCLogDebug("SSLv3ParseHeartbeatProtocol returned %d", retval);
return -1; return SSL_DECODER_ERROR(-1);
} }
break; break;
} }
@ -2486,7 +2514,7 @@ static int SSLv3Decode(uint8_t direction, SSLState *ssl_state, AppLayerParserSta
SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_RECORD_TYPE); SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_RECORD_TYPE);
SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD); SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
SCLogDebug("unsupported record type"); SCLogDebug("unsupported record type");
return -1; return SSL_DECODER_ERROR(-1);
} }
if (HaveEntireRecord(ssl_state->curr_connp, record_len)) { if (HaveEntireRecord(ssl_state->curr_connp, record_len)) {
@ -2498,7 +2526,7 @@ static int SSLv3Decode(uint8_t direction, SSLState *ssl_state, AppLayerParserSta
/* defensive checks. Something is wrong. */ /* defensive checks. Something is wrong. */
SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD); SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
SCLogDebug("defensive checks. Something is wrong."); SCLogDebug("defensive checks. Something is wrong.");
return -1; return SSL_DECODER_ERROR(-1);
} }
SCLogDebug("record complete, trigger RAW"); SCLogDebug("record complete, trigger RAW");
@ -2511,15 +2539,15 @@ static int SSLv3Decode(uint8_t direction, SSLState *ssl_state, AppLayerParserSta
parsed += diff; parsed += diff;
SSLParserReset(ssl_state); SSLParserReset(ssl_state);
ValidateRecordState(ssl_state->curr_connp); ValidateRecordState(ssl_state->curr_connp);
return parsed; return SSL_DECODER_OK(parsed);
/* we still don't have the entire record for the one we are /* we still don't have the entire record for the one we are
currently parsing */ currently parsing */
} else { } else {
parsed += record_len; parsed += record_len;
ssl_state->curr_connp->bytes_processed += record_len; ssl_state->curr_connp->bytes_processed += record_len;
ValidateRecordState(ssl_state->curr_connp); ValidateRecordState(ssl_state->curr_connp);
return parsed; return SSL_DECODER_OK(parsed);
} }
} }
@ -2548,6 +2576,7 @@ static AppLayerResult SSLDecode(Flow *f, uint8_t direction, void *alstate,
uint32_t counter = 0; uint32_t counter = 0;
ssl_state->f = f; ssl_state->f = f;
const uint8_t *input = StreamSliceGetData(&stream_slice); const uint8_t *input = StreamSliceGetData(&stream_slice);
const uint8_t *init_input = input;
int32_t input_len = (int32_t)StreamSliceGetDataLen(&stream_slice); int32_t input_len = (int32_t)StreamSliceGetDataLen(&stream_slice);
if (input == NULL && if (input == NULL &&
@ -2626,17 +2655,23 @@ static AppLayerResult SSLDecode(Flow *f, uint8_t direction, void *alstate,
SCLogDebug("Continuing parsing TLS record: record_length %u, bytes_processed %u", SCLogDebug("Continuing parsing TLS record: record_length %u, bytes_processed %u",
ssl_state->curr_connp->record_length, ssl_state->curr_connp->bytes_processed); ssl_state->curr_connp->record_length, ssl_state->curr_connp->bytes_processed);
} }
int retval = SSLv3Decode(direction, ssl_state, pstate, input, input_len, stream_slice); struct SSLDecoderResult r =
if (retval < 0 || retval > input_len) { SSLv3Decode(direction, ssl_state, pstate, input, input_len, stream_slice);
DEBUG_VALIDATE_BUG_ON(retval > input_len); if (r.retval < 0 || r.retval > input_len) {
DEBUG_VALIDATE_BUG_ON(r.retval > input_len);
SCLogDebug("Error parsing TLS. Reseting parser " SCLogDebug("Error parsing TLS. Reseting parser "
"state. Let's get outta here"); "state. Let's get outta here");
SSLParserReset(ssl_state); SSLParserReset(ssl_state);
return APP_LAYER_ERROR; return APP_LAYER_ERROR;
} else if (r.needed) {
input += r.retval;
SCLogDebug("returning consumed %" PRIuMAX " needed %u",
(uintmax_t)(input - init_input), r.needed);
SCReturnStruct(APP_LAYER_INCOMPLETE(input - init_input, r.needed));
} }
input_len -= retval; input_len -= r.retval;
input += retval; input += r.retval;
SCLogDebug("TLS decoder consumed %d bytes: %u left", retval, input_len); SCLogDebug("TLS decoder consumed %d bytes: %u left", r.retval, input_len);
if (ssl_state->curr_connp->bytes_processed == SSLV3_RECORD_HDR_LEN if (ssl_state->curr_connp->bytes_processed == SSLV3_RECORD_HDR_LEN
&& ssl_state->curr_connp->record_length == 0) { && ssl_state->curr_connp->record_length == 0) {

Loading…
Cancel
Save