suricata
detect-email.c
Go to the documentation of this file.
1 /* Copyright (C) 2025 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 #include "detect-engine.h"
19 #include "detect-engine-helper.h"
20 #include "detect-parse.h"
21 #include "app-layer-smtp.h"
22 #include "detect-email.h"
23 #include "rust.h"
25 
26 static int g_mime_email_from_buffer_id = 0;
27 static int g_mime_email_subject_buffer_id = 0;
28 static int g_mime_email_to_buffer_id = 0;
29 static int g_mime_email_cc_buffer_id = 0;
30 static int g_mime_email_date_buffer_id = 0;
31 static int g_mime_email_message_id_buffer_id = 0;
32 static int g_mime_email_x_mailer_buffer_id = 0;
33 static int g_mime_email_url_buffer_id = 0;
34 static int g_mime_email_received_buffer_id = 0;
35 
36 static int DetectMimeEmailFromSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
37 {
38  if (DetectBufferSetActiveList(de_ctx, s, g_mime_email_from_buffer_id) < 0)
39  return -1;
40 
42  return -1;
43 
44  return 0;
45 }
46 
47 static InspectionBuffer *GetMimeEmailFromData(DetectEngineThreadCtx *det_ctx,
48  const DetectEngineTransforms *transforms, Flow *f, const uint8_t _flow_flags, void *txv,
49  const int list_id)
50 {
51  InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
52  if (buffer->inspect == NULL) {
53  SMTPTransaction *tx = (SMTPTransaction *)txv;
54 
55  const uint8_t *b_email_from = NULL;
56  uint32_t b_email_from_len = 0;
57 
58  if (tx->mime_state == NULL)
59  return NULL;
60 
61  if (SCDetectMimeEmailGetData(tx->mime_state, &b_email_from, &b_email_from_len, "from") != 1)
62  return NULL;
63 
64  InspectionBufferSetup(det_ctx, list_id, buffer, b_email_from, b_email_from_len);
65  InspectionBufferApplyTransforms(det_ctx, buffer, transforms);
66  }
67  return buffer;
68 }
69 
70 static int DetectMimeEmailSubjectSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
71 {
72  if (DetectBufferSetActiveList(de_ctx, s, g_mime_email_subject_buffer_id) < 0)
73  return -1;
74 
76  return -1;
77 
78  return 0;
79 }
80 
81 static InspectionBuffer *GetMimeEmailSubjectData(DetectEngineThreadCtx *det_ctx,
82  const DetectEngineTransforms *transforms, Flow *f, const uint8_t _flow_flags, void *txv,
83  const int list_id)
84 {
85  InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
86  if (buffer->inspect == NULL) {
87  SMTPTransaction *tx = (SMTPTransaction *)txv;
88 
89  const uint8_t *b_email_sub = NULL;
90  uint32_t b_email_sub_len = 0;
91 
92  if (tx->mime_state == NULL)
93  return NULL;
94 
95  if (SCDetectMimeEmailGetData(tx->mime_state, &b_email_sub, &b_email_sub_len, "subject") !=
96  1)
97  return NULL;
98 
99  InspectionBufferSetup(det_ctx, list_id, buffer, b_email_sub, b_email_sub_len);
100  InspectionBufferApplyTransforms(det_ctx, buffer, transforms);
101  }
102  return buffer;
103 }
104 
105 static int DetectMimeEmailToSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
106 {
107  if (DetectBufferSetActiveList(de_ctx, s, g_mime_email_to_buffer_id) < 0)
108  return -1;
109 
111  return -1;
112 
113  return 0;
114 }
115 
116 static InspectionBuffer *GetMimeEmailToData(DetectEngineThreadCtx *det_ctx,
117  const DetectEngineTransforms *transforms, Flow *f, const uint8_t _flow_flags, void *txv,
118  const int list_id)
119 {
120  InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
121  if (buffer->inspect == NULL) {
122  SMTPTransaction *tx = (SMTPTransaction *)txv;
123 
124  const uint8_t *b_email_to = NULL;
125  uint32_t b_email_to_len = 0;
126 
127  if ((tx->mime_state != NULL)) {
128  if (SCDetectMimeEmailGetData(tx->mime_state, &b_email_to, &b_email_to_len, "to") != 1)
129  return NULL;
130  }
131 
132  if (b_email_to == NULL || b_email_to_len == 0)
133  return NULL;
134 
135  InspectionBufferSetup(det_ctx, list_id, buffer, b_email_to, b_email_to_len);
136  InspectionBufferApplyTransforms(det_ctx, buffer, transforms);
137  }
138  return buffer;
139 }
140 
141 static int DetectMimeEmailCcSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
142 {
143  if (DetectBufferSetActiveList(de_ctx, s, g_mime_email_cc_buffer_id) < 0)
144  return -1;
145 
147  return -1;
148 
149  return 0;
150 }
151 
152 static InspectionBuffer *GetMimeEmailCcData(DetectEngineThreadCtx *det_ctx,
153  const DetectEngineTransforms *transforms, Flow *f, const uint8_t _flow_flags, void *txv,
154  const int list_id)
155 {
156  InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
157  if (buffer->inspect == NULL) {
158  SMTPTransaction *tx = (SMTPTransaction *)txv;
159 
160  const uint8_t *b_email_cc = NULL;
161  uint32_t b_email_cc_len = 0;
162 
163  if (tx->mime_state == NULL)
164  return NULL;
165 
166  if (SCDetectMimeEmailGetData(tx->mime_state, &b_email_cc, &b_email_cc_len, "cc") != 1)
167  return NULL;
168 
169  InspectionBufferSetup(det_ctx, list_id, buffer, b_email_cc, b_email_cc_len);
170  InspectionBufferApplyTransforms(det_ctx, buffer, transforms);
171  }
172  return buffer;
173 }
174 
175 static int DetectMimeEmailDateSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
176 {
177  if (DetectBufferSetActiveList(de_ctx, s, g_mime_email_date_buffer_id) < 0)
178  return -1;
179 
181  return -1;
182 
183  return 0;
184 }
185 
186 static InspectionBuffer *GetMimeEmailDateData(DetectEngineThreadCtx *det_ctx,
187  const DetectEngineTransforms *transforms, Flow *f, const uint8_t _flow_flags, void *txv,
188  const int list_id)
189 {
190  InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
191  if (buffer->inspect == NULL) {
192  SMTPTransaction *tx = (SMTPTransaction *)txv;
193 
194  const uint8_t *b_email_date = NULL;
195  uint32_t b_email_date_len = 0;
196 
197  if (tx->mime_state == NULL)
198  return NULL;
199 
200  if (SCDetectMimeEmailGetData(tx->mime_state, &b_email_date, &b_email_date_len, "date") != 1)
201  return NULL;
202 
203  InspectionBufferSetup(det_ctx, list_id, buffer, b_email_date, b_email_date_len);
204  InspectionBufferApplyTransforms(det_ctx, buffer, transforms);
205  }
206  return buffer;
207 }
208 
209 static int DetectMimeEmailMessageIdSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
210 {
211  if (DetectBufferSetActiveList(de_ctx, s, g_mime_email_message_id_buffer_id) < 0)
212  return -1;
213 
215  return -1;
216 
217  return 0;
218 }
219 
220 static InspectionBuffer *GetMimeEmailMessageIdData(DetectEngineThreadCtx *det_ctx,
221  const DetectEngineTransforms *transforms, Flow *f, const uint8_t _flow_flags, void *txv,
222  const int list_id)
223 {
224  InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
225  if (buffer->inspect == NULL) {
226  SMTPTransaction *tx = (SMTPTransaction *)txv;
227 
228  const uint8_t *b_email_msg_id = NULL;
229  uint32_t b_email_msg_id_len = 0;
230 
231  if (tx->mime_state == NULL)
232  return NULL;
233 
234  if (SCDetectMimeEmailGetData(
235  tx->mime_state, &b_email_msg_id, &b_email_msg_id_len, "message-id") != 1)
236  return NULL;
237 
238  InspectionBufferSetup(det_ctx, list_id, buffer, b_email_msg_id, b_email_msg_id_len);
239  InspectionBufferApplyTransforms(det_ctx, buffer, transforms);
240  }
241  return buffer;
242 }
243 
244 static int DetectMimeEmailXMailerSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
245 {
246  if (DetectBufferSetActiveList(de_ctx, s, g_mime_email_x_mailer_buffer_id) < 0)
247  return -1;
248 
250  return -1;
251 
252  return 0;
253 }
254 
255 static InspectionBuffer *GetMimeEmailXMailerData(DetectEngineThreadCtx *det_ctx,
256  const DetectEngineTransforms *transforms, Flow *f, const uint8_t _flow_flags, void *txv,
257  const int list_id)
258 {
259  InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
260  if (buffer->inspect == NULL) {
261  SMTPTransaction *tx = (SMTPTransaction *)txv;
262 
263  const uint8_t *b_email_x_mailer = NULL;
264  uint32_t b_email_x_mailer_len = 0;
265 
266  if (tx->mime_state == NULL)
267  return NULL;
268 
269  if (SCDetectMimeEmailGetData(
270  tx->mime_state, &b_email_x_mailer, &b_email_x_mailer_len, "x-mailer") != 1)
271  return NULL;
272 
273  InspectionBufferSetup(det_ctx, list_id, buffer, b_email_x_mailer, b_email_x_mailer_len);
274  InspectionBufferApplyTransforms(det_ctx, buffer, transforms);
275  }
276  return buffer;
277 }
278 
279 static int DetectMimeEmailUrlSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
280 {
281  if (DetectBufferSetActiveList(de_ctx, s, g_mime_email_url_buffer_id) < 0)
282  return -1;
283 
285  return -1;
286 
287  return 0;
288 }
289 
290 static InspectionBuffer *GetMimeEmailUrlData(DetectEngineThreadCtx *det_ctx,
291  const DetectEngineTransforms *transforms, Flow *f, const uint8_t _flow_flags, void *txv,
292  const int list_id, uint32_t idx)
293 {
294  InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, idx);
295  if (buffer == NULL || buffer->initialized)
296  return buffer;
297 
298  SMTPTransaction *tx = (SMTPTransaction *)txv;
299 
300  const uint8_t *b_email_url = NULL;
301  uint32_t b_email_url_len = 0;
302 
303  if (tx->mime_state == NULL) {
305  return NULL;
306  }
307 
308  if (SCDetectMimeEmailGetUrl(tx->mime_state, &b_email_url, &b_email_url_len, idx) != 1) {
310  return NULL;
311  }
312 
313  InspectionBufferSetupMulti(det_ctx, buffer, transforms, b_email_url, b_email_url_len);
314  buffer->flags = DETECT_CI_FLAGS_SINGLE;
315  return buffer;
316 }
317 
318 static int DetectMimeEmailReceivedSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
319 {
320  if (DetectBufferSetActiveList(de_ctx, s, g_mime_email_received_buffer_id) < 0)
321  return -1;
322 
324  return -1;
325 
326  return 0;
327 }
328 
329 static InspectionBuffer *GetMimeEmailReceivedData(DetectEngineThreadCtx *det_ctx,
330  const DetectEngineTransforms *transforms, Flow *f, const uint8_t _flow_flags, void *txv,
331  const int list_id, uint32_t idx)
332 {
333  InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, idx);
334  if (buffer == NULL || buffer->initialized)
335  return buffer;
336 
337  SMTPTransaction *tx = (SMTPTransaction *)txv;
338 
339  const uint8_t *b_email_received = NULL;
340  uint32_t b_email_received_len = 0;
341 
342  if (tx->mime_state == NULL) {
344  return NULL;
345  }
346 
347  if (SCDetectMimeEmailGetDataArray(
348  tx->mime_state, &b_email_received, &b_email_received_len, "received", idx) != 1) {
350  return NULL;
351  }
352 
353  InspectionBufferSetupMulti(det_ctx, buffer, transforms, b_email_received, b_email_received_len);
354  buffer->flags = DETECT_CI_FLAGS_SINGLE;
355  return buffer;
356 }
357 
359 {
360  SCSigTableElmt kw = { 0 };
361 
362  kw.name = "email.from";
363  kw.desc = "'From' field from an email";
364  kw.url = "/rules/email-keywords.html#email.from";
365  kw.Setup = (int (*)(void *, void *, const char *))DetectMimeEmailFromSetup;
368  g_mime_email_from_buffer_id =
369  DetectHelperBufferMpmRegister("email.from", "MIME EMAIL FROM", ALPROTO_SMTP, false,
370  true, // to server
371  GetMimeEmailFromData);
372 
373  kw.name = "email.subject";
374  kw.desc = "'Subject' field from an email";
375  kw.url = "/rules/email-keywords.html#email.subject";
376  kw.Setup = (int (*)(void *, void *, const char *))DetectMimeEmailSubjectSetup;
379  g_mime_email_subject_buffer_id = DetectHelperBufferMpmRegister("email.subject",
380  "MIME EMAIL SUBJECT", ALPROTO_SMTP, false,
381  true, // to server
382  GetMimeEmailSubjectData);
383 
384  kw.name = "email.to";
385  kw.desc = "'To' field from an email";
386  kw.url = "/rules/email-keywords.html#email.to";
387  kw.Setup = (int (*)(void *, void *, const char *))DetectMimeEmailToSetup;
390  g_mime_email_to_buffer_id =
391  DetectHelperBufferMpmRegister("email.to", "MIME EMAIL TO", ALPROTO_SMTP, false,
392  true, // to server
393  GetMimeEmailToData);
394 
395  kw.name = "email.cc";
396  kw.desc = "'Cc' field from an email";
397  kw.url = "/rules/email-keywords.html#email.cc";
398  kw.Setup = (int (*)(void *, void *, const char *))DetectMimeEmailCcSetup;
401  g_mime_email_cc_buffer_id =
402  DetectHelperBufferMpmRegister("email.cc", "MIME EMAIL CC", ALPROTO_SMTP, false,
403  true, // to server
404  GetMimeEmailCcData);
405 
406  kw.name = "email.date";
407  kw.desc = "'Date' field from an email";
408  kw.url = "/rules/email-keywords.html#email.date";
409  kw.Setup = (int (*)(void *, void *, const char *))DetectMimeEmailDateSetup;
412  g_mime_email_date_buffer_id =
413  DetectHelperBufferMpmRegister("email.date", "MIME EMAIL DATE", ALPROTO_SMTP, false,
414  true, // to server
415  GetMimeEmailDateData);
416 
417  kw.name = "email.message_id";
418  kw.desc = "'Message-Id' field from an email";
419  kw.url = "/rules/email-keywords.html#email.message_id";
420  kw.Setup = (int (*)(void *, void *, const char *))DetectMimeEmailMessageIdSetup;
423  g_mime_email_message_id_buffer_id = DetectHelperBufferMpmRegister("email.message_id",
424  "MIME EMAIL Message-Id", ALPROTO_SMTP, false,
425  true, // to server
426  GetMimeEmailMessageIdData);
427 
428  kw.name = "email.x_mailer";
429  kw.desc = "'X-Mailer' field from an email";
430  kw.url = "/rules/email-keywords.html#email.x_mailer";
431  kw.Setup = (int (*)(void *, void *, const char *))DetectMimeEmailXMailerSetup;
434  g_mime_email_x_mailer_buffer_id = DetectHelperBufferMpmRegister("email.x_mailer",
435  "MIME EMAIL X-Mailer", ALPROTO_SMTP, false,
436  true, // to server
437  GetMimeEmailXMailerData);
438 
439  kw.name = "email.url";
440  kw.desc = "'Url' extracted from an email";
441  kw.url = "/rules/email-keywords.html#email.url";
442  kw.Setup = (int (*)(void *, void *, const char *))DetectMimeEmailUrlSetup;
445  g_mime_email_url_buffer_id =
446  DetectHelperMultiBufferMpmRegister("email.url", "MIME EMAIL URL", ALPROTO_SMTP, false,
447  true, // to server
448  GetMimeEmailUrlData);
449 
450  kw.name = "email.received";
451  kw.desc = "'Received' field from an email";
452  kw.url = "/rules/email-keywords.html#email.received";
453  kw.Setup = (int (*)(void *, void *, const char *))DetectMimeEmailReceivedSetup;
456  g_mime_email_received_buffer_id = DetectHelperMultiBufferMpmRegister("email.received",
457  "MIME EMAIL RECEIVED", ALPROTO_SMTP, false,
458  true, // to server
459  GetMimeEmailReceivedData);
460 }
DetectEmailRegister
void DetectEmailRegister(void)
Definition: detect-email.c:358
DetectSignatureSetAppProto
int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
Definition: detect-parse.c:2218
detect-engine.h
SIGMATCH_INFO_STICKY_BUFFER
#define SIGMATCH_INFO_STICKY_BUFFER
Definition: detect.h:1616
detect-email.h
InspectionBuffer::initialized
bool initialized
Definition: detect.h:384
DetectEngineTransforms
Definition: detect.h:415
DetectBufferSetActiveList
int DetectBufferSetActiveList(DetectEngineCtx *de_ctx, Signature *s, const int list)
Definition: detect-engine.c:1422
InspectionBuffer
Definition: detect.h:380
Flow_
Flow data structure.
Definition: flow.h:356
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:920
rust.h
InspectionBuffer::flags
uint8_t flags
Definition: detect.h:385
DetectHelperBufferMpmRegister
int DetectHelperBufferMpmRegister(const char *name, const char *desc, AppProto alproto, bool toclient, bool toserver, InspectionBufferGetDataPtr GetData)
Definition: detect-engine-helper.c:64
InspectionBufferGet
InspectionBuffer * InspectionBufferGet(DetectEngineThreadCtx *det_ctx, const int list_id)
Definition: detect-engine.c:1578
InspectionBufferSetupMultiEmpty
void InspectionBufferSetupMultiEmpty(InspectionBuffer *buffer)
setup the buffer empty
Definition: detect-engine.c:1666
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:18
DetectEngineThreadCtx_
Definition: detect.h:1197
ALPROTO_SMTP
@ ALPROTO_SMTP
Definition: app-layer-protos.h:38
detect-engine-helper.h
SMTPTransaction_
Definition: app-layer-smtp.h:72
DetectHelperMultiBufferMpmRegister
int DetectHelperMultiBufferMpmRegister(const char *name, const char *desc, AppProto alproto, bool toclient, bool toserver, InspectionMultiBufferGetDataPtr GetData)
Definition: detect-engine-helper.c:83
detect-engine-content-inspection.h
DETECT_CI_FLAGS_SINGLE
#define DETECT_CI_FLAGS_SINGLE
Definition: detect-engine-content-inspection.h:49
DetectHelperKeywordRegister
int DetectHelperKeywordRegister(const SCSigTableElmt *kw)
Definition: detect-engine-helper.c:114
InspectionBufferApplyTransforms
void InspectionBufferApplyTransforms(DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, const DetectEngineTransforms *transforms)
Definition: detect-engine.c:1650
InspectionBuffer::inspect
const uint8_t * inspect
Definition: detect.h:381
InspectionBufferSetup
void InspectionBufferSetup(DetectEngineThreadCtx *det_ctx, const int list_id, InspectionBuffer *buffer, const uint8_t *data, const uint32_t data_len)
setup the buffer with our initial data
Definition: detect-engine.c:1712
detect-parse.h
Signature_
Signature container.
Definition: detect.h:669
InspectionBufferMultipleForListGet
InspectionBuffer * InspectionBufferMultipleForListGet(DetectEngineThreadCtx *det_ctx, const int list_id, const uint32_t local_id)
for a InspectionBufferMultipleForList get a InspectionBuffer
Definition: detect-engine.c:1598
SIGMATCH_NOOPT
#define SIGMATCH_NOOPT
Definition: detect.h:1592
app-layer-smtp.h
InspectionBufferSetupMulti
void InspectionBufferSetupMulti(DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, const DetectEngineTransforms *transforms, const uint8_t *data, const uint32_t data_len)
setup the buffer with our initial data
Definition: detect-engine.c:1679
SMTPTransaction_::mime_state
MimeStateSMTP * mime_state
Definition: app-layer-smtp.h:85