mirror of https://github.com/OISF/suricata
imap: implement parser
This introduces a parser for IMAP protocol, replacing the
detection-only implementation.
An IMAP transaction has two states driven by the 'complete' field:
- Open (complete = false): waiting for a matching tagged response.
- Complete (complete = true): tagged response received, or special
conditions met (BYE, server greeting).
Completion logic (is_complete):
- If a tagged request exists: requires a matching tagged response.
- No tagged request + BYE: complete (server closing).
- No tagged request + any response: complete (server greeting).
Transactions are created in three places:
1. Request parser: every parsed command creates a new tx.
2. Response parser (tagged, no matching request): midstream/async;
orphan tagged response gets its own tx.
3. Response parser (untagged, no incomplete tx): server greeting or
unsolicited response.
Once open, messages accumulate:
- Request side: continuation and literal data attach to the most
recent incomplete tx.
- Response side: untagged responses attach to the most recent
incomplete tx; tagged responses attach via find_request(tag).
Literals, sent by the client (e.g. APPEND) or by the server (e.g.
FETCH), are consumed incrementally and their content is not retained.
Hard limits prevent unbounded growth:
- IMAP_MAX_TX (256, configurable): total transactions. Exceeded:
all incomplete txs force-completed with TooManyTransactions event.
- IMAP_MAX_MSGS_PER_TX (512): requests per tx. Exceeded: request
dropped, DataLimitReached event.
- IMAP_MAX_LINES (512): response lines per tx. Exceeded: line
dropped, DataLimitReached event.
- IMAP_MAX_LINE_BYTES_PER_STATE (100MB): retained request and
response data per flow. Exceeded: data dropped, DataLimitReached
event.
- IMAP_MAX_LINE_SIZE (8KB): request/response line max length.
Exceeded: LineTooLong event.
- Literal size: bounded by declared size in {N} specifier.
IMAP_MAX_TX only is configurable limit via app-layer.protocols.imap.max-tx.
Ticket #8276
pull/16515/head
parent
67f2f243f7
commit
1d7da06d9f
@ -0,0 +1,8 @@
|
||||
# IMAP app layer event rules
|
||||
#
|
||||
# SID's fall in the 2244000+ range. See rules/README.md
|
||||
|
||||
alert imap any any -> any any (msg:"SURICATA IMAP too many transactions"; app-layer-event:imap.too_many_transactions; classtype:protocol-command-decode; sid:2244000; rev:1;)
|
||||
alert imap any any -> any any (msg:"SURICATA IMAP invalid data"; app-layer-event:imap.invalid_data; classtype:protocol-command-decode; sid:2244001; rev:1;)
|
||||
alert imap any any -> any any (msg:"SURICATA IMAP line too long"; app-layer-event:imap.line_too_long; classtype:protocol-command-decode; sid:2244002; rev:1;)
|
||||
alert imap any any -> any any (msg:"SURICATA IMAP data limit reached"; app-layer-event:imap.data_limit_reached; classtype:protocol-command-decode; sid:2244003; rev:1;)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,133 +0,0 @@
|
||||
/* Copyright (C) 2024 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Mahmoud Maatuq <mahmoudmatook.mm@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "app-layer-detect-proto.h"
|
||||
#include "app-layer-imap.h"
|
||||
#include "app-layer-protos.h"
|
||||
#include "rust.h"
|
||||
#include "stream-tcp-private.h"
|
||||
#include "stream-tcp-reassemble.h"
|
||||
#include "util-debug.h"
|
||||
|
||||
static AppProto ImapClientProbingParser(
|
||||
const Flow *f, uint8_t direction, const uint8_t *input, uint32_t len, uint8_t *rdir)
|
||||
{
|
||||
// This is called after " CAPABILITY" pattern has been found
|
||||
if (f->alproto_tc == ALPROTO_IMAP) {
|
||||
// server was already recognised as IMAP, so ok
|
||||
return ALPROTO_IMAP;
|
||||
}
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (input[i] >= '0' && input[i] <= '9') {
|
||||
// We check if the tag at the beginning of the input contains a number
|
||||
return ALPROTO_IMAP;
|
||||
}
|
||||
if (input[i] == ' ') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TcpSession *ssn = (TcpSession *)f->protoctx;
|
||||
if (ssn == NULL) {
|
||||
return ALPROTO_FAILED;
|
||||
}
|
||||
const uint32_t size_tc = StreamDataAvailableForProtoDetect(&ssn->server);
|
||||
|
||||
if (size_tc < 8 && f->alproto_tc == ALPROTO_UNKNOWN) {
|
||||
// wait for another packet from server
|
||||
return ALPROTO_UNKNOWN;
|
||||
}
|
||||
return ALPROTO_FAILED;
|
||||
}
|
||||
|
||||
static int IMAPRegisterPatternsForProtocolDetection(void)
|
||||
{
|
||||
if (SCAppLayerProtoDetectPMRegisterPatternCI(
|
||||
IPPROTO_TCP, ALPROTO_IMAP, "* OK ", 5, 0, STREAM_TOCLIENT) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SCAppLayerProtoDetectPMRegisterPatternCI(
|
||||
IPPROTO_TCP, ALPROTO_IMAP, "* NO ", 5, 0, STREAM_TOCLIENT) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SCAppLayerProtoDetectPMRegisterPatternCI(
|
||||
IPPROTO_TCP, ALPROTO_IMAP, "* BAD ", 6, 0, STREAM_TOCLIENT) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SCAppLayerProtoDetectPMRegisterPatternCI(
|
||||
IPPROTO_TCP, ALPROTO_IMAP, "* LIST ", 7, 0, STREAM_TOCLIENT) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SCAppLayerProtoDetectPMRegisterPatternCI(
|
||||
IPPROTO_TCP, ALPROTO_IMAP, "* ESEARCH ", 10, 0, STREAM_TOCLIENT) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SCAppLayerProtoDetectPMRegisterPatternCI(
|
||||
IPPROTO_TCP, ALPROTO_IMAP, "* STATUS ", 9, 0, STREAM_TOCLIENT) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SCAppLayerProtoDetectPMRegisterPatternCI(
|
||||
IPPROTO_TCP, ALPROTO_IMAP, "* FLAGS ", 8, 0, STREAM_TOCLIENT) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* there is no official document that limits the length of the tag
|
||||
* some practical implementations limit it to 20 characters
|
||||
* but keeping depth equal to 31 fails unit tests such AppLayerTest10
|
||||
* so keeping depth 17 for now to pass unit tests, that might miss some detections
|
||||
* until we find a better solution for the unit tests.
|
||||
*
|
||||
* AppLayerTest10 fails because it expects protocol detection to be completed with only 17 bytes
|
||||
* as input, and with this new pattern, we would need more bytes to finish protocol detection.
|
||||
*/
|
||||
if (SCAppLayerProtoDetectPMRegisterPatternCIwPP(IPPROTO_TCP, ALPROTO_IMAP, " CAPABILITY",
|
||||
17 /*6 for max tag len + space + len(CAPABILITY)*/, 0, STREAM_TOSERVER,
|
||||
ImapClientProbingParser, 12, 17) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RegisterIMAPParsers(void)
|
||||
{
|
||||
const char *proto_name = "imap";
|
||||
|
||||
if (SCAppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
|
||||
SCLogDebug("IMAP protocol detection is enabled.");
|
||||
AppLayerProtoDetectRegisterProtocol(ALPROTO_IMAP, proto_name);
|
||||
if (IMAPRegisterPatternsForProtocolDetection() < 0)
|
||||
SCLogError("Failed to register IMAP protocol detection patterns.");
|
||||
} else {
|
||||
SCLogDebug("Protocol detector and parser disabled for IMAP.");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue