65
55
#include "app-layer-htp.h"
66
56
#include "app-layer-protos.h"
69
* \brief Run the actual payload match function for http request body.
71
* For accounting the last match in relative matching the
72
* det_ctx->payload_offset int is used.
74
* \param de_ctx Detection engine context.
75
* \param det_ctx Detection engine thread context.
76
* \param s Signature to inspect.
77
* \param sm SigMatch to inspect.
78
* \param payload Ptr to the request body to inspect.
79
* \param payload_len Length of the request body.
84
static int DoInspectHttpClientBody(DetectEngineCtx *de_ctx,
85
DetectEngineThreadCtx *det_ctx,
86
Signature *s, SigMatch *sm,
87
uint8_t *payload, uint32_t payload_len)
91
det_ctx->inspection_recursion_counter++;
93
if (det_ctx->inspection_recursion_counter == de_ctx->inspection_recursion_limit) {
94
det_ctx->discontinue_matching = 1;
102
if (sm->type == DETECT_AL_HTTP_CLIENT_BODY) {
103
if (payload_len == 0) {
107
DetectContentData *cd = (DetectContentData *)sm->ctx;
108
SCLogDebug("inspecting content %"PRIu32" payload_len %"PRIu32, cd->id, payload_len);
110
//if (cd->flags & DETECT_CONTENT_HCBD_MPM && !(cd->flags & DETECT_CONTENT_NEGATED))
113
/* rule parsers should take care of this */
115
BUG_ON(cd->depth != 0 && cd->depth <= cd->offset);
118
/* search for our pattern, checking the matches recursively.
119
* if we match we look for the next SigMatch as well */
120
uint8_t *found = NULL;
122
uint32_t depth = payload_len;
123
uint32_t prev_offset = 0; /**< used in recursive searching */
124
uint32_t prev_payload_offset = det_ctx->payload_offset;
127
if (cd->flags & DETECT_CONTENT_DISTANCE ||
128
cd->flags & DETECT_CONTENT_WITHIN) {
129
SCLogDebug("prev_payload_offset %"PRIu32, prev_payload_offset);
131
offset = prev_payload_offset;
134
if (cd->flags & DETECT_CONTENT_DISTANCE) {
135
if (cd->distance < 0 && (uint32_t)(abs(cd->distance)) > offset)
138
offset += cd->distance;
141
if (cd->flags & DETECT_CONTENT_WITHIN) {
142
if ((int32_t)depth > (int32_t)(prev_payload_offset + cd->within + cd->distance)) {
143
depth = prev_payload_offset + cd->within + cd->distance;
147
if (cd->depth != 0) {
148
if ((cd->depth + prev_payload_offset) < depth) {
149
depth = prev_payload_offset + cd->depth;
153
if (cd->offset > offset) {
156
} else { /* implied no relative matches */
158
if (cd->depth != 0) {
164
prev_payload_offset = 0;
167
/* update offset with prev_offset if we're searching for
168
* matches after the first occurence. */
169
if (prev_offset != 0)
170
offset = prev_offset;
172
if (depth > payload_len)
175
/* if offset is bigger than depth we can never match on a pattern.
176
* We can however, "match" on a negated pattern. */
177
if (offset > depth || depth == 0) {
178
if (cd->flags & DETECT_CONTENT_NEGATED) {
185
uint8_t *spayload = payload + offset;
186
uint32_t spayload_len = depth - offset;
187
uint32_t match_offset = 0;
189
BUG_ON(spayload_len > payload_len);
192
/* do the actual search with boyer moore precooked ctx */
193
if (cd->flags & DETECT_CONTENT_NOCASE) {
194
found = BoyerMooreNocase(cd->content, cd->content_len,
195
spayload, spayload_len,
196
cd->bm_ctx->bmGs, cd->bm_ctx->bmBc);
198
found = BoyerMoore(cd->content, cd->content_len,
199
spayload, spayload_len,
200
cd->bm_ctx->bmGs, cd->bm_ctx->bmBc);
203
/* next we evaluate the result in combination with the
205
if (found == NULL && !(cd->flags & DETECT_CONTENT_NEGATED)) {
207
} else if (found == NULL && cd->flags & DETECT_CONTENT_NEGATED) {
209
} else if (found != NULL && cd->flags & DETECT_CONTENT_NEGATED) {
210
det_ctx->discontinue_matching = 1;
213
match_offset = (uint32_t)((found - payload) + cd->content_len);
214
det_ctx->payload_offset = match_offset;
216
if (!(cd->flags & DETECT_CONTENT_RELATIVE_NEXT)) {
217
SCLogDebug("no relative match coming up, so this is a match");
221
/* bail out if we have no next match. Technically this is an
222
* error, as the current cd has the DETECT_CONTENT_RELATIVE_NEXT
224
if (sm->next == NULL) {
228
/* see if the next payload keywords match. If not, we will
229
* search for another occurence of this http client body content
230
* and see if the others match then until we run out of matches */
231
int r = DoInspectHttpClientBody(de_ctx, det_ctx, s, sm->next,
232
payload, payload_len);
237
if (det_ctx->discontinue_matching)
240
/* set the previous match offset to the start of this match + 1 */
241
prev_offset = (match_offset - (cd->content_len - 1));
242
SCLogDebug("trying to see if there is another match after "
243
"prev_offset %"PRIu32, prev_offset);
248
} else if (sm->type == DETECT_PCRE) {
249
SCLogDebug("inspecting pcre");
250
DetectPcreData *pe = (DetectPcreData *)sm->ctx;
251
uint32_t prev_payload_offset = det_ctx->payload_offset;
252
uint32_t prev_offset = 0;
255
det_ctx->pcre_match_start_offset = 0;
257
r = DetectPcrePayloadMatch(det_ctx, s, sm, NULL, NULL,
258
payload, payload_len);
261
det_ctx->discontinue_matching = 1;
265
if (!(pe->flags & DETECT_PCRE_RELATIVE_NEXT)) {
266
SCLogDebug("no relative match coming up, so this is a match");
270
/* save it, in case we need to do a pcre match once again */
271
prev_offset = det_ctx->pcre_match_start_offset;
273
/* see if the next payload keywords match. If not, we will
274
* search for another occurence of this pcre and see
275
* if the others match, until we run out of matches */
276
int r = DoInspectHttpClientBody(de_ctx, det_ctx, s, sm->next,
277
payload, payload_len);
282
if (det_ctx->discontinue_matching)
285
det_ctx->payload_offset = prev_payload_offset;
286
det_ctx->pcre_match_start_offset = prev_offset;
289
/* we should never get here, but bail out just in case */
290
SCLogDebug("sm->type %u", sm->type);
299
/* this sigmatch matched, inspect the next one. If it was the last,
300
* the payload portion of the signature matched. */
301
if (sm->next != NULL) {
302
int r = DoInspectHttpClientBody(de_ctx, det_ctx, s, sm->next, payload,
58
#define BODY_SCAN_WINDOW 4096
59
#define BODY_MINIMAL_SIZE 32768
311
62
* \brief Helps buffer request bodies for different transactions and stores them
348
95
/* let's get the transaction count. We need this to hold the client body
349
96
* buffer for each transaction */
350
det_ctx->hcbd_buffers_list_len = list_size(htp_state->connp->conn->transactions) - tmp_idx;
97
size_t txs = list_size(htp_state->connp->conn->transactions) - tmp_idx;
351
98
/* no transactions?! cool. get out of here */
352
if (det_ctx->hcbd_buffers_list_len == 0)
355
/* assign space to hold buffers. Each per transaction */
356
det_ctx->hcbd_buffers = SCMalloc(det_ctx->hcbd_buffers_list_len * sizeof(uint8_t *));
357
if (det_ctx->hcbd_buffers == NULL) {
360
memset(det_ctx->hcbd_buffers, 0, det_ctx->hcbd_buffers_list_len * sizeof(uint8_t *));
362
det_ctx->hcbd_buffers_len = SCMalloc(det_ctx->hcbd_buffers_list_len * sizeof(uint32_t));
363
if (det_ctx->hcbd_buffers_len == NULL) {
366
memset(det_ctx->hcbd_buffers_len, 0, det_ctx->hcbd_buffers_list_len * sizeof(uint32_t));
100
det_ctx->hcbd_buffers_list_len = 0;
102
} else if (txs > det_ctx->hcbd_buffers_list_len) {
103
det_ctx->hcbd = SCRealloc(det_ctx->hcbd, txs * sizeof(HttpReassembledBody));
104
if (det_ctx->hcbd == NULL) {
105
det_ctx->hcbd_buffers_list_len = 0;
109
memset(det_ctx->hcbd + det_ctx->hcbd_buffers_list_len, 0,
110
(txs - det_ctx->hcbd_buffers_list_len) * sizeof(HttpReassembledBody));
111
det_ctx->hcbd_buffers_list_len = txs;
368
114
idx = AppLayerTransactionGetInspectId(f);
373
119
int size = (int)list_size(htp_state->connp->conn->transactions);
374
120
for (; idx < size; idx++, i++) {
122
if (det_ctx->hcbd[i].buffer_len > 0) {
123
SCLogDebug("set up already");
376
127
tx = list_get(htp_state->connp->conn->transactions, idx);
380
133
HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx);
135
SCLogDebug("no htud");
140
if (htud->request_body.body_inspected == htud->request_body.content_len_so_far) {
141
SCLogDebug("no new data");
384
145
HtpBodyChunk *cur = htud->request_body.first;
386
if (htud->request_body.nchunks == 0) {
387
147
SCLogDebug("No http chunks to inspect for this transacation");
390
/* no chunks?!! move on to the next transaction */
392
SCLogDebug("No http chunks to inspect");
396
/* in case of chunked transfer encoding, we don't have the length
397
* of the request body until we see a chunk with length 0. This
398
* doesn't let us use the request body callback function to
399
* figure out the end of request body. Instead we do it here. If
400
* the length is 0, and we have already seen content, it indicates
401
* chunked transfer. We also check if the parser has truly seen
402
* the last chunk by checking the progress state for the
403
* transaction. If we are done parsing all the chunks, we would
404
* have it set to something other than TX_PROGRESS_REQ_BODY.
405
* Either ways we should be moving away from buffering in the end
406
* and running content validation on this buffer type of architecture
407
* to a stateful inspection, where we can inspect body chunks as and
409
if (htud->request_body.content_len == 0) {
410
if ((htud->request_body.content_len_so_far > 0) &&
151
/* in case of chunked transfer encoding, we don't have the length
152
* of the request body until we see a chunk with length 0. This
153
* doesn't let us use the request body callback function to
154
* figure out the end of request body. Instead we do it here. If
155
* the length is 0, and we have already seen content, it indicates
156
* chunked transfer. We also check if the parser has truly seen
157
* the last chunk by checking the progress state for the
158
* transaction. If we are done parsing all the chunks, we would
159
* have it set to something other than TX_PROGRESS_REQ_BODY.
160
* Either ways we should be moving away from buffering in the end
161
* and running content validation on this buffer type of architecture
162
* to a stateful inspection, where we can inspect body chunks as and
164
if (htud->request_body.content_len == 0) {
165
if ((htud->request_body.content_len_so_far > 0) &&
411
166
tx->progress != TX_PROGRESS_REQ_BODY) {
412
/* final length of the body */
413
htud->flags |= HTP_BODY_COMPLETE;
167
/* final length of the body */
168
htud->tsflags |= HTP_REQ_BODY_COMPLETE;
172
if (flags & STREAM_EOF) {
173
htud->tsflags |= HTP_REQ_BODY_COMPLETE;
176
/* inspect the body if the transfer is complete or we have hit
177
* our body size limit */
178
if (htud->request_body.content_len_so_far < BODY_MINIMAL_SIZE &&
179
!(htud->tsflags & HTP_REQ_BODY_COMPLETE)) {
180
SCLogDebug("we still haven't seen the entire request body. "
181
"Let's defer body inspection till we see the "
187
while (cur != NULL) {
188
/* see if we can filter out chunks */
189
if (htud->request_body.body_inspected > 0) {
190
if (cur->stream_offset < htud->request_body.body_inspected) {
191
if (htud->request_body.body_inspected - cur->stream_offset > BODY_SCAN_WINDOW) {
195
/* include this one */
198
/* include this one */
417
/* inspect the body if the transfer is complete or we have hit
418
* our body size limit */
419
if (!(htud->flags & HTP_BODY_COMPLETE)) {
420
SCLogDebug("we still haven't seen the entire request body. "
421
"Let's defer body inspection till we see the "
203
det_ctx->hcbd[i].offset = cur->stream_offset;
426
uint8_t *chunks_buffer = NULL;
427
int32_t chunks_buffer_len = 0;
428
while (cur != NULL) {
429
chunks_buffer_len += cur->len;
430
if ( (chunks_buffer = SCRealloc(chunks_buffer, chunks_buffer_len)) == NULL) {
207
/* see if we need to grow the buffer */
208
if (det_ctx->hcbd[i].buffer == NULL || det_ctx->hcbd[i].buffer_len + cur->len > det_ctx->hcbd[i].buffer_size) {
209
det_ctx->hcbd[i].buffer_size += cur->len * 2;
211
if ((det_ctx->hcbd[i].buffer = SCRealloc(det_ctx->hcbd[i].buffer, det_ctx->hcbd[i].buffer_size)) == NULL) {
434
memcpy(chunks_buffer + chunks_buffer_len - cur->len, cur->data, cur->len);
437
/* store the buffers. We will need it for further inspection */
438
det_ctx->hcbd_buffers[i] = chunks_buffer;
439
det_ctx->hcbd_buffers_len[i] = chunks_buffer_len;
441
} /* else - if (htud->body.nchunks == 0) */
215
memcpy(det_ctx->hcbd[i].buffer + det_ctx->hcbd[i].buffer_len, cur->data, cur->len);
216
det_ctx->hcbd[i].buffer_len += cur->len;
221
/* update inspected tracker */
222
htud->request_body.body_inspected =
223
htud->request_body.last->stream_offset +
224
htud->request_body.last->len;
442
225
} /* for (idx = AppLayerTransactionGetInspectId(f); .. */
448
231
int DetectEngineRunHttpClientBodyMpm(DetectEngineCtx *de_ctx,
449
DetectEngineThreadCtx *det_ctx, Flow *f, HtpState *htp_state)
232
DetectEngineThreadCtx *det_ctx, Flow *f,
233
HtpState *htp_state, uint8_t flags)
452
236
uint32_t cnt = 0;
454
/* bail before locking if we have nothing to do */
455
if (det_ctx->hcbd_buffers_list_len == 0) {
457
DetectEngineBufferHttpClientBodies(de_ctx, det_ctx, f, htp_state);
458
SCMutexUnlock(&f->m);
461
for (i = 0; i < det_ctx->hcbd_buffers_list_len; i++) {
462
cnt += HttpClientBodyPatternSearch(det_ctx,
463
det_ctx->hcbd_buffers[i],
464
det_ctx->hcbd_buffers_len[i]);
239
DetectEngineBufferHttpClientBodies(de_ctx, det_ctx, f, htp_state, flags);
242
if (det_ctx->hcbd != NULL && det_ctx->hcbd_buffers_list_len) {
243
for (i = 0; i < det_ctx->hcbd_buffers_list_len; i++) {
244
if (det_ctx->hcbd[i].buffer_len == 0)
247
cnt += HttpClientBodyPatternSearch(det_ctx,
248
det_ctx->hcbd[i].buffer,
249
det_ctx->hcbd[i].buffer_len,
492
/* bail before locking if we have nothing to do */
493
if (det_ctx->hcbd_buffers_list_len == 0) {
495
DetectEngineBufferHttpClientBodies(de_ctx, det_ctx, f, alstate);
496
SCMutexUnlock(&f->m);
499
for (i = 0; i < det_ctx->hcbd_buffers_list_len; i++) {
500
uint8_t *hcbd_buffer = det_ctx->hcbd_buffers[i];
501
uint32_t hcbd_buffer_len = det_ctx->hcbd_buffers_len[i];
503
if (hcbd_buffer == NULL)
506
r = DoInspectHttpClientBody(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HCBDMATCH],
507
hcbd_buffer, hcbd_buffer_len);
280
DetectEngineBufferHttpClientBodies(de_ctx, det_ctx, f, alstate, flags);
283
if (det_ctx->hcbd != NULL && det_ctx->hcbd_buffers_list_len) {
284
for (i = 0; i < det_ctx->hcbd_buffers_list_len; i++) {
285
uint8_t *hcbd_buffer = det_ctx->hcbd[i].buffer;
286
uint32_t hcbd_buffer_len = det_ctx->hcbd[i].buffer_len;
288
if (hcbd_buffer == NULL || hcbd_buffer_len == 0)
291
det_ctx->buffer_offset = 0;
292
det_ctx->discontinue_matching = 0;
293
det_ctx->inspection_recursion_counter = 0;
295
r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HCBDMATCH],
299
DETECT_ENGINE_CONTENT_INSPECTION_MODE_HCBD, NULL);