proto detect: TCP cleanup

Split function into multiple smaller ones.
pull/2359/head
Victor Julien 9 years ago
parent 8347aa01fa
commit 6022fa44a5

@ -115,29 +115,163 @@ void AppLayerIncTxCounter(ThreadVars *tv, Flow *f, uint64_t step)
} }
} }
int AppLayerHandleTCPData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, /* See if we're going to have to give up:
Packet *p, Flow *f, *
TcpSession *ssn, TcpStream *stream, * If we're getting a lot of data in one direction and the
uint8_t *data, uint32_t data_len, * proto for this direction is unknown, proto detect will
uint8_t flags) * hold up segments in the segment list in the stream.
* They are held so that if we detect the protocol on the
* opposing stream, we can still parse this side of the stream
* as well. However, some sessions are very unbalanced. FTP
* data channels, large PUT/POST request and many others, can
* lead to cases where we would have to store many megabytes
* worth of segments before we see the opposing stream. This
* leads to risks of resource starvation.
*
* Here a cutoff point is enforced. If we've stored 100k in
* one direction and we've seen no data in the other direction,
* we give up.
*
* Giving up means we disable applayer an set an applayer event
*/
static void TCPProtoDetectCheckBailConditions(Flow *f, TcpSession *ssn, Packet *p)
{ {
SCEnter(); uint32_t size_ts = ssn->client.last_ack - ssn->client.isn - 1;
uint32_t size_tc = ssn->server.last_ack - ssn->server.isn - 1;
SCLogDebug("size_ts %u, size_tc %u", size_ts, size_tc);
DEBUG_ASSERT_FLOW_LOCKED(f); #ifdef DEBUG_VALIDATION
if (!(ssn->client.flags & STREAMTCP_STREAM_FLAG_GAP))
BUG_ON(size_ts > 1000000UL);
if (!(ssn->server.flags & STREAMTCP_STREAM_FLAG_GAP))
BUG_ON(size_tc > 1000000UL);
#endif /* DEBUG_VALIDATION */
AppLayerThreadCtx *app_tctx = ra_ctx->app_tctx; if (ProtoDetectDone(f, ssn, STREAM_TOSERVER) &&
AppProto *alproto; ProtoDetectDone(f, ssn, STREAM_TOCLIENT))
AppProto *alproto_otherdir; {
uint8_t dir; DisableAppLayer(f);
uint32_t data_al_so_far; ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
int r = 0;
uint8_t first_data_dir; } else if (FLOW_IS_PM_DONE(f, STREAM_TOSERVER) && FLOW_IS_PP_DONE(f, STREAM_TOSERVER) &&
size_ts > 100000 && size_tc == 0)
{
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
APPLAYER_PROTO_DETECTION_SKIPPED);
} else if (FLOW_IS_PM_DONE(f, STREAM_TOCLIENT) && FLOW_IS_PP_DONE(f, STREAM_TOCLIENT) &&
size_tc > 100000 && size_ts == 0)
{
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
APPLAYER_PROTO_DETECTION_SKIPPED);
/* little data in ts direction, pp done, pm not done (max
* depth not reached), ts direction done, lots of data in
* tc direction. */
} else if (size_tc > 100000 &&
FLOW_IS_PP_DONE(f, STREAM_TOSERVER) && !(FLOW_IS_PM_DONE(f, STREAM_TOSERVER)) &&
FLOW_IS_PM_DONE(f, STREAM_TOCLIENT) && FLOW_IS_PP_DONE(f, STREAM_TOCLIENT))
{
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
APPLAYER_PROTO_DETECTION_SKIPPED);
/* little data in tc direction, pp done, pm not done (max
* depth not reached), tc direction done, lots of data in
* ts direction. */
} else if (size_ts > 100000 &&
FLOW_IS_PP_DONE(f, STREAM_TOCLIENT) && !(FLOW_IS_PM_DONE(f, STREAM_TOCLIENT)) &&
FLOW_IS_PM_DONE(f, STREAM_TOSERVER) && FLOW_IS_PP_DONE(f, STREAM_TOSERVER))
{
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
APPLAYER_PROTO_DETECTION_SKIPPED);
/* in case of really low TS data (e.g. 4 bytes) we can have
* the PP complete, PM not complete (depth not reached) and
* the TC side also not recognized (proto unknown) */
} else if (size_tc > 100000 &&
FLOW_IS_PP_DONE(f, STREAM_TOSERVER) && !(FLOW_IS_PM_DONE(f, STREAM_TOSERVER)) &&
(!FLOW_IS_PM_DONE(f, STREAM_TOCLIENT) && !FLOW_IS_PP_DONE(f, STREAM_TOCLIENT)))
{
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
APPLAYER_PROTO_DETECTION_SKIPPED);
}
}
SCLogDebug("data_len %u flags %02X", data_len, flags); /** \todo modifying p this way seems too hacky */
if (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) { static int TCPProtoDetectTriggerOpposingSide(ThreadVars *tv,
SCLogDebug("STREAMTCP_FLAG_APP_LAYER_DISABLED is set"); TcpReassemblyThreadCtx *ra_ctx,
goto end; Packet *p, TcpSession *ssn, TcpStream *stream,
uint8_t flags)
{
TcpStream *opposing_stream = NULL;
if (stream == &ssn->client) {
opposing_stream = &ssn->server;
if (StreamTcpInlineMode()) {
p->flowflags &= ~FLOW_PKT_TOSERVER;
p->flowflags |= FLOW_PKT_TOCLIENT;
} else {
p->flowflags &= ~FLOW_PKT_TOCLIENT;
p->flowflags |= FLOW_PKT_TOSERVER;
}
} else {
opposing_stream = &ssn->client;
if (StreamTcpInlineMode()) {
p->flowflags &= ~FLOW_PKT_TOCLIENT;
p->flowflags |= FLOW_PKT_TOSERVER;
} else {
p->flowflags &= ~FLOW_PKT_TOSERVER;
p->flowflags |= FLOW_PKT_TOCLIENT;
}
}
int ret = 0;
/* if the opposing side is not going to work, then
* we just have to give up. */
if (opposing_stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY)
ret = -1;
else
ret = StreamTcpReassembleAppLayer(tv, ra_ctx, ssn,
opposing_stream, p);
if (stream == &ssn->client) {
if (StreamTcpInlineMode()) {
p->flowflags &= ~FLOW_PKT_TOCLIENT;
p->flowflags |= FLOW_PKT_TOSERVER;
} else {
p->flowflags &= ~FLOW_PKT_TOSERVER;
p->flowflags |= FLOW_PKT_TOCLIENT;
}
} else {
if (StreamTcpInlineMode()) {
p->flowflags &= ~FLOW_PKT_TOSERVER;
p->flowflags |= FLOW_PKT_TOCLIENT;
} else {
p->flowflags &= ~FLOW_PKT_TOCLIENT;
p->flowflags |= FLOW_PKT_TOSERVER;
}
} }
return ret;
}
/** \todo data const */
static int TCPProtoDetect(ThreadVars *tv,
TcpReassemblyThreadCtx *ra_ctx, AppLayerThreadCtx *app_tctx,
Packet *p, Flow *f, TcpSession *ssn, TcpStream *stream,
uint8_t *data, uint32_t data_len, uint8_t flags)
{
uint32_t data_al_so_far = 0;
AppProto *alproto;
AppProto *alproto_otherdir;
int dir;
if (flags & STREAM_TOSERVER) { if (flags & STREAM_TOSERVER) {
alproto = &f->alproto_ts; alproto = &f->alproto_ts;
@ -148,328 +282,263 @@ int AppLayerHandleTCPData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
alproto_otherdir = &f->alproto_ts; alproto_otherdir = &f->alproto_ts;
dir = 1; dir = 1;
} }
if (data_len > 0)
data_al_so_far = f->data_al_so_far[dir];
/* if we don't know the proto yet and we have received a stream SCLogDebug("Stream initializer (len %" PRIu32 ")", data_len);
* initializer message, we run proto detection.
* We receive 2 stream init msgs (one for each direction) but we
* only run the proto detection once. */
if (*alproto == ALPROTO_UNKNOWN && (flags & STREAM_GAP)) {
StreamTcpSetStreamFlagAppProtoDetectionCompleted(stream);
StreamTcpSetSessionNoReassemblyFlag(ssn, dir);
SCLogDebug("ALPROTO_UNKNOWN flow %p, due to GAP in stream start", f);
} else if (*alproto == ALPROTO_UNKNOWN && (flags & STREAM_START)) {
if (data_len == 0)
data_al_so_far = 0;
else
data_al_so_far = f->data_al_so_far[dir];
SCLogDebug("Stream initializer (len %" PRIu32 ")", data_len);
#ifdef PRINT #ifdef PRINT
if (data_len > 0) { if (data_len > 0) {
printf("=> Init Stream Data (app layer) -- start %s%s\n", printf("=> Init Stream Data (app layer) -- start %s%s\n",
flags & STREAM_TOCLIENT ? "toclient" : "", flags & STREAM_TOCLIENT ? "toclient" : "",
flags & STREAM_TOSERVER ? "toserver" : ""); flags & STREAM_TOSERVER ? "toserver" : "");
PrintRawDataFp(stdout, data, data_len); PrintRawDataFp(stdout, data, data_len);
printf("=> Init Stream Data -- end\n"); printf("=> Init Stream Data -- end\n");
} }
#endif #endif
PACKET_PROFILING_APP_PD_START(app_tctx); PACKET_PROFILING_APP_PD_START(app_tctx);
*alproto = AppLayerProtoDetectGetProto(app_tctx->alpd_tctx, *alproto = AppLayerProtoDetectGetProto(app_tctx->alpd_tctx,
f, f, data, data_len,
data, data_len, IPPROTO_TCP, flags);
IPPROTO_TCP, flags); PACKET_PROFILING_APP_PD_END(app_tctx);
PACKET_PROFILING_APP_PD_END(app_tctx);
if (*alproto != ALPROTO_UNKNOWN) {
if (*alproto != ALPROTO_UNKNOWN) { if (*alproto_otherdir != ALPROTO_UNKNOWN && *alproto_otherdir != *alproto) {
if (*alproto_otherdir != ALPROTO_UNKNOWN && *alproto_otherdir != *alproto) { AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events, APPLAYER_MISMATCH_PROTOCOL_BOTH_DIRECTIONS);
APPLAYER_MISMATCH_PROTOCOL_BOTH_DIRECTIONS); /* it indicates some data has already been sent to the parser */
/* it indicates some data has already been sent to the parser */ if (ssn->data_first_seen_dir == APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
if (ssn->data_first_seen_dir == APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) { f->alproto = *alproto = *alproto_otherdir;
} else {
if (flags & STREAM_TOCLIENT)
f->alproto = *alproto_otherdir = *alproto;
else
f->alproto = *alproto = *alproto_otherdir; f->alproto = *alproto = *alproto_otherdir;
} else {
if (flags & STREAM_TOCLIENT)
f->alproto = *alproto_otherdir = *alproto;
else
f->alproto = *alproto = *alproto_otherdir;
}
} }
}
f->alproto = *alproto;
StreamTcpSetStreamFlagAppProtoDetectionCompleted(stream);
TcpSessionSetReassemblyDepth(ssn,
AppLayerParserGetStreamDepth(f->proto, *alproto));
f->alproto = *alproto; /* account flow if we have both sides */
StreamTcpSetStreamFlagAppProtoDetectionCompleted(stream); if (*alproto_otherdir != ALPROTO_UNKNOWN) {
TcpSessionSetReassemblyDepth(ssn, AppLayerParserGetStreamDepth(f->proto, *alproto)); AppLayerIncFlowCounter(tv, f);
}
/* account flow if we have both side */ /* if we have seen data from the other direction first, send
if (*alproto_otherdir != ALPROTO_UNKNOWN) { * data for that direction first to the parser. This shouldn't
AppLayerIncFlowCounter(tv, f); * be an issue, since each stream processing happens
* independently of the other stream direction. At this point of
* call, you need to know that this function's already being
* called by the very same StreamReassembly() function that we
* will now call shortly for the opposing direction. */
if ((ssn->data_first_seen_dir & (STREAM_TOSERVER | STREAM_TOCLIENT)) &&
!(flags & ssn->data_first_seen_dir))
{
if (TCPProtoDetectTriggerOpposingSide(tv, ra_ctx,
p, ssn, stream, flags) != 0)
{
DisableAppLayer(f);
goto failure;
} }
}
/* if we have seen data from the other direction first, send /* if the parser operates such that it needs to see data from
* data for that direction first to the parser. This shouldn't * a particular direction first, we check if we have seen
* be an issue, since each stream processing happens * data from that direction first for the flow. IF it is not
* independently of the other stream direction. At this point of * the same, we set an event and exit.
* call, you need to know that this function's already being *
* called by the very same StreamReassembly() function that we * \todo We need to figure out a more robust solution for this,
* will now call shortly for the opposing direction. */ * as this can lead to easy evasion tactics, where the
if ((ssn->data_first_seen_dir & (STREAM_TOSERVER | STREAM_TOCLIENT)) && * attackeer can first send some dummy data in the wrong
!(flags & ssn->data_first_seen_dir)) { * direction first to mislead our proto detection process.
TcpStream *opposing_stream = NULL; * While doing this we need to update the parsers as well,
if (stream == &ssn->client) { * since the parsers must be robust to see such wrong
opposing_stream = &ssn->server; * direction data.
if (StreamTcpInlineMode()) { * Either ways the moment we see the
p->flowflags &= ~FLOW_PKT_TOSERVER; * APPLAYER_WRONG_DIRECTION_FIRST_DATA event set for the
p->flowflags |= FLOW_PKT_TOCLIENT; * flow, it shows something's fishy.
} else { */
p->flowflags &= ~FLOW_PKT_TOCLIENT; if (ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) {
p->flowflags |= FLOW_PKT_TOSERVER; uint8_t first_data_dir;
} first_data_dir = AppLayerParserGetFirstDataDir(f->proto, *alproto);
} else {
opposing_stream = &ssn->client; if (first_data_dir && !(first_data_dir & ssn->data_first_seen_dir)) {
if (StreamTcpInlineMode()) { AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
p->flowflags &= ~FLOW_PKT_TOCLIENT; APPLAYER_WRONG_DIRECTION_FIRST_DATA);
p->flowflags |= FLOW_PKT_TOSERVER; DisableAppLayer(f);
} else { /* Set a value that is neither STREAM_TOSERVER, nor STREAM_TOCLIENT */
p->flowflags &= ~FLOW_PKT_TOSERVER; ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
p->flowflags |= FLOW_PKT_TOCLIENT; goto failure;
} }
} /* This can happen if the current direction is not the
* right direction, and the data from the other(also
* the right direction) direction is available to be sent
* to the app layer, but it is not ack'ed yet and hence
* the forced call to STreamTcpAppLayerReassemble still
* hasn't managed to send data from the other direction
* to the app layer. */
if (first_data_dir && !(first_data_dir & flags)) {
BUG_ON(*alproto_otherdir != ALPROTO_UNKNOWN);
FlowCleanupAppLayer(f);
f->alproto = *alproto = ALPROTO_UNKNOWN;
StreamTcpResetStreamFlagAppProtoDetectionCompleted(stream);
FLOW_RESET_PP_DONE(f, flags);
FLOW_RESET_PM_DONE(f, flags);
goto failure;
}
}
int ret = 0; /* Set a value that is neither STREAM_TOSERVER, nor STREAM_TOCLIENT */
/* if the opposing side is not going to work, then ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
* we just have to give up. */
if (opposing_stream->flags & STREAMTCP_STREAM_FLAG_NOREASSEMBLY) /* finally, invoke the parser */
ret = -1; PACKET_PROFILING_APP_START(app_tctx, *alproto);
else int r = AppLayerParserParse(tv, app_tctx->alp_tctx, f, *alproto,
ret = StreamTcpReassembleAppLayer(tv, ra_ctx, ssn, flags, data + data_al_so_far,
opposing_stream, p); data_len - data_al_so_far);
if (stream == &ssn->client) { PACKET_PROFILING_APP_END(app_tctx, *alproto);
if (StreamTcpInlineMode()) { f->data_al_so_far[dir] = 0;
p->flowflags &= ~FLOW_PKT_TOCLIENT; if (r < 0)
p->flowflags |= FLOW_PKT_TOSERVER; goto failure;
} else {
p->flowflags &= ~FLOW_PKT_TOSERVER; } else {
p->flowflags |= FLOW_PKT_TOCLIENT; /* if the ssn is midstream, we may end up with a case where the
} * start of an HTTP request is missing. We won't detect HTTP based
} else { * on the request. However, the reply is fine, so we detect
if (StreamTcpInlineMode()) { * HTTP anyway. This leads to passing the incomplete request to
p->flowflags &= ~FLOW_PKT_TOSERVER; * the htp parser.
p->flowflags |= FLOW_PKT_TOCLIENT; *
} else { * This has been observed, where the http parser then saw many
p->flowflags &= ~FLOW_PKT_TOCLIENT; * bogus requests in the incomplete data.
p->flowflags |= FLOW_PKT_TOSERVER; *
} * To counter this case, a midstream session MUST find it's
} * protocol in the toserver direction. If not, we assume the
if (ret < 0) { * start of the request/toserver is incomplete and no reliable
DisableAppLayer(f); * detection and parsing is possible. So we give up.
goto failure; */
} if ((ssn->flags & STREAMTCP_FLAG_MIDSTREAM) &&
!(ssn->flags & STREAMTCP_FLAG_MIDSTREAM_SYNACK))
{
if (FLOW_IS_PM_DONE(f, STREAM_TOSERVER) && FLOW_IS_PP_DONE(f, STREAM_TOSERVER)) {
SCLogDebug("midstream end pd %p", ssn);
/* midstream and toserver detection failed: give up */
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
goto end;
} }
}
/* if the parser operates such that it needs to see data from if (*alproto_otherdir != ALPROTO_UNKNOWN) {
* a particular direction first, we check if we have seen uint8_t first_data_dir;
* data from that direction first for the flow. IF it is not first_data_dir = AppLayerParserGetFirstDataDir(f->proto, *alproto_otherdir);
* the same, we set an event and exit.
* /* this would handle this test case -
* \todo We need to figure out a more robust solution for this, * http parser which says it wants to see toserver data first only.
* as this can lead to easy evasion tactics, where the * tcp handshake
* attackeer can first send some dummy data in the wrong * toclient data first received. - RUBBISH DATA which
* direction first to mislead our proto detection process. * we don't detect as http
* While doing this we need to update the parsers as well, * toserver data next sent - we detect this as http.
* since the parsers must be robust to see such wrong * at this stage we see that toclient is the first data seen
* direction data. * for this session and we try and redetect the app protocol,
* Either ways the moment we see the * but we are unable to detect the app protocol like before.
* APPLAYER_WRONG_DIRECTION_FIRST_DATA event set for the * But since we have managed to detect the protocol for the
* flow, it shows something's fishy. * other direction as http, we try to use that. At this
* stage we check if the direction of this stream matches
* to that acceptable by the app parser. If it is not the
* acceptable direction we error out.
*/ */
if (ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) { if ((ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) &&
first_data_dir = AppLayerParserGetFirstDataDir(f->proto, *alproto); (first_data_dir) && !(first_data_dir & flags))
{
if (first_data_dir && !(first_data_dir & ssn->data_first_seen_dir)) { DisableAppLayer(f);
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events, goto failure;
APPLAYER_WRONG_DIRECTION_FIRST_DATA);
DisableAppLayer(f);
/* Set a value that is neither STREAM_TOSERVER, nor STREAM_TOCLIENT */
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
goto failure;
}
/* This can happen if the current direction is not the
* right direction, and the data from the other(also
* the right direction) direction is available to be sent
* to the app layer, but it is not ack'ed yet and hence
* the forced call to STreamTcpAppLayerReassemble still
* hasn't managed to send data from the other direction
* to the app layer. */
if (first_data_dir && !(first_data_dir & flags)) {
BUG_ON(*alproto_otherdir != ALPROTO_UNKNOWN);
FlowCleanupAppLayer(f);
f->alproto = *alproto = ALPROTO_UNKNOWN;
StreamTcpResetStreamFlagAppProtoDetectionCompleted(stream);
FLOW_RESET_PP_DONE(f, flags);
FLOW_RESET_PM_DONE(f, flags);
goto failure;
}
} }
/* Set a value that is neither STREAM_TOSERVER, nor STREAM_TOCLIENT */ if (data_len > 0)
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER; ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
PACKET_PROFILING_APP_START(app_tctx, *alproto); PACKET_PROFILING_APP_START(app_tctx, *alproto_otherdir);
r = AppLayerParserParse(tv, app_tctx->alp_tctx, f, *alproto, int r = AppLayerParserParse(tv, app_tctx->alp_tctx, f,
flags, data + data_al_so_far, *alproto_otherdir, flags,
data_len - data_al_so_far); data + data_al_so_far,
PACKET_PROFILING_APP_END(app_tctx, *alproto); data_len - data_al_so_far);
f->data_al_so_far[dir] = 0; PACKET_PROFILING_APP_END(app_tctx, *alproto_otherdir);
} else { if (FLOW_IS_PM_DONE(f, flags) && FLOW_IS_PP_DONE(f, flags)) {
/* if the ssn is midstream, we may end up with a case where the AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
* start of an HTTP request is missing. We won't detect HTTP based APPLAYER_DETECT_PROTOCOL_ONLY_ONE_DIRECTION);
* on the request. However, the reply is fine, so we detect StreamTcpSetStreamFlagAppProtoDetectionCompleted(stream);
* HTTP anyway. This leads to passing the incomplete request to f->data_al_so_far[dir] = 0;
* the htp parser. TcpSessionSetReassemblyDepth(ssn,
* AppLayerParserGetStreamDepth(f->proto, *alproto));
* This has been observed, where the http parser then saw many } else {
* bogus requests in the incomplete data. f->data_al_so_far[dir] = data_len;
*
* To counter this case, a midstream session MUST find it's
* protocol in the toserver direction. If not, we assume the
* start of the request/toserver is incomplete and no reliable
* detection and parsing is possible. So we give up.
*/
if ((ssn->flags & STREAMTCP_FLAG_MIDSTREAM) && !(ssn->flags & STREAMTCP_FLAG_MIDSTREAM_SYNACK)) {
if (FLOW_IS_PM_DONE(f, STREAM_TOSERVER) && FLOW_IS_PP_DONE(f, STREAM_TOSERVER)) {
SCLogDebug("midstream end pd %p", ssn);
/* midstream and toserver detection failed: give up */
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
goto end;
}
} }
if (r < 0)
goto failure;
if (*alproto_otherdir != ALPROTO_UNKNOWN) { } else {
first_data_dir = AppLayerParserGetFirstDataDir(f->proto, *alproto_otherdir); /* both sides unknown, let's see if we need to give up */
TCPProtoDetectCheckBailConditions(f, ssn, p);
/* this would handle this test case - }
* http parser which says it wants to see toserver data first only. }
* tcp handshake end:
* toclient data first received. - RUBBISH DATA which return 0;
* we don't detect as http
* toserver data next sent - we detect this as http.
* at this stage we see that toclient is the first data seen
* for this session and we try and redetect the app protocol,
* but we are unable to detect the app protocol like before.
* But since we have managed to detect the protocol for the
* other direction as http, we try to use that. At this
* stage we check if the direction of this stream matches
* to that acceptable by the app parser. If it is not the
* acceptable direction we error out.
*/
if ((ssn->data_first_seen_dir != APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER) &&
(first_data_dir) && !(first_data_dir & flags))
{
DisableAppLayer(f);
goto failure;
}
if (data_len > 0) failure:
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER; return -1;
}
PACKET_PROFILING_APP_START(app_tctx, *alproto_otherdir);
r = AppLayerParserParse(tv, app_tctx->alp_tctx, f,
*alproto_otherdir, flags,
data + data_al_so_far,
data_len - data_al_so_far);
PACKET_PROFILING_APP_END(app_tctx, *alproto_otherdir);
if (FLOW_IS_PM_DONE(f, flags) && FLOW_IS_PP_DONE(f, flags)) {
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
APPLAYER_DETECT_PROTOCOL_ONLY_ONE_DIRECTION);
StreamTcpSetStreamFlagAppProtoDetectionCompleted(stream);
f->data_al_so_far[dir] = 0;
TcpSessionSetReassemblyDepth(ssn, AppLayerParserGetStreamDepth(f->proto, *alproto));
} else {
f->data_al_so_far[dir] = data_len;
}
} else {
/* See if we're going to have to give up:
*
* If we're getting a lot of data in one direction and the
* proto for this direction is unknown, proto detect will
* hold up segments in the segment list in the stream.
* They are held so that if we detect the protocol on the
* opposing stream, we can still parse this side of the stream
* as well. However, some sessions are very unbalanced. FTP
* data channels, large PUT/POST request and many others, can
* lead to cases where we would have to store many megabytes
* worth of segments before we see the opposing stream. This
* leads to risks of resource starvation.
*
* Here a cutoff point is enforced. If we've stored 100k in
* one direction and we've seen no data in the other direction,
* we give up. */
uint32_t size_ts = ssn->client.last_ack - ssn->client.isn - 1;
uint32_t size_tc = ssn->server.last_ack - ssn->server.isn - 1;
SCLogDebug("size_ts %u, size_tc %u", size_ts, size_tc);
#ifdef DEBUG_VALIDATION
if (!(ssn->client.flags & STREAMTCP_STREAM_FLAG_GAP))
BUG_ON(size_ts > 1000000UL);
if (!(ssn->server.flags & STREAMTCP_STREAM_FLAG_GAP))
BUG_ON(size_tc > 1000000UL);
#endif /* DEBUG_VALIDATION */
if (ProtoDetectDone(f, ssn, STREAM_TOSERVER) && /** \brief handle TCP data for the app-layer.
ProtoDetectDone(f, ssn, STREAM_TOCLIENT)) *
{ * First run protocol detection and then when the protocol is known invoke
DisableAppLayer(f); * the app layer parser.
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER; */
int AppLayerHandleTCPData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx,
Packet *p, Flow *f,
TcpSession *ssn, TcpStream *stream,
uint8_t *data, uint32_t data_len,
uint8_t flags)
{
SCEnter();
} else if (FLOW_IS_PM_DONE(f, STREAM_TOSERVER) && FLOW_IS_PP_DONE(f, STREAM_TOSERVER) && DEBUG_ASSERT_FLOW_LOCKED(f);
size_ts > 100000 && size_tc == 0)
{ AppLayerThreadCtx *app_tctx = ra_ctx->app_tctx;
DisableAppLayer(f); AppProto alproto;
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER; uint8_t dir;
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events, int r = 0;
APPLAYER_PROTO_DETECTION_SKIPPED);
} else if (FLOW_IS_PM_DONE(f, STREAM_TOCLIENT) && FLOW_IS_PP_DONE(f, STREAM_TOCLIENT) && SCLogDebug("data_len %u flags %02X", data_len, flags);
size_tc > 100000 && size_ts == 0) if (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) {
{ SCLogDebug("STREAMTCP_FLAG_APP_LAYER_DISABLED is set");
DisableAppLayer(f); goto end;
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER; }
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
APPLAYER_PROTO_DETECTION_SKIPPED); if (flags & STREAM_TOSERVER) {
/* little data in ts direction, pp done, pm not done (max alproto = f->alproto_ts;
* depth not reached), ts direction done, lots of data in dir = 0;
* tc direction. */ } else {
} else if (size_tc > 100000 && alproto = f->alproto_tc;
FLOW_IS_PP_DONE(f, STREAM_TOSERVER) && !(FLOW_IS_PM_DONE(f, STREAM_TOSERVER)) && dir = 1;
FLOW_IS_PM_DONE(f, STREAM_TOCLIENT) && FLOW_IS_PP_DONE(f, STREAM_TOCLIENT)) }
{
DisableAppLayer(f); /* if we don't know the proto yet and we have received a stream
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER; * initializer message, we run proto detection.
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events, * We receive 2 stream init msgs (one for each direction) but we
APPLAYER_PROTO_DETECTION_SKIPPED); * only run the proto detection once. */
/* little data in tc direction, pp done, pm not done (max if (alproto == ALPROTO_UNKNOWN && (flags & STREAM_GAP)) {
* depth not reached), tc direction done, lots of data in StreamTcpSetStreamFlagAppProtoDetectionCompleted(stream);
* ts direction. */ StreamTcpSetSessionNoReassemblyFlag(ssn, dir);
} else if (size_ts > 100000 && SCLogDebug("ALPROTO_UNKNOWN flow %p, due to GAP in stream start", f);
FLOW_IS_PP_DONE(f, STREAM_TOCLIENT) && !(FLOW_IS_PM_DONE(f, STREAM_TOCLIENT)) &&
FLOW_IS_PM_DONE(f, STREAM_TOSERVER) && FLOW_IS_PP_DONE(f, STREAM_TOSERVER)) } else if (alproto == ALPROTO_UNKNOWN && (flags & STREAM_START)) {
{ /* run protocol detection */
DisableAppLayer(f); if (TCPProtoDetect(tv, ra_ctx, app_tctx, p, f, ssn, stream,
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER; data, data_len, flags) != 0) {
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events, goto failure;
APPLAYER_PROTO_DETECTION_SKIPPED);
/* in case of really low TS data (e.g. 4 bytes) we can have
* the PP complete, PM not complete (depth not reached) and
* the TC side also not recognized (proto unknown) */
} else if (size_tc > 100000 &&
FLOW_IS_PP_DONE(f, STREAM_TOSERVER) && !(FLOW_IS_PM_DONE(f, STREAM_TOSERVER)) &&
(!FLOW_IS_PM_DONE(f, STREAM_TOCLIENT) && !FLOW_IS_PP_DONE(f, STREAM_TOCLIENT)))
{
DisableAppLayer(f);
ssn->data_first_seen_dir = APP_LAYER_DATA_ALREADY_SENT_TO_APP_LAYER;
AppLayerDecoderEventsSetEventRaw(&p->app_layer_events,
APPLAYER_PROTO_DETECTION_SKIPPED);
}
}
} }
} else { } else {
SCLogDebug("stream data (len %" PRIu32 " alproto " SCLogDebug("stream data (len %" PRIu32 " alproto "

@ -31,19 +31,21 @@
#include "util-unittest.h" #include "util-unittest.h"
#include "util-unittest-helper.h" #include "util-unittest-helper.h"
void DetectAppLayerProtocolRegisterTests(void); static void DetectAppLayerProtocolRegisterTests(void);
int DetectAppLayerProtocolMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, static int DetectAppLayerProtocolMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, Flow *f, uint8_t flags, void *state,
Signature *s, SigMatch *m) Signature *s, SigMatch *m)
{ {
SCEnter();
int r = 0; int r = 0;
DetectAppLayerProtocolData *data = (DetectAppLayerProtocolData *)m->ctx; DetectAppLayerProtocolData *data = (DetectAppLayerProtocolData *)m->ctx;
r = (data->negated) ? (f->alproto != data->alproto) : r = (data->negated) ? (f->alproto != data->alproto) :
(f->alproto == data->alproto); (f->alproto == data->alproto);
return r; SCReturnInt(r);
} }
static DetectAppLayerProtocolData *DetectAppLayerProtocolParse(const char *arg) static DetectAppLayerProtocolData *DetectAppLayerProtocolParse(const char *arg)
@ -86,8 +88,8 @@ static DetectAppLayerProtocolData *DetectAppLayerProtocolParse(const char *arg)
return data; return data;
} }
int DetectAppLayerProtocolSetup(DetectEngineCtx *de_ctx, Signature *s, static int DetectAppLayerProtocolSetup(DetectEngineCtx *de_ctx,
char *arg) Signature *s, char *arg)
{ {
DetectAppLayerProtocolData *data = NULL; DetectAppLayerProtocolData *data = NULL;
SigMatch *sm = NULL; SigMatch *sm = NULL;
@ -125,10 +127,9 @@ error:
return -1; return -1;
} }
void DetectAppLayerProtocolFree(void *ptr) static void DetectAppLayerProtocolFree(void *ptr)
{ {
SCFree(ptr); SCFree(ptr);
return; return;
} }
@ -389,7 +390,7 @@ int DetectAppLayerProtocolTest09(void)
#endif /* UNITTESTS */ #endif /* UNITTESTS */
void DetectAppLayerProtocolRegisterTests(void) static void DetectAppLayerProtocolRegisterTests(void)
{ {
#ifdef UNITTESTS /* UNITTESTS */ #ifdef UNITTESTS /* UNITTESTS */
UtRegisterTest("DetectAppLayerProtocolTest01", UtRegisterTest("DetectAppLayerProtocolTest01",

Loading…
Cancel
Save