suricata
detect-http-client-body.c
Go to the documentation of this file.
1/* Copyright (C) 2007-2021 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/**
19 * \ingroup httplayer
20 *
21 * @{
22 */
23
24
25/**
26 * \file
27 *
28 * \author Anoop Saldanha <anoopsaldanha@gmail.com>
29 *
30 * Implements support for the http_client_body keyword
31 */
32
33#include "suricata-common.h"
34#include "threads.h"
35#include "decode.h"
36
37#include "detect.h"
38#include "detect-parse.h"
39#include "detect-engine.h"
41#include "detect-engine-mpm.h"
42#include "detect-engine-state.h"
45#include "detect-content.h"
46#include "detect-pcre.h"
47// PrefilterMpmFiledata
48#include "detect-file-data.h"
49
50#include "flow.h"
51#include "flow-var.h"
52#include "flow-util.h"
53
54#include "util-debug.h"
55#include "util-unittest.h"
57#include "util-spm.h"
58
59#include "app-layer.h"
60#include "app-layer-parser.h"
61#include "app-layer-htp.h"
63#include "stream-tcp.h"
64#include "util-profiling.h"
65
66static int DetectHttpClientBodySetup(DetectEngineCtx *, Signature *, const char *);
67static int DetectHttpClientBodySetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str);
68#ifdef UNITTESTS
69static void DetectHttpClientBodyRegisterTests(void);
70#endif
71static void DetectHttpClientBodySetupCallback(const DetectEngineCtx *de_ctx,
72 Signature *s);
73static int g_http_client_body_buffer_id = 0;
74
75static uint8_t DetectEngineInspectBufferHttpBody(DetectEngineCtx *de_ctx,
77 const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id);
78
79static int PrefilterMpmHttpRequestBodyRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
80 MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id);
81
82/**
83 * \brief Registers the keyword handlers for the "http_client_body" keyword.
84 */
86{
87 /* http_client_body content modifier */
88 sigmatch_table[DETECT_HTTP_CLIENT_BODY].name = "http_client_body";
90 "content modifier to match only on HTTP request-body";
91 sigmatch_table[DETECT_HTTP_CLIENT_BODY].url = "/rules/http-keywords.html#http-client-body";
92 sigmatch_table[DETECT_HTTP_CLIENT_BODY].Setup = DetectHttpClientBodySetup;
93#ifdef UNITTESTS
95#endif
99
100 /* http.request_body sticky buffer */
101 sigmatch_table[DETECT_HTTP_REQUEST_BODY].name = "http.request_body";
102 sigmatch_table[DETECT_HTTP_REQUEST_BODY].desc = "sticky buffer to match the HTTP request body buffer";
103 sigmatch_table[DETECT_HTTP_REQUEST_BODY].url = "/rules/http-keywords.html#http-client-body";
104 sigmatch_table[DETECT_HTTP_REQUEST_BODY].Setup = DetectHttpClientBodySetupSticky;
107
109 HTP_REQUEST_PROGRESS_BODY, DetectEngineInspectBufferHttpBody, NULL);
110
111 DetectAppLayerMpmRegister("http_client_body", SIG_FLAG_TOSERVER, 2,
112 PrefilterMpmHttpRequestBodyRegister, NULL, ALPROTO_HTTP1, HTP_REQUEST_PROGRESS_BODY);
113
115 HTTP2StateDataClient, DetectEngineInspectFiledata, NULL);
116 DetectAppLayerMpmRegister("http_client_body", SIG_FLAG_TOSERVER, 2,
117 PrefilterMpmFiledataRegister, NULL, ALPROTO_HTTP2, HTTP2StateDataClient);
118
119 DetectBufferTypeSetDescriptionByName("http_client_body",
120 "http request body");
121
122 DetectBufferTypeRegisterSetupCallback("http_client_body",
123 DetectHttpClientBodySetupCallback);
124
125 g_http_client_body_buffer_id = DetectBufferTypeGetByName("http_client_body");
126}
127
128static void DetectHttpClientBodySetupCallback(const DetectEngineCtx *de_ctx,
129 Signature *s)
130{
131 SCLogDebug("callback invoked by %u", s->id);
133
134 /* client body needs to be inspected in sync with stream if possible */
136}
137
138/**
139 * \brief The setup function for the http_client_body keyword for a signature.
140 *
141 * \param de_ctx Pointer to the detection engine context.
142 * \param s Pointer to signature for the current Signature being parsed
143 * from the rules.
144 * \param m Pointer to the head of the SigMatchs for the current rule
145 * being parsed.
146 * \param arg Pointer to the string holding the keyword value.
147 *
148 * \retval 0 On success
149 * \retval -1 On failure
150 */
151int DetectHttpClientBodySetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
152{
154 de_ctx, s, arg, DETECT_HTTP_CLIENT_BODY, g_http_client_body_buffer_id, ALPROTO_HTTP1);
155}
156
157/**
158 * \brief this function setup the http.request_body keyword used in the rule
159 *
160 * \param de_ctx Pointer to the Detection Engine Context
161 * \param s Pointer to the Signature to which the current keyword belongs
162 * \param str Should hold an empty string always
163 *
164 * \retval 0 On success
165 */
166static int DetectHttpClientBodySetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str)
167{
168 if (SCDetectBufferSetActiveList(de_ctx, s, g_http_client_body_buffer_id) < 0)
169 return -1;
171 return -1;
172 // we cannot use a transactional rule with a fast pattern to client and this
174 SCLogError("fast_pattern cannot be used on to_client keyword for "
175 "transactional rule with a streaming buffer to server %u",
176 s->id);
177 return -1;
178 }
180 return 0;
181}
182
183static inline HtpBody *GetRequestBody(htp_tx_t *tx)
184{
185 HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
186 return &htud->request_body;
187}
188
195
196static void PrefilterMpmHttpRequestBodyFree(void *ptr)
197{
198 SCFree(ptr);
199}
200
201static inline InspectionBuffer *HttpRequestBodyXformsGetDataCallback(DetectEngineThreadCtx *det_ctx,
202 const DetectEngineTransforms *transforms, const int list_id, InspectionBuffer *base_buffer)
203{
204 InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
205 if (buffer->inspect != NULL)
206 return buffer;
207
208 InspectionBufferSetup(det_ctx, list_id, buffer, base_buffer->inspect, base_buffer->inspect_len);
209 buffer->inspect_offset = base_buffer->inspect_offset;
210 InspectionBufferApplyTransforms(det_ctx, buffer, transforms);
211 SCLogDebug("xformed buffer %p size %u", buffer, buffer->inspect_len);
212 SCReturnPtr(buffer, "InspectionBuffer");
213}
214
215static InspectionBuffer *HttpRequestBodyGetDataCallback(DetectEngineThreadCtx *det_ctx,
216 const DetectEngineTransforms *transforms, Flow *f, const uint8_t flow_flags, void *txv,
217 const int list_id, const int base_id)
218{
219 SCEnter();
220
221 InspectionBuffer *buffer = InspectionBufferGet(det_ctx, base_id);
222 if (base_id != list_id && buffer->inspect != NULL)
223 return HttpRequestBodyXformsGetDataCallback(det_ctx, transforms, list_id, buffer);
224 else if (buffer->inspect != NULL)
225 return buffer;
226
227 htp_tx_t *tx = txv;
228 HtpState *htp_state = f->alstate;
229 const uint8_t flags = flow_flags;
230
231 HtpBody *body = GetRequestBody(tx);
232 if (body == NULL) {
233 return NULL;
234 }
235
236 /* no new data */
237 if (body->body_inspected == body->content_len_so_far) {
238 SCLogDebug("no new data");
239 return NULL;
240 }
241
242 HtpBodyChunk *cur = body->first;
243 if (cur == NULL) {
244 SCLogDebug("No http chunks to inspect for this transaction");
245 return NULL;
246 }
247
248 SCLogDebug("request.body_limit %u request_body.content_len_so_far %" PRIu64
249 ", request.inspect_min_size %" PRIu32 ", EOF %s, progress > body? %s",
250 htp_state->cfg->request.body_limit, body->content_len_so_far,
251 htp_state->cfg->request.inspect_min_size, flags & STREAM_EOF ? "true" : "false",
253 HTP_REQUEST_PROGRESS_BODY)
254 ? "true"
255 : "false");
256
257 if (!htp_state->cfg->http_body_inline) {
258 /* inspect the body if the transfer is complete or we have hit
259 * our body size limit */
260 if ((htp_state->cfg->request.body_limit == 0 ||
261 body->content_len_so_far < htp_state->cfg->request.body_limit) &&
262 body->content_len_so_far < htp_state->cfg->request.inspect_min_size &&
264 HTP_REQUEST_PROGRESS_BODY) &&
265 !(flags & STREAM_EOF)) {
266 SCLogDebug("we still haven't seen the entire request body. "
267 "Let's defer body inspection till we see the "
268 "entire body.");
269 return NULL;
270 }
271 }
272
273 /* get the inspect buffer
274 *
275 * make sure that we have at least the configured inspect_win size.
276 * If we have more, take at least 1/4 of the inspect win size before
277 * the new data.
278 */
279 uint64_t offset = 0;
280 if (body->body_inspected > htp_state->cfg->request.inspect_min_size) {
282 uint64_t inspect_win = body->content_len_so_far - body->body_inspected;
283 SCLogDebug("inspect_win %"PRIu64, inspect_win);
284 if (inspect_win < htp_state->cfg->request.inspect_window) {
285 uint64_t inspect_short = htp_state->cfg->request.inspect_window - inspect_win;
286 if (body->body_inspected < inspect_short)
287 offset = 0;
288 else
289 offset = body->body_inspected - inspect_short;
290 } else {
291 offset = body->body_inspected - (htp_state->cfg->request.inspect_window / 4);
292 }
293 }
294
295 const uint8_t *data;
296 uint32_t data_len;
297
299 &data, &data_len, offset);
300 InspectionBufferSetup(det_ctx, base_id, buffer, data, data_len);
301 buffer->inspect_offset = offset;
303 SCLogDebug("body->body_inspected now: %" PRIu64, body->body_inspected);
304
305 if (base_id != list_id) {
306 buffer = HttpRequestBodyXformsGetDataCallback(det_ctx, transforms, list_id, buffer);
307 }
308 SCReturnPtr(buffer, "InspectionBuffer");
309}
310
311static uint8_t DetectEngineInspectBufferHttpBody(DetectEngineCtx *de_ctx,
313 const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
314{
315 bool eof =
317 const InspectionBuffer *buffer = HttpRequestBodyGetDataCallback(
318 det_ctx, engine->v2.transforms, f, flags, txv, engine->sm_list, engine->sm_list_base);
319 if (buffer == NULL || buffer->inspect == NULL) {
320 if (eof && engine->match_on_null) {
322 }
324 }
325
326 const uint32_t data_len = buffer->inspect_len;
327 const uint8_t *data = buffer->inspect;
328 const uint64_t offset = buffer->inspect_offset;
329
330 uint8_t ci_flags = eof ? DETECT_CI_FLAGS_END : 0;
331 ci_flags |= (offset == 0 ? DETECT_CI_FLAGS_START : 0);
332 ci_flags |= buffer->flags;
333
334 /* Inspect all the uricontents fetched on each
335 * transaction at the app layer */
336 const bool match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, NULL, f, data,
338 if (match) {
340 }
341
342 if (flags & STREAM_TOSERVER) {
343 if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, txv, flags) >
344 HTP_REQUEST_PROGRESS_BODY)
346 } else {
347 if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, txv, flags) >
348 HTP_RESPONSE_PROGRESS_BODY)
350 }
352}
353
354/** \brief HTTP Request body callback
355 *
356 * \param det_ctx detection engine thread ctx
357 * \param pectx inspection context
358 * \param p packet to inspect
359 * \param f flow to inspect
360 * \param txv tx to inspect
361 * \param idx transaction id
362 * \param flags STREAM_* flags including direction
363 */
364static void PrefilterTxHttpRequestBody(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
365 Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
366{
367 SCEnter();
368
370 const MpmCtx *mpm_ctx = ctx->mpm_ctx;
371 const int list_id = ctx->list_id;
372
373 InspectionBuffer *buffer = HttpRequestBodyGetDataCallback(
374 det_ctx, ctx->transforms, f, flags, txv, list_id, ctx->base_list_id);
375 if (buffer == NULL)
376 return;
377
378 if (buffer->inspect_len >= mpm_ctx->minlen) {
379 (void)mpm_table[mpm_ctx->mpm_type].Search(
380 mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, buffer->inspect, buffer->inspect_len);
382 }
383}
384
385static int PrefilterMpmHttpRequestBodyRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
386 MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id)
387{
388 PrefilterMpmHttpRequestBody *pectx = SCCalloc(1, sizeof(*pectx));
389 if (pectx == NULL)
390 return -1;
391 pectx->list_id = list_id;
392 pectx->base_list_id = mpm_reg->sm_list_base;
393 SCLogDebug("list_id %d base_list_id %d", list_id, pectx->base_list_id);
394 pectx->mpm_ctx = mpm_ctx;
395 pectx->transforms = &mpm_reg->transforms;
396
397 return PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxHttpRequestBody, mpm_reg->app_v2.alproto,
398 mpm_reg->app_v2.tx_min_progress, pectx, PrefilterMpmHttpRequestBodyFree,
399 mpm_reg->pname);
400}
401
402#ifdef UNITTESTS
403#include "detect-engine-alert.h"
405#endif /* UNITTESTS */
406
407/**
408 * @}
409 */
int AppLayerParserGetStateProgress(uint8_t ipproto, AppProto alproto, void *alstate, uint8_t flags)
get the progress value for a tx/protocol
struct AppLayerTxData AppLayerTxData
@ ALPROTO_HTTP2
@ ALPROTO_HTTP
@ ALPROTO_HTTP1
uint8_t flags
Definition decode-gre.h:0
int SCDetectBufferSetActiveList(DetectEngineCtx *de_ctx, Signature *s, const int list)
bool DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Packet *p, Flow *f, const uint8_t *buffer, const uint32_t buffer_len, const uint64_t stream_start_offset, const uint8_t flags, const enum DetectContentInspectionType inspection_mode)
wrapper around DetectEngineContentInspectionInternal to return true/false only
@ DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE
#define DETECT_CI_FLAGS_END
#define DETECT_CI_FLAGS_START
void InspectionBufferApplyTransforms(DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, const DetectEngineTransforms *transforms)
InspectionBuffer * InspectionBufferGet(DetectEngineThreadCtx *det_ctx, const int list_id)
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
void DetectAppLayerMpmRegister(const char *name, int direction, int priority, PrefilterRegisterFunc PrefilterRegister, InspectionBufferGetDataPtr GetData, AppProto alproto, int tx_min_progress)
register an app layer keyword for mpm
int PrefilterAppendTxEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, PrefilterTxFn PrefilterTxFunc, AppProto alproto, int tx_min_progress, void *pectx, void(*FreeFunc)(void *pectx), const char *name)
@ DETECT_HTTP_CLIENT_BODY
@ DETECT_HTTP_REQUEST_BODY
Data structures and function prototypes for keeping state for the detection engine.
#define DETECT_ENGINE_INSPECT_SIG_MATCH
#define DETECT_ENGINE_INSPECT_SIG_CANT_MATCH
#define DETECT_ENGINE_INSPECT_SIG_NO_MATCH
void DetectBufferTypeSetDescriptionByName(const char *name, const char *desc)
void DetectBufferTypeRegisterSetupCallback(const char *name, void(*SetupCallback)(const DetectEngineCtx *, Signature *))
void DetectAppLayerInspectEngineRegister(const char *name, AppProto alproto, uint32_t dir, int progress, InspectEngineFuncPtr Callback, InspectionBufferGetDataPtr GetData)
Registers an app inspection engine.
int DetectBufferTypeGetByName(const char *name)
uint8_t DetectEngineInspectFiledata(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const DetectEngineAppInspectionEngine *engine, const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
int PrefilterMpmFiledataRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id)
void DetectHttpClientBodyRegister(void)
Registers the keyword handlers for the "http_client_body" keyword.
int SCDetectSignatureSetAppProto(Signature *s, AppProto alproto)
int DetectEngineContentModifierBufferSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg, int sm_type, int sm_list, AppProto alproto)
SigTableElmt * sigmatch_table
#define SIGMATCH_NOOPT
Definition detect.h:1651
#define SIGMATCH_INFO_STICKY_BUFFER
Definition detect.h:1676
#define SIG_FLAG_INIT_TXDIR_STREAMING_TOSERVER
Definition detect.h:304
#define SIG_FLAG_INIT_NEED_FLUSH
Definition detect.h:297
#define SIG_FLAG_INIT_TXDIR_FAST_TOCLIENT
Definition detect.h:306
#define SIG_FLAG_TOSERVER
Definition detect.h:271
#define SIGMATCH_INFO_CONTENT_MODIFIER
Definition detect.h:1674
DetectEngineCtx * de_ctx
void AppLayerHtpEnableRequestBodyCallback(void)
Sets a flag that informs the HTP app layer that some module in the engine needs the http request body...
struct Thresholds ctx
one time registration of keywords at start up
Definition detect.h:762
DetectEngineTransforms transforms
Definition detect.h:775
struct DetectBufferMpmRegistry_::@98::@100 app_v2
const DetectEngineTransforms * transforms
Definition detect.h:436
struct DetectEngineAppInspectionEngine_::@90 v2
main detection engine ctx
Definition detect.h:932
MpmThreadCtx mtc
Definition detect.h:1345
PrefilterRuleStore pmq
Definition detect.h:1349
Flow data structure.
Definition flow.h:356
uint8_t proto
Definition flow.h:378
AppProto alproto
application level protocol
Definition flow.h:450
void * alstate
Definition flow.h:479
uint32_t inspect_window
uint32_t inspect_min_size
uint32_t body_limit
HTPCfgDir request
uint64_t content_len_so_far
HtpBodyChunk * first
uint64_t body_inspected
StreamingBuffer * sb
const struct HTPCfgRec_ * cfg
uint8_t mpm_type
Definition util-mpm.h:95
uint16_t minlen
Definition util-mpm.h:104
uint32_t(* Search)(const struct MpmCtx_ *, struct MpmThreadCtx_ *, PrefilterRuleStore *, const uint8_t *, uint32_t)
Definition util-mpm.h:178
const DetectEngineTransforms * transforms
Container for matching data for a signature group.
Definition detect.h:1629
const char * url
Definition detect.h:1462
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition detect.h:1441
uint16_t alternative
Definition detect.h:1457
uint16_t flags
Definition detect.h:1450
const char * desc
Definition detect.h:1461
void(* RegisterTests)(void)
Definition detect.h:1448
const char * name
Definition detect.h:1459
uint32_t init_flags
Definition detect.h:608
Signature container.
Definition detect.h:668
SignatureInitData * init_data
Definition detect.h:747
uint32_t id
Definition detect.h:713
#define BUG_ON(x)
#define str(s)
Handle HTTP request body match corresponding to http_client_body keyword.
void DetectHttpClientBodyRegisterTests(void)
#define SCEnter(...)
Definition util-debug.h:277
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnPtr(x, type)
Definition util-debug.h:293
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
MpmTableElmt mpm_table[MPM_TABLE_SIZE]
Definition util-mpm.c:47
#define PREFILTER_PROFILING_ADD_BYTES(det_ctx, bytes)
int StreamingBufferGetDataAtOffset(const StreamingBuffer *sb, const uint8_t **data, uint32_t *data_len, uint64_t offset)
uint64_t offset