1 /* Copyright (C) 2007-2021 Open Information Security Foundation
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
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.
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
21 * \author Victor Julien <victor@inliniac.net>
25 #include "suricata-common.h"
31 #include "detect-parse.h"
33 #include "detect-engine.h"
34 #include "detect-engine-mpm.h"
35 #include "detect-engine-state.h"
36 #include "detect-engine-prefilter.h"
37 #include "detect-engine-content-inspection.h"
38 #include "detect-file-data.h"
40 #include "app-layer-parser.h"
41 #include "app-layer-htp.h"
42 #include "app-layer-smtp.h"
46 #include "flow-util.h"
48 #include "util-debug.h"
49 #include "util-spm-bm.h"
50 #include "util-unittest.h"
51 #include "util-unittest-helper.h"
52 #include "util-file-decompression.h"
54 static int DetectFiledataSetup (DetectEngineCtx
*, Signature
*, const char *);
56 static void DetectFiledataRegisterTests(void);
58 static void DetectFiledataSetupCallback(const DetectEngineCtx
*de_ctx
,
60 static int g_file_data_buffer_id
= 0;
62 static inline HtpBody
*GetResponseBody(htp_tx_t
*tx
);
65 static int PrefilterMpmHTTPFiledataRegister(DetectEngineCtx
*de_ctx
, SigGroupHead
*sgh
,
66 MpmCtx
*mpm_ctx
, const DetectBufferMpmRegistery
*mpm_reg
, int list_id
);
69 static int DetectEngineInspectFiledata(
70 DetectEngineCtx
*de_ctx
, DetectEngineThreadCtx
*det_ctx
,
71 const DetectEngineAppInspectionEngine
*engine
,
73 Flow
*f
, uint8_t flags
, void *alstate
, void *txv
, uint64_t tx_id
);
74 int PrefilterMpmFiledataRegister(DetectEngineCtx
*de_ctx
,
75 SigGroupHead
*sgh
, MpmCtx
*mpm_ctx
,
76 const DetectBufferMpmRegistery
*mpm_reg
, int list_id
);
78 static int DetectEngineInspectBufferHttpBody(DetectEngineCtx
*de_ctx
,
79 DetectEngineThreadCtx
*det_ctx
, const DetectEngineAppInspectionEngine
*engine
,
80 const Signature
*s
, Flow
*f
, uint8_t flags
, void *alstate
, void *txv
, uint64_t tx_id
);
83 * \brief Registration function for keyword: file_data
85 void DetectFiledataRegister(void)
87 sigmatch_table
[DETECT_FILE_DATA
].name
= "file.data";
88 sigmatch_table
[DETECT_FILE_DATA
].alias
= "file_data";
89 sigmatch_table
[DETECT_FILE_DATA
].desc
= "make content keywords match on file data";
90 sigmatch_table
[DETECT_FILE_DATA
].url
= "/rules/http-keywords.html#file-data";
91 sigmatch_table
[DETECT_FILE_DATA
].Setup
= DetectFiledataSetup
;
93 sigmatch_table
[DETECT_FILE_DATA
].RegisterTests
= DetectFiledataRegisterTests
;
95 sigmatch_table
[DETECT_FILE_DATA
].flags
= SIGMATCH_NOOPT
;
97 DetectAppLayerMpmRegister2("file_data", SIG_FLAG_TOSERVER
, 2,
98 PrefilterMpmFiledataRegister
, NULL
,
100 DetectAppLayerMpmRegister2("file_data", SIG_FLAG_TOCLIENT
, 2, PrefilterMpmHTTPFiledataRegister
,
101 NULL
, ALPROTO_HTTP1
, HTP_RESPONSE_BODY
);
102 DetectAppLayerMpmRegister2("file_data", SIG_FLAG_TOSERVER
, 2,
103 PrefilterMpmFiledataRegister
, NULL
,
105 DetectAppLayerMpmRegister2("file_data", SIG_FLAG_TOCLIENT
, 2,
106 PrefilterMpmFiledataRegister
, NULL
,
108 DetectAppLayerMpmRegister2("file_data", SIG_FLAG_TOSERVER
, 2,
109 PrefilterMpmFiledataRegister
, NULL
,
110 ALPROTO_HTTP2
, HTTP2StateDataClient
);
111 DetectAppLayerMpmRegister2("file_data", SIG_FLAG_TOCLIENT
, 2,
112 PrefilterMpmFiledataRegister
, NULL
,
113 ALPROTO_HTTP2
, HTTP2StateDataServer
);
114 DetectAppLayerMpmRegister2("file_data", SIG_FLAG_TOSERVER
, 2, PrefilterMpmFiledataRegister
,
115 NULL
, ALPROTO_FTPDATA
, 0);
116 DetectAppLayerMpmRegister2("file_data", SIG_FLAG_TOCLIENT
, 2, PrefilterMpmFiledataRegister
,
117 NULL
, ALPROTO_FTPDATA
, 0);
118 DetectAppLayerMpmRegister2(
119 "file_data", SIG_FLAG_TOSERVER
, 2, PrefilterMpmFiledataRegister
, NULL
, ALPROTO_FTP
, 0);
120 DetectAppLayerMpmRegister2(
121 "file_data", SIG_FLAG_TOCLIENT
, 2, PrefilterMpmFiledataRegister
, NULL
, ALPROTO_FTP
, 0);
123 DetectAppLayerInspectEngineRegister2("file_data", ALPROTO_HTTP1
, SIG_FLAG_TOCLIENT
,
124 HTP_RESPONSE_BODY
, DetectEngineInspectBufferHttpBody
, NULL
);
125 DetectAppLayerInspectEngineRegister2("file_data",
126 ALPROTO_SMTP
, SIG_FLAG_TOSERVER
, 0,
127 DetectEngineInspectFiledata
, NULL
);
128 DetectBufferTypeRegisterSetupCallback("file_data",
129 DetectFiledataSetupCallback
);
130 DetectAppLayerInspectEngineRegister2("file_data",
131 ALPROTO_SMB
, SIG_FLAG_TOSERVER
, 0,
132 DetectEngineInspectFiledata
, NULL
);
133 DetectAppLayerInspectEngineRegister2("file_data",
134 ALPROTO_SMB
, SIG_FLAG_TOCLIENT
, 0,
135 DetectEngineInspectFiledata
, NULL
);
136 DetectAppLayerInspectEngineRegister2("file_data",
137 ALPROTO_HTTP2
, SIG_FLAG_TOSERVER
, HTTP2StateDataClient
,
138 DetectEngineInspectFiledata
, NULL
);
139 DetectAppLayerInspectEngineRegister2("file_data",
140 ALPROTO_HTTP2
, SIG_FLAG_TOCLIENT
, HTTP2StateDataServer
,
141 DetectEngineInspectFiledata
, NULL
);
142 DetectAppLayerInspectEngineRegister2(
143 "file_data", ALPROTO_FTPDATA
, SIG_FLAG_TOSERVER
, 0, DetectEngineInspectFiledata
, NULL
);
144 DetectAppLayerInspectEngineRegister2(
145 "file_data", ALPROTO_FTPDATA
, SIG_FLAG_TOCLIENT
, 0, DetectEngineInspectFiledata
, NULL
);
146 DetectAppLayerInspectEngineRegister2(
147 "file_data", ALPROTO_FTP
, SIG_FLAG_TOSERVER
, 0, DetectEngineInspectFiledata
, NULL
);
148 DetectAppLayerInspectEngineRegister2(
149 "file_data", ALPROTO_FTP
, SIG_FLAG_TOCLIENT
, 0, DetectEngineInspectFiledata
, NULL
);
151 DetectBufferTypeSetDescriptionByName("file_data",
152 "http response body, smb files or smtp attachments data");
154 g_file_data_buffer_id
= DetectBufferTypeGetByName("file_data");
157 #define FILEDATA_CONTENT_LIMIT 100000
158 #define FILEDATA_CONTENT_INSPECT_MIN_SIZE 32768
159 #define FILEDATA_CONTENT_INSPECT_WINDOW 4096
161 static void SetupDetectEngineConfig(DetectEngineCtx
*de_ctx
) {
162 if (de_ctx
->filedata_config_initialized
)
165 /* initialize default */
166 for (int i
= 0; i
< (int)ALPROTO_MAX
; i
++) {
167 de_ctx
->filedata_config
[i
].content_limit
= FILEDATA_CONTENT_LIMIT
;
168 de_ctx
->filedata_config
[i
].content_inspect_min_size
= FILEDATA_CONTENT_INSPECT_MIN_SIZE
;
169 de_ctx
->filedata_config
[i
].content_inspect_window
= FILEDATA_CONTENT_INSPECT_WINDOW
;
172 /* add protocol specific settings here */
175 de_ctx
->filedata_config
[ALPROTO_SMTP
].content_limit
= smtp_config
.content_limit
;
176 de_ctx
->filedata_config
[ALPROTO_SMTP
].content_inspect_min_size
= smtp_config
.content_inspect_min_size
;
177 de_ctx
->filedata_config
[ALPROTO_SMTP
].content_inspect_window
= smtp_config
.content_inspect_window
;
179 de_ctx
->filedata_config_initialized
= true;
183 * \brief this function is used to parse filedata options
184 * \brief into the current signature
186 * \param de_ctx pointer to the Detection Engine Context
187 * \param s pointer to the Current Signature
188 * \param str pointer to the user provided "filestore" option
190 * \retval 0 on Success
191 * \retval -1 on Failure
193 static int DetectFiledataSetup (DetectEngineCtx
*de_ctx
, Signature
*s
, const char *str
)
197 if (!DetectProtoContainsProto(&s
->proto
, IPPROTO_TCP
) ||
198 (s
->alproto
!= ALPROTO_UNKNOWN
&& s
->alproto
!= ALPROTO_HTTP1
&&
199 s
->alproto
!= ALPROTO_SMTP
&& s
->alproto
!= ALPROTO_SMB
&&
200 s
->alproto
!= ALPROTO_HTTP2
&& s
->alproto
!= ALPROTO_FTP
&&
201 s
->alproto
!= ALPROTO_FTPDATA
&& s
->alproto
!= ALPROTO_HTTP
)) {
202 SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS
, "rule contains conflicting keywords.");
206 if ((s
->alproto
== ALPROTO_HTTP1
|| s
->alproto
== ALPROTO_HTTP
) &&
207 (s
->init_data
->init_flags
& SIG_FLAG_INIT_FLOW
) && (s
->flags
& SIG_FLAG_TOSERVER
) &&
208 !(s
->flags
& SIG_FLAG_TOCLIENT
)) {
209 SCLogError(SC_ERR_INVALID_SIGNATURE
, "Can't use file_data with "
210 "flow:to_server or flow:from_client with http.");
214 if (s
->alproto
== ALPROTO_SMTP
&& (s
->init_data
->init_flags
& SIG_FLAG_INIT_FLOW
) &&
215 !(s
->flags
& SIG_FLAG_TOSERVER
) && (s
->flags
& SIG_FLAG_TOCLIENT
)) {
216 SCLogError(SC_ERR_INVALID_SIGNATURE
, "Can't use file_data with "
217 "flow:to_client or flow:from_server with smtp.");
221 if (DetectBufferSetActiveList(s
, DetectBufferTypeGetByName("file_data")) < 0)
224 s
->init_data
->init_flags
|= SIG_FLAG_INIT_FILEDATA
;
225 SetupDetectEngineConfig(de_ctx
);
229 static void DetectFiledataSetupCallback(const DetectEngineCtx
*de_ctx
,
232 if (s
->alproto
== ALPROTO_HTTP1
|| s
->alproto
== ALPROTO_UNKNOWN
||
233 s
->alproto
== ALPROTO_HTTP
) {
234 AppLayerHtpEnableResponseBodyCallback();
237 /* server body needs to be inspected in sync with stream if possible */
238 s
->init_data
->init_flags
|= SIG_FLAG_INIT_NEED_FLUSH
;
240 SCLogDebug("callback invoked by %u", s
->id
);
245 typedef struct PrefilterMpmFiledata
{
248 const MpmCtx
*mpm_ctx
;
249 const DetectEngineTransforms
*transforms
;
250 } PrefilterMpmFiledata
;
252 static void PrefilterMpmFiledataFree(void *ptr
)
257 /* HTTP based detection */
259 static inline HtpBody
*GetResponseBody(htp_tx_t
*tx
)
261 HtpTxUserData
*htud
= (HtpTxUserData
*)htp_tx_get_user_data(tx
);
263 SCLogDebug("no htud");
267 return &htud
->response_body
;
270 static inline InspectionBuffer
*HttpServerBodyXformsGetDataCallback(DetectEngineThreadCtx
*det_ctx
,
271 const DetectEngineTransforms
*transforms
, const int list_id
, InspectionBuffer
*base_buffer
)
273 InspectionBuffer
*buffer
= InspectionBufferGet(det_ctx
, list_id
);
274 if (buffer
->inspect
!= NULL
)
277 InspectionBufferSetup(det_ctx
, list_id
, buffer
, base_buffer
->inspect
, base_buffer
->inspect_len
);
278 buffer
->inspect_offset
= base_buffer
->inspect_offset
;
279 InspectionBufferApplyTransforms(buffer
, transforms
);
280 SCLogDebug("xformed buffer %p size %u", buffer
, buffer
->inspect_len
);
281 SCReturnPtr(buffer
, "InspectionBuffer");
284 static InspectionBuffer
*HttpServerBodyGetDataCallback(DetectEngineThreadCtx
*det_ctx
,
285 const DetectEngineTransforms
*transforms
, Flow
*f
, const uint8_t flow_flags
, void *txv
,
286 const int list_id
, const int base_id
)
290 InspectionBuffer
*buffer
= InspectionBufferGet(det_ctx
, base_id
);
291 if (base_id
!= list_id
&& buffer
->inspect
!= NULL
)
292 return HttpServerBodyXformsGetDataCallback(det_ctx
, transforms
, list_id
, buffer
);
293 else if (buffer
->inspect
!= NULL
)
297 HtpState
*htp_state
= f
->alstate
;
298 const uint8_t flags
= flow_flags
;
300 HtpBody
*body
= GetResponseBody(tx
);
306 if (body
->body_inspected
== body
->content_len_so_far
) {
307 SCLogDebug("no new data");
311 HtpBodyChunk
*cur
= body
->first
;
313 SCLogDebug("No http chunks to inspect for this transaction");
317 SCLogDebug("response.body_limit %u response_body.content_len_so_far %" PRIu64
318 ", response.inspect_min_size %" PRIu32
", EOF %s, progress > body? %s",
319 htp_state
->cfg
->response
.body_limit
, body
->content_len_so_far
,
320 htp_state
->cfg
->response
.inspect_min_size
, flags
& STREAM_EOF
? "true" : "false",
321 (AppLayerParserGetStateProgress(IPPROTO_TCP
, ALPROTO_HTTP1
, tx
, flags
) >
326 if (!htp_state
->cfg
->http_body_inline
) {
327 /* inspect the body if the transfer is complete or we have hit
328 * our body size limit */
329 if ((htp_state
->cfg
->response
.body_limit
== 0 ||
330 body
->content_len_so_far
< htp_state
->cfg
->response
.body_limit
) &&
331 body
->content_len_so_far
< htp_state
->cfg
->response
.inspect_min_size
&&
332 !(AppLayerParserGetStateProgress(IPPROTO_TCP
, ALPROTO_HTTP1
, tx
, flags
) >
333 HTP_RESPONSE_BODY
) &&
334 !(flags
& STREAM_EOF
)) {
335 SCLogDebug("we still haven't seen the entire response body. "
336 "Let's defer body inspection till we see the "
342 /* get the inspect buffer
344 * make sure that we have at least the configured inspect_win size.
345 * If we have more, take at least 1/4 of the inspect win size before
349 if (body
->body_inspected
> htp_state
->cfg
->response
.inspect_min_size
) {
350 BUG_ON(body
->content_len_so_far
< body
->body_inspected
);
351 uint64_t inspect_win
= body
->content_len_so_far
- body
->body_inspected
;
352 SCLogDebug("inspect_win %"PRIu64
, inspect_win
);
353 if (inspect_win
< htp_state
->cfg
->response
.inspect_window
) {
354 uint64_t inspect_short
= htp_state
->cfg
->response
.inspect_window
- inspect_win
;
355 if (body
->body_inspected
< inspect_short
)
358 offset
= body
->body_inspected
- inspect_short
;
360 offset
= body
->body_inspected
- (htp_state
->cfg
->response
.inspect_window
/ 4);
367 StreamingBufferGetDataAtOffset(body
->sb
,
368 &data
, &data_len
, offset
);
369 InspectionBufferSetup(det_ctx
, base_id
, buffer
, data
, data_len
);
370 buffer
->inspect_offset
= offset
;
371 body
->body_inspected
= body
->content_len_so_far
;
372 SCLogDebug("body->body_inspected now: %" PRIu64
, body
->body_inspected
);
374 /* built-in 'transformation' */
375 if (htp_state
->cfg
->swf_decompression_enabled
) {
376 int swf_file_type
= FileIsSwfFile(data
, data_len
);
377 if (swf_file_type
== FILE_SWF_ZLIB_COMPRESSION
||
378 swf_file_type
== FILE_SWF_LZMA_COMPRESSION
)
380 (void)FileSwfDecompression(data
, data_len
,
383 htp_state
->cfg
->swf_compression_type
,
384 htp_state
->cfg
->swf_decompress_depth
,
385 htp_state
->cfg
->swf_compress_depth
);
389 if (base_id
!= list_id
) {
390 buffer
= HttpServerBodyXformsGetDataCallback(det_ctx
, transforms
, list_id
, buffer
);
392 SCReturnPtr(buffer
, "InspectionBuffer");
395 static int DetectEngineInspectBufferHttpBody(DetectEngineCtx
*de_ctx
,
396 DetectEngineThreadCtx
*det_ctx
, const DetectEngineAppInspectionEngine
*engine
,
397 const Signature
*s
, Flow
*f
, uint8_t flags
, void *alstate
, void *txv
, uint64_t tx_id
)
400 (AppLayerParserGetStateProgress(f
->proto
, f
->alproto
, txv
, flags
) > engine
->progress
);
401 const InspectionBuffer
*buffer
= HttpServerBodyGetDataCallback(
402 det_ctx
, engine
->v2
.transforms
, f
, flags
, txv
, engine
->sm_list
, engine
->sm_list_base
);
403 if (buffer
== NULL
|| buffer
->inspect
== NULL
) {
404 return eof
? DETECT_ENGINE_INSPECT_SIG_CANT_MATCH
: DETECT_ENGINE_INSPECT_SIG_NO_MATCH
;
407 const uint32_t data_len
= buffer
->inspect_len
;
408 const uint8_t *data
= buffer
->inspect
;
409 const uint64_t offset
= buffer
->inspect_offset
;
411 uint8_t ci_flags
= eof
? DETECT_CI_FLAGS_END
: 0;
412 ci_flags
|= (offset
== 0 ? DETECT_CI_FLAGS_START
: 0);
413 ci_flags
|= buffer
->flags
;
415 det_ctx
->discontinue_matching
= 0;
416 det_ctx
->buffer_offset
= 0;
417 det_ctx
->inspection_recursion_counter
= 0;
419 /* Inspect all the uricontents fetched on each
420 * transaction at the app layer */
421 int r
= DetectEngineContentInspection(de_ctx
, det_ctx
, s
, engine
->smd
, NULL
, f
, (uint8_t *)data
,
422 data_len
, offset
, ci_flags
, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE
);
424 return DETECT_ENGINE_INSPECT_SIG_MATCH
;
427 if (flags
& STREAM_TOSERVER
) {
428 if (AppLayerParserGetStateProgress(IPPROTO_TCP
, ALPROTO_HTTP1
, txv
, flags
) >
430 return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH
;
432 if (AppLayerParserGetStateProgress(IPPROTO_TCP
, ALPROTO_HTTP1
, txv
, flags
) >
434 return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH
;
436 return DETECT_ENGINE_INSPECT_SIG_NO_MATCH
;
439 /** \brief Filedata Filedata Mpm prefilter callback
441 * \param det_ctx detection engine thread ctx
442 * \param pectx inspection context
443 * \param p packet to inspect
444 * \param f flow to inspect
445 * \param txv tx to inspect
446 * \param idx transaction id
447 * \param flags STREAM_* flags including direction
449 static void PrefilterTxHTTPFiledata(DetectEngineThreadCtx
*det_ctx
, const void *pectx
, Packet
*p
,
450 Flow
*f
, void *txv
, const uint64_t idx
, const uint8_t flags
)
454 const PrefilterMpmFiledata
*ctx
= (const PrefilterMpmFiledata
*)pectx
;
455 const MpmCtx
*mpm_ctx
= ctx
->mpm_ctx
;
456 const int list_id
= ctx
->list_id
;
458 InspectionBuffer
*buffer
= HttpServerBodyGetDataCallback(
459 det_ctx
, ctx
->transforms
, f
, flags
, txv
, list_id
, ctx
->base_list_id
);
463 if (buffer
->inspect_len
>= mpm_ctx
->minlen
) {
464 (void)mpm_table
[mpm_ctx
->mpm_type
].Search(
465 mpm_ctx
, &det_ctx
->mtcu
, &det_ctx
->pmq
, buffer
->inspect
, buffer
->inspect_len
);
469 static int PrefilterMpmHTTPFiledataRegister(DetectEngineCtx
*de_ctx
, SigGroupHead
*sgh
,
470 MpmCtx
*mpm_ctx
, const DetectBufferMpmRegistery
*mpm_reg
, int list_id
)
472 PrefilterMpmFiledata
*pectx
= SCCalloc(1, sizeof(*pectx
));
475 pectx
->list_id
= list_id
;
476 pectx
->base_list_id
= mpm_reg
->sm_list_base
;
477 SCLogDebug("list_id %d base_list_id %d", list_id
, pectx
->base_list_id
);
478 pectx
->mpm_ctx
= mpm_ctx
;
479 pectx
->transforms
= &mpm_reg
->transforms
;
481 return PrefilterAppendTxEngine(de_ctx
, sgh
, PrefilterTxHTTPFiledata
, mpm_reg
->app_v2
.alproto
,
482 mpm_reg
->app_v2
.tx_min_progress
, pectx
, PrefilterMpmFiledataFree
, mpm_reg
->pname
);
485 /* file API based inspection */
487 static inline InspectionBuffer
*FiledataWithXformsGetDataCallback(DetectEngineThreadCtx
*det_ctx
,
488 const DetectEngineTransforms
*transforms
, const int list_id
, int local_file_id
,
489 InspectionBuffer
*base_buffer
, const bool first
)
491 InspectionBuffer
*buffer
= InspectionBufferMultipleForListGet(det_ctx
, list_id
, local_file_id
);
492 if (buffer
== NULL
) {
493 SCLogDebug("list_id: %d: no buffer", list_id
);
496 if (!first
&& buffer
->inspect
!= NULL
) {
497 SCLogDebug("list_id: %d: returning %p", list_id
, buffer
);
501 InspectionBufferSetupMulti(buffer
, transforms
, base_buffer
->inspect
, base_buffer
->inspect_len
);
502 buffer
->inspect_offset
= base_buffer
->inspect_offset
;
503 SCLogDebug("xformed buffer %p size %u", buffer
, buffer
->inspect_len
);
504 SCReturnPtr(buffer
, "InspectionBuffer");
507 static InspectionBuffer
*FiledataGetDataCallback(DetectEngineThreadCtx
*det_ctx
,
508 const DetectEngineTransforms
*transforms
, Flow
*f
, uint8_t flow_flags
, File
*cur_file
,
509 const int list_id
, const int base_id
, int local_file_id
, bool first
)
513 "starting: list_id %d base_id %d first %s", list_id
, base_id
, first
? "true" : "false");
515 InspectionBuffer
*buffer
= InspectionBufferMultipleForListGet(det_ctx
, base_id
, local_file_id
);
516 SCLogDebug("base: buffer %p", buffer
);
519 if (base_id
!= list_id
&& buffer
->inspect
!= NULL
) {
520 SCLogDebug("handle xform %s", (list_id
!= base_id
) ? "true" : "false");
521 return FiledataWithXformsGetDataCallback(
522 det_ctx
, transforms
, list_id
, local_file_id
, buffer
, first
);
524 if (!first
&& buffer
->inspect
!= NULL
) {
525 SCLogDebug("base_id: %d, not first: use %p", base_id
, buffer
);
529 const uint64_t file_size
= FileDataSize(cur_file
);
530 const DetectEngineCtx
*de_ctx
= det_ctx
->de_ctx
;
531 const uint32_t content_limit
= de_ctx
->filedata_config
[f
->alproto
].content_limit
;
532 const uint32_t content_inspect_min_size
= de_ctx
->filedata_config
[f
->alproto
].content_inspect_min_size
;
533 // TODO this is unused, is that right?
534 //const uint32_t content_inspect_window = de_ctx->filedata_config[f->alproto].content_inspect_window;
536 SCLogDebug("[list %d] first: %d, content_limit %u, content_inspect_min_size %u", list_id
,
537 first
? 1 : 0, content_limit
, content_inspect_min_size
);
539 SCLogDebug("[list %d] file %p size %" PRIu64
", state %d", list_id
, cur_file
, file_size
,
543 if (cur_file
->content_inspected
== file_size
) {
544 SCLogDebug("no new data");
548 if (file_size
== 0) {
549 SCLogDebug("no data to inspect for this transaction");
553 if ((content_limit
== 0 || file_size
< content_limit
) &&
554 file_size
< content_inspect_min_size
&&
555 !(flow_flags
& STREAM_EOF
) && !(cur_file
->state
> FILE_STATE_OPENED
)) {
556 SCLogDebug("we still haven't seen the entire content. "
557 "Let's defer content inspection till we see the "
565 StreamingBufferGetDataAtOffset(cur_file
->sb
,
567 cur_file
->content_inspected
);
568 InspectionBufferSetupMulti(buffer
, NULL
, data
, data_len
);
569 SCLogDebug("[list %d] [before] buffer offset %" PRIu64
"; buffer len %" PRIu32
570 "; data_len %" PRIu32
"; file_size %" PRIu64
,
571 list_id
, buffer
->inspect_offset
, buffer
->inspect_len
, data_len
, file_size
);
572 buffer
->inspect_offset
= cur_file
->content_inspected
;
574 /* update inspected tracker */
575 cur_file
->content_inspected
= buffer
->inspect_len
+ buffer
->inspect_offset
;
576 SCLogDebug("content inspected: %" PRIu64
, cur_file
->content_inspected
);
578 /* get buffer for the list id if it is different from the base id */
579 if (list_id
!= base_id
) {
580 SCLogDebug("regular %d has been set up: now handle xforms id %d", base_id
, list_id
);
581 InspectionBuffer
*tbuffer
= FiledataWithXformsGetDataCallback(
582 det_ctx
, transforms
, list_id
, local_file_id
, buffer
, first
);
583 SCReturnPtr(tbuffer
, "InspectionBuffer");
585 SCLogDebug("regular buffer %p size %u", buffer
, buffer
->inspect_len
);
586 SCReturnPtr(buffer
, "InspectionBuffer");
590 static int DetectEngineInspectFiledata(
591 DetectEngineCtx
*de_ctx
, DetectEngineThreadCtx
*det_ctx
,
592 const DetectEngineAppInspectionEngine
*engine
,
594 Flow
*f
, uint8_t flags
, void *alstate
, void *txv
, uint64_t tx_id
)
599 const DetectEngineTransforms
*transforms
= NULL
;
601 transforms
= engine
->v2
.transforms
;
604 FileContainer
*ffc
= AppLayerParserGetFiles(f
, flags
);
606 return DETECT_ENGINE_INSPECT_SIG_NO_MATCH
;
609 int local_file_id
= 0;
610 File
*file
= ffc
->head
;
611 for (; file
!= NULL
; file
= file
->next
) {
612 if (file
->txid
!= tx_id
)
615 InspectionBuffer
*buffer
= FiledataGetDataCallback(det_ctx
, transforms
, f
, flags
, file
,
616 engine
->sm_list
, engine
->sm_list_base
, local_file_id
, false);
620 bool eof
= (file
->state
== FILE_STATE_CLOSED
);
621 uint8_t ciflags
= eof
? DETECT_CI_FLAGS_END
: 0;
622 if (buffer
->inspect_offset
== 0)
623 ciflags
|= DETECT_CI_FLAGS_START
;
625 det_ctx
->buffer_offset
= 0;
626 det_ctx
->discontinue_matching
= 0;
627 det_ctx
->inspection_recursion_counter
= 0;
628 match
= DetectEngineContentInspection(de_ctx
, det_ctx
, s
, engine
->smd
,
630 (uint8_t *)buffer
->inspect
,
632 buffer
->inspect_offset
, ciflags
,
633 DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE
);
642 return DETECT_ENGINE_INSPECT_SIG_MATCH
;
644 return DETECT_ENGINE_INSPECT_SIG_NO_MATCH
;
647 /** \brief Filedata Filedata Mpm prefilter callback
649 * \param det_ctx detection engine thread ctx
650 * \param pectx inspection context
651 * \param p packet to inspect
652 * \param f flow to inspect
653 * \param txv tx to inspect
654 * \param idx transaction id
655 * \param flags STREAM_* flags including direction
657 static void PrefilterTxFiledata(DetectEngineThreadCtx
*det_ctx
,
659 Packet
*p
, Flow
*f
, void *txv
,
660 const uint64_t idx
, const uint8_t flags
)
664 const PrefilterMpmFiledata
*ctx
= (const PrefilterMpmFiledata
*)pectx
;
665 const MpmCtx
*mpm_ctx
= ctx
->mpm_ctx
;
666 const int list_id
= ctx
->list_id
;
668 FileContainer
*ffc
= AppLayerParserGetFiles(f
, flags
);
669 int local_file_id
= 0;
671 File
*file
= ffc
->head
;
672 for (; file
!= NULL
; file
= file
->next
) {
673 if (file
->txid
!= idx
)
676 InspectionBuffer
*buffer
= FiledataGetDataCallback(det_ctx
, ctx
->transforms
, f
, flags
,
677 file
, list_id
, ctx
->base_list_id
, local_file_id
, true);
681 if (buffer
->inspect_len
>= mpm_ctx
->minlen
) {
682 (void)mpm_table
[mpm_ctx
->mpm_type
].Search(mpm_ctx
,
683 &det_ctx
->mtcu
, &det_ctx
->pmq
,
684 buffer
->inspect
, buffer
->inspect_len
);
691 int PrefilterMpmFiledataRegister(DetectEngineCtx
*de_ctx
,
692 SigGroupHead
*sgh
, MpmCtx
*mpm_ctx
,
693 const DetectBufferMpmRegistery
*mpm_reg
, int list_id
)
695 PrefilterMpmFiledata
*pectx
= SCCalloc(1, sizeof(*pectx
));
698 pectx
->list_id
= list_id
;
699 pectx
->base_list_id
= mpm_reg
->sm_list_base
;
700 pectx
->mpm_ctx
= mpm_ctx
;
701 pectx
->transforms
= &mpm_reg
->transforms
;
703 return PrefilterAppendTxEngine(de_ctx
, sgh
, PrefilterTxFiledata
,
704 mpm_reg
->app_v2
.alproto
, mpm_reg
->app_v2
.tx_min_progress
,
705 pectx
, PrefilterMpmFiledataFree
, mpm_reg
->pname
);
709 #include "tests/detect-file-data.c"