diff --git a/doc/userguide/rules/email-keywords.rst b/doc/userguide/rules/email-keywords.rst index 206c3837e6..766b87bf4d 100644 --- a/doc/userguide/rules/email-keywords.rst +++ b/doc/userguide/rules/email-keywords.rst @@ -222,3 +222,29 @@ Example of a signature that would alert if a packet contains the MIME field ``re .. container:: example-rule alert smtp any any -> any any (msg:"Test mime email received"; :example-rule-emphasis:`email.received; content:"from [65.201.218.30] (helo=COZOXORY.club)by 173-66-46-112.wash.fios.verizon.net with esmtpa (Exim 4.86)(envelope-from )id 71cF63a9for mirjam@abrakadabra.ch\; Mon, 29 Jul 2019 17:01:45 +0000";` sid:1;) + +email.body_md5 +-------------- + +Matches the ``md5`` hash generated from an email body. +This keyword only works if config option +``app-layer.protocols.smtp.mime.body-md5`` is enabled or auto. +It should be used with ``requires: keyword email.body_md5;`` + +Syntax:: + + email.body_md5; content:""; + +``email.body_md5`` is a 'sticky buffer' and can be used as a ``fast_pattern``. + +This keyword maps to the EVE field ``email.body_md5``. + +Example +^^^^^^^ + +Example of a signature that would alert if the hash ``ed00c81b85fa455d60e19f1230977134`` +is generated from an email body: + +.. container:: example-rule + + alert smtp any any -> any any (msg:"Test mime email body_md5"; :example-rule-emphasis:`requires: keyword email.body_md5; email.body_md5; content:"ed00c81b85fa455d60e19f1230977134";` sid:1;) diff --git a/rust/src/mime/detect.rs b/rust/src/mime/detect.rs index fb9b6d08cf..b1a36ce6a0 100644 --- a/rust/src/mime/detect.rs +++ b/rust/src/mime/detect.rs @@ -93,3 +93,12 @@ pub unsafe extern "C" fn SCDetectMimeEmailGetDataArray( } return 0; } + +#[no_mangle] +pub unsafe extern "C" fn SCDetectMimeEmailGetBodyMd5( + ctx: &MimeStateSMTP, buffer: *mut *const u8, buffer_len: *mut u32, +) { + let hash = &ctx.md5_result; + *buffer = hash.as_ptr(); + *buffer_len = hash.len() as u32; +} diff --git a/rust/src/mime/smtp.rs b/rust/src/mime/smtp.rs index 5e7e478e9b..2cabf8d922 100644 --- a/rust/src/mime/smtp.rs +++ b/rust/src/mime/smtp.rs @@ -375,7 +375,7 @@ fn mime_smtp_parse_line( let mut warnings = 0; match ctx.state_flag { MimeSmtpParserState::MimeSmtpStart => { - if unsafe { MIME_SMTP_CONFIG_BODY_MD5 } + if unsafe { MIME_SMTP_CONFIG_ENABLE_BODY_MD5 } && ctx.md5_state != MimeSmtpMd5State::MimeSmtpMd5Started { ctx.md5 = Md5::new(); @@ -710,7 +710,8 @@ pub unsafe extern "C" fn SCMimeSmtpGetHeaderName( static mut MIME_SMTP_CONFIG_DECODE_BASE64: bool = true; static mut MIME_SMTP_CONFIG_DECODE_QUOTED: bool = true; -static mut MIME_SMTP_CONFIG_BODY_MD5: bool = false; +static mut MIME_SMTP_CONFIG_ENABLE_BODY_MD5: bool = false; +static mut MIME_SMTP_CONFIG_DISABLE_BODY_MD5: bool = false; static mut MIME_SMTP_CONFIG_HEADER_VALUE_DEPTH: u32 = 0; static mut MIME_SMTP_CONFIG_EXTRACT_URLS: bool = true; static mut MIME_SMTP_CONFIG_LOG_URL_SCHEME: bool = false; @@ -737,8 +738,22 @@ pub unsafe extern "C" fn SCMimeSmtpConfigLogUrlScheme(val: std::os::raw::c_int) } #[no_mangle] -pub unsafe extern "C" fn SCMimeSmtpConfigBodyMd5(val: std::os::raw::c_int) { - MIME_SMTP_CONFIG_BODY_MD5 = val != 0; +pub unsafe extern "C" fn SCMimeSmtpConfigBodyMd5(val: bool) { + if val { + MIME_SMTP_CONFIG_ENABLE_BODY_MD5 = true; + } else { + MIME_SMTP_CONFIG_DISABLE_BODY_MD5 = true; + } +} + +#[no_mangle] +pub unsafe extern "C" fn MimeBodyMd5IsEnabled() -> bool { + MIME_SMTP_CONFIG_ENABLE_BODY_MD5 +} + +#[no_mangle] +pub unsafe extern "C" fn MimeBodyMd5IsDisabled() -> bool { + MIME_SMTP_CONFIG_DISABLE_BODY_MD5 } #[no_mangle] diff --git a/src/app-layer-smtp.c b/src/app-layer-smtp.c index a7b76ec442..e7808e5ef2 100644 --- a/src/app-layer-smtp.c +++ b/src/app-layer-smtp.c @@ -391,9 +391,16 @@ static void SMTPConfigure(void) { SCMimeSmtpConfigLogUrlScheme(val); } - ret = SCConfGetChildValueBool(config, "body-md5", &val); - if (ret) { - SCMimeSmtpConfigBodyMd5(val); + // default (if value is absent) is auto : do not set anything + const char *strval; + if (SCConfGetChildValue(config, "body-md5", &strval) == 1) { + if (SCConfValIsFalse(strval)) { + SCMimeSmtpConfigBodyMd5(false); + } else if (SCConfValIsTrue(strval)) { + SCMimeSmtpConfigBodyMd5(true); + } else if (strcmp(strval, "auto") != 0) { + SCLogWarning("Unknown value for body-md5: %s", strval); + } } } diff --git a/src/detect-email.c b/src/detect-email.c index 26bd4974ce..4fd7e1db79 100644 --- a/src/detect-email.c +++ b/src/detect-email.c @@ -33,6 +33,7 @@ static int g_mime_email_message_id_buffer_id = 0; static int g_mime_email_x_mailer_buffer_id = 0; static int g_mime_email_url_buffer_id = 0; static int g_mime_email_received_buffer_id = 0; +static int g_mime_email_body_md5_buffer_id = 0; static int DetectMimeEmailFromSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) { @@ -225,6 +226,36 @@ static bool GetMimeEmailReceivedData(DetectEngineThreadCtx *det_ctx, const void return true; } +int DETECT_EMAIL_BODY_MD5 = 0; + +static int DetectMimeEmailBodyMd5Setup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) +{ + if (SCDetectBufferSetActiveList(de_ctx, s, g_mime_email_body_md5_buffer_id) < 0) + return -1; + + if (SCDetectSignatureSetAppProto(s, ALPROTO_SMTP) < 0) + return -1; + + if (!MimeBodyMd5IsEnabled()) { + // we registered the keyword if not explicitly disabled, so we are here in auto mode + SCMimeSmtpConfigBodyMd5(true); + } + + return 0; +} + +static bool GetMimeEmailBodyMd5Data( + const void *txv, const uint8_t _flow_flags, const uint8_t **data, uint32_t *data_len) +{ + SMTPTransaction *tx = (SMTPTransaction *)txv; + if (tx->mime_state == NULL) + return false; + + SCDetectMimeEmailGetBodyMd5(tx->mime_state, data, data_len); + + return true; +} + void DetectEmailRegister(void) { SCSigTableAppLiteElmt kw = { 0 }; @@ -309,4 +340,19 @@ void DetectEmailRegister(void) SCDetectHelperKeywordRegister(&kw); g_mime_email_received_buffer_id = SCDetectHelperMultiBufferMpmRegister("email.received", "MIME EMAIL RECEIVED", ALPROTO_SMTP, STREAM_TOSERVER, GetMimeEmailReceivedData); + + if (!MimeBodyMd5IsDisabled()) { + // do not register the keyword if explicitly disabled + kw.name = "email.body_md5"; + kw.desc = "'md5' hash generated from an email body"; + kw.url = "/rules/email-keywords.html#email.body_md5"; + kw.Setup = DetectMimeEmailBodyMd5Setup; + kw.flags = SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER; + DETECT_EMAIL_BODY_MD5 = SCDetectHelperKeywordRegister(&kw); + // We do not need a progress because SMTP tx has only progress 0 or 1 + // even if we have a MimeSmtpMd5State enumeration + g_mime_email_body_md5_buffer_id = SCDetectHelperBufferMpmRegister("email.body_md5", + "MIME EMAIL BODY MD5", ALPROTO_SMTP, STREAM_TOSERVER, GetMimeEmailBodyMd5Data); + DetectBufferTypeRegisterValidateCallback("email.body_md5", DetectMd5ValidateCallback); + } } diff --git a/suricata.yaml.in b/suricata.yaml.in index 9b96a59ea3..4e861f9a9f 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -1039,7 +1039,9 @@ app-layer: #log-url-scheme: yes # Set to yes to compute the md5 of the mail body. You will then # be able to journalize it. - body-md5: no + # Set it to no to disable it. + # Default is auto: not enabled until a rule needs it + # body-md5: auto # Configure inspected-tracker for file_data keyword inspected-tracker: content-limit: 100000