diff --git a/doc/userguide/rules/differences-from-snort.rst b/doc/userguide/rules/differences-from-snort.rst index db5691256856..6e22f51248e2 100644 --- a/doc/userguide/rules/differences-from-snort.rst +++ b/doc/userguide/rules/differences-from-snort.rst @@ -19,6 +19,7 @@ Automatic Protocol Detection - dns - http - imap (detection only by default; no parsing) + - pop3 (detection only by default; no parsing) - ftp - modbus (disabled by default; minimalist probe parser; can lead to false positives) - smb diff --git a/doc/userguide/rules/intro.rst b/doc/userguide/rules/intro.rst index ab35f8a311ca..c00fbe762625 100644 --- a/doc/userguide/rules/intro.rst +++ b/doc/userguide/rules/intro.rst @@ -96,6 +96,7 @@ you can pick from. These are: * ssh * smtp * imap +* pop3 * modbus (disabled by default) * dnp3 (disabled by default) * enip (disabled by default) diff --git a/etc/schema.json b/etc/schema.json index 7223e0d75541..b0b4e8652640 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -3826,6 +3826,9 @@ "pgsql": { "$ref": "#/$defs/stats_applayer_error" }, + "pop3": { + "$ref": "#/$defs/stats_applayer_error" + }, "quic": { "$ref": "#/$defs/stats_applayer_error" }, @@ -3943,6 +3946,9 @@ "pgsql": { "type": "integer" }, + "pop3": { + "type": "integer" + }, "quic": { "type": "integer" }, @@ -4054,6 +4060,9 @@ "pgsql": { "type": "integer" }, + "pop3": { + "type": "integer" + }, "quic": { "type": "integer" }, diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index 39e36bd31742..a28b38654989 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -287,6 +287,10 @@ static inline int PMGetProtoInspect(AppLayerProtoDetectThreadCtx *tctx, uint8_t pm_results_bf[(ALPROTO_MAX / 8) + 1]; memset(pm_results_bf, 0, sizeof(pm_results_bf)); + // Do not take pm_ctx->pp_max_len of all probing parsers, + // but only the probing parsers which matched a pattern. + uint32_t pp_max_len = pm_ctx->mpm_ctx.maxdepth; + /* loop through unique pattern id's. Can't use search_cnt here, * as that contains all matches, tctx->pmq.pattern_id_array_cnt * contains only *unique* matches. */ @@ -296,6 +300,9 @@ static inline int PMGetProtoInspect(AppLayerProtoDetectThreadCtx *tctx, AppProto proto = AppLayerProtoDetectPMMatchSignature( s, tctx, f, flags, buf, buflen, searchlen, rflow); + if (s->pp_max_depth > pp_max_len) { + pp_max_len = s->pp_max_depth; + } /* store each unique proto once */ if (AppProtoIsValid(proto) && !(pm_results_bf[proto / 8] & (1 << (proto % 8))) ) @@ -306,7 +313,7 @@ static inline int PMGetProtoInspect(AppLayerProtoDetectThreadCtx *tctx, s = s->next; } } - if (pm_matches == 0 && buflen >= pm_ctx->pp_max_len) { + if (pm_matches == 0 && buflen >= pp_max_len) { pm_matches = -2; } PmqReset(&tctx->pmq); @@ -613,6 +620,10 @@ static AppProto AppLayerProtoDetectPPGetProto(Flow *f, const uint8_t *buf, uint3 mask = pp_port_dp->alproto_mask; else if (pp_port_sp) mask = pp_port_sp->alproto_mask; + else { + // only pe0 + mask = pe0->alproto_mask; + } if (alproto_masks[0] == mask) { FLOW_SET_PP_DONE(f, dir); diff --git a/src/app-layer-ftp.c b/src/app-layer-ftp.c index 7f0accadc149..2a719e19acab 100644 --- a/src/app-layer-ftp.c +++ b/src/app-layer-ftp.c @@ -952,6 +952,15 @@ static int FTPGetAlstateProgress(void *vtx, uint8_t direction) return FTP_STATE_FINISHED; } +static AppProto FTPUserProbingParser( + Flow *f, uint8_t direction, const uint8_t *input, uint32_t len, uint8_t *rdir) +{ + if (f->alproto_tc == ALPROTO_POP3) { + // POP traffic begins by same "USER" pattern as FTP + return ALPROTO_FAILED; + } + return ALPROTO_FTP; +} static int FTPRegisterPatternsForProtocolDetection(void) { @@ -963,8 +972,8 @@ static int FTPRegisterPatternsForProtocolDetection(void) IPPROTO_TCP, ALPROTO_FTP, "FEAT", 4, 0, STREAM_TOSERVER) < 0) { return -1; } - if (AppLayerProtoDetectPMRegisterPatternCI( - IPPROTO_TCP, ALPROTO_FTP, "USER ", 5, 0, STREAM_TOSERVER) < 0) { + if (AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP, ALPROTO_FTP, "USER ", 5, 0, + STREAM_TOSERVER, FTPUserProbingParser, 5, 5) < 0) { return -1; } if (AppLayerProtoDetectPMRegisterPatternCI( diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 7783c076b65b..a7513e6c2042 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -1789,6 +1789,18 @@ void AppLayerParserRegisterProtocolParsers(void) "imap"); } + /** POP3 */ + AppLayerProtoDetectRegisterProtocol(ALPROTO_POP3, "pop3"); + if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", "pop3")) { + if (AppLayerProtoDetectPMRegisterPatternCS( + IPPROTO_TCP, ALPROTO_POP3, "+OK ", 4, 0, STREAM_TOCLIENT) < 0) { + SCLogInfo("pop3 proto registration failure"); + exit(EXIT_FAILURE); + } + } else { + SCLogInfo("Protocol detection and parser disabled for pop3 protocol."); + } + ValidateParsers(); return; } diff --git a/src/app-layer-protos.c b/src/app-layer-protos.c index 368efacd88d7..e71b0e8b2937 100644 --- a/src/app-layer-protos.c +++ b/src/app-layer-protos.c @@ -64,6 +64,7 @@ const AppProtoStringTuple AppProtoStrings[ALPROTO_MAX] = { { ALPROTO_RDP, "rdp" }, { ALPROTO_HTTP2, "http2" }, { ALPROTO_BITTORRENT_DHT, "bittorrent-dht" }, + { ALPROTO_POP3, "pop3" }, { ALPROTO_HTTP, "http" }, { ALPROTO_FAILED, "failed" }, #ifdef UNITTESTS diff --git a/src/app-layer-protos.h b/src/app-layer-protos.h index dd372550cbf5..d0becafbce56 100644 --- a/src/app-layer-protos.h +++ b/src/app-layer-protos.h @@ -60,6 +60,7 @@ enum AppProtoEnum { ALPROTO_RDP, ALPROTO_HTTP2, ALPROTO_BITTORRENT_DHT, + ALPROTO_POP3, // signature-only (ie not seen in flow) // HTTP for any version (ALPROTO_HTTP1 (version 1) or ALPROTO_HTTP2) diff --git a/src/app-layer.c b/src/app-layer.c index 6d4ac2ea8f79..5da25742b8c7 100644 --- a/src/app-layer.c +++ b/src/app-layer.c @@ -514,6 +514,10 @@ static int TCPProtoDetect(ThreadVars *tv, if (r < 0) { goto parser_error; } + // If AppLayerParserParse disabled us (because of detection-only) + if (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) { + AppLayerIncFlowCounter(tv, f); + } } else { /* 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 @@ -779,6 +783,9 @@ int AppLayerHandleTCPData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, if (r != 1) { StreamTcpUpdateAppLayerProgress(ssn, direction, data_len); if (r < 0) { + if (f->alproto_tc == ALPROTO_UNKNOWN || f->alproto_ts == ALPROTO_UNKNOWN) { + AppLayerIncFlowCounter(tv, f); + } ExceptionPolicyApply( p, g_applayerparser_error_policy, PKT_DROP_REASON_APPLAYER_ERROR); SCReturnInt(-1); diff --git a/suricata.yaml.in b/suricata.yaml.in index 947a68268228..dc044e183d97 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -963,6 +963,8 @@ app-layer: content-inspect-window: 4096 imap: enabled: detection-only + pop3: + enabled: detection-only smb: enabled: yes detection-ports: