suricata
detect-http-header.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2018 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 Pablo Rincon <pablo.rincon.crespo@gmail.com>
29  *
30  * Implements support for http_header 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"
40 #include "detect-engine-mpm.h"
41 #include "detect-engine-state.h"
44 #include "detect-content.h"
45 #include "detect-pcre.h"
46 
47 #include "util-debug.h"
48 #include "util-print.h"
49 #include "util-memcmp.h"
50 
51 #include "app-layer.h"
52 #include "app-layer-parser.h"
53 
54 #include "app-layer-htp.h"
55 #include "detect-http-header.h"
57 
58 static int DetectHttpHeaderSetup(DetectEngineCtx *, Signature *, const char *);
59 #ifdef UNITTESTS
60 static void DetectHttpHeaderRegisterTests(void);
61 #endif
62 static int g_http_header_buffer_id = 0;
63 static int g_keyword_thread_id = 0;
64 
65 #define BUFFER_TX_STEP 4
66 #define BUFFER_SIZE_STEP 1024
68 
69 static uint8_t *GetBufferForTX(htp_tx_t *tx, uint64_t tx_id,
70  DetectEngineThreadCtx *det_ctx,
71  Flow *f, uint8_t flags, uint32_t *buffer_len)
72 {
73  *buffer_len = 0;
74 
75  HttpHeaderThreadData *hdr_td = NULL;
77  tx_id, g_keyword_thread_id, &hdr_td);
78  if (unlikely(buf == NULL)) {
79  return NULL;
80  } else if (buf->len > 0) {
81  /* already filled buf, reuse */
82  *buffer_len = buf->len;
83  return buf->buffer;
84  }
85 
86  htp_table_t *headers;
87  if (flags & STREAM_TOSERVER) {
88  if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) <= HTP_REQUEST_HEADERS)
89  return NULL;
90  headers = tx->request_headers;
91  } else {
92  if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, tx, flags) <= HTP_RESPONSE_HEADERS)
93  return NULL;
94  headers = tx->response_headers;
95  }
96  if (headers == NULL)
97  return NULL;
98 
99  size_t i = 0;
100  size_t no_of_headers = htp_table_size(headers);
101  for (; i < no_of_headers; i++) {
102  htp_header_t *h = htp_table_get_index(headers, i, NULL);
103  size_t size1 = bstr_size(h->name);
104  size_t size2 = bstr_size(h->value);
105 
106  if (flags & STREAM_TOSERVER) {
107  if (size1 == 6 &&
108  SCMemcmpLowercase("cookie", bstr_ptr(h->name), 6) == 0) {
109  continue;
110  }
111  } else {
112  if (size1 == 10 &&
113  SCMemcmpLowercase("set-cookie", bstr_ptr(h->name), 10) == 0) {
114  continue;
115  }
116  }
117 
118  size_t size = size1 + size2 + 4;
119 #if 0
120  if (i + 1 == no_of_headers)
121  size += 2;
122 #endif
123  if (size + buf->len > buf->size) {
124  if (HttpHeaderExpandBuffer(hdr_td, buf, size) != 0) {
125  return NULL;
126  }
127  }
128 
129  memcpy(buf->buffer + buf->len, bstr_ptr(h->name), bstr_size(h->name));
130  buf->len += bstr_size(h->name);
131  buf->buffer[buf->len++] = ':';
132  buf->buffer[buf->len++] = ' ';
133  memcpy(buf->buffer + buf->len, bstr_ptr(h->value), bstr_size(h->value));
134  buf->len += bstr_size(h->value);
135  buf->buffer[buf->len++] = '\r';
136  buf->buffer[buf->len++] = '\n';
137 #if 0 // looks like this breaks existing rules
138  if (i + 1 == no_of_headers) {
139  buf->buffer[buf->len++] = '\r';
140  buf->buffer[buf->len++] = '\n';
141  }
142 #endif
143  }
144 
145  *buffer_len = buf->len;
146  return buf->buffer;
147 }
148 
149 /** \internal
150  * \brief custom inspect function to utilize the cached headers
151  */
152 static int DetectEngineInspectBufferHttpHeader(
153  DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
154  const DetectEngineAppInspectionEngine *engine,
155  const Signature *s,
156  Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
157 {
158  SCEnter();
159 
160  const int list_id = engine->sm_list;
161  InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
162  if (buffer->inspect == NULL) {
163  SCLogDebug("setting up inspect buffer %d", list_id);
164 
165  /* if prefilter didn't already run, we need to consider transformations */
166  const DetectEngineTransforms *transforms = NULL;
167  if (!engine->mpm) {
168  transforms = engine->v2.transforms;
169  }
170 
171  uint32_t rawdata_len = 0;
172  uint8_t *rawdata = GetBufferForTX(txv, tx_id, det_ctx,
173  f, flags, &rawdata_len);
174  if (rawdata_len == 0) {
175  SCLogDebug("no data");
176  goto end;
177  }
178  /* setup buffer and apply transforms */
179  InspectionBufferSetup(buffer, rawdata, rawdata_len);
180  InspectionBufferApplyTransforms(buffer, transforms);
181  }
182 
183  const uint32_t data_len = buffer->inspect_len;
184  const uint8_t *data = buffer->inspect;
185  const uint64_t offset = buffer->inspect_offset;
186 
187  det_ctx->discontinue_matching = 0;
188  det_ctx->buffer_offset = 0;
189  det_ctx->inspection_recursion_counter = 0;
190 
191  /* Inspect all the uricontents fetched on each
192  * transaction at the app layer */
193  int r = DetectEngineContentInspection(de_ctx, det_ctx,
194  s, engine->smd,
195  f,
196  (uint8_t *)data, data_len, offset, DETECT_CI_FLAGS_SINGLE,
198  SCLogDebug("r = %d", r);
199  if (r == 1) {
201  }
202 end:
203  if (flags & STREAM_TOSERVER) {
204  if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, txv, flags) > HTP_REQUEST_HEADERS)
206  } else {
207  if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, txv, flags) > HTP_RESPONSE_HEADERS)
209  }
211 }
212 
214  int list_id;
215  const MpmCtx *mpm_ctx;
218 
219 /** \brief Generic Mpm prefilter callback
220  *
221  * \param det_ctx detection engine thread ctx
222  * \param p packet to inspect
223  * \param f flow to inspect
224  * \param txv tx to inspect
225  * \param pectx inspection context
226  */
227 static void PrefilterMpmHttpHeader(DetectEngineThreadCtx *det_ctx,
228  const void *pectx,
229  Packet *p, Flow *f, void *txv,
230  const uint64_t idx, const uint8_t flags)
231 {
232  SCEnter();
233 
234  const PrefilterMpmHttpHeaderCtx *ctx = pectx;
235  const MpmCtx *mpm_ctx = ctx->mpm_ctx;
236  SCLogDebug("running on list %d", ctx->list_id);
237 
238  const int list_id = ctx->list_id;
239  InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
240  if (buffer->inspect == NULL) {
241  uint32_t rawdata_len = 0;
242  uint8_t *rawdata = GetBufferForTX(txv, idx, det_ctx,
243  f, flags, &rawdata_len);
244  if (rawdata_len == 0)
245  return;
246 
247  /* setup buffer and apply transforms */
248  InspectionBufferSetup(buffer, rawdata, rawdata_len);
250  }
251 
252  const uint32_t data_len = buffer->inspect_len;
253  const uint8_t *data = buffer->inspect;
254 
255  SCLogDebug("mpm'ing buffer:");
256  //PrintRawDataFp(stdout, data, data_len);
257 
258  if (data != NULL && data_len >= mpm_ctx->minlen) {
259  (void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx,
260  &det_ctx->mtcu, &det_ctx->pmq, data, data_len);
261  }
262 }
263 
264 static void PrefilterMpmHttpTrailer(DetectEngineThreadCtx *det_ctx,
265  const void *pectx,
266  Packet *p, Flow *f, void *txv,
267  const uint64_t idx, const uint8_t flags)
268 {
269  SCEnter();
270 
271  htp_tx_t *tx = txv;
272  const HtpTxUserData *htud = (const HtpTxUserData *)htp_tx_get_user_data(tx);
273  /* if the request wasn't flagged as having a trailer, we skip */
274  if (htud && (
275  ((flags & STREAM_TOSERVER) && !htud->request_has_trailers) ||
276  ((flags & STREAM_TOCLIENT) && !htud->response_has_trailers))) {
277  SCReturn;
278  }
279  PrefilterMpmHttpHeader(det_ctx, pectx, p, f, txv, idx, flags);
280  SCReturn;
281 }
282 
283 static void PrefilterMpmHttpHeaderFree(void *ptr)
284 {
285  SCFree(ptr);
286 }
287 
288 static int PrefilterMpmHttpHeaderRequestRegister(DetectEngineCtx *de_ctx,
289  SigGroupHead *sgh, MpmCtx *mpm_ctx,
290  const DetectMpmAppLayerRegistery *mpm_reg, int list_id)
291 {
292  SCEnter();
293 
294  /* header */
295  PrefilterMpmHttpHeaderCtx *pectx = SCCalloc(1, sizeof(*pectx));
296  if (pectx == NULL)
297  return -1;
298  pectx->list_id = list_id;
299  pectx->mpm_ctx = mpm_ctx;
300  pectx->transforms = &mpm_reg->v2.transforms;
301 
302  int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpHeader,
303  mpm_reg->v2.alproto, HTP_REQUEST_HEADERS,
304  pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
305  if (r != 0) {
306  SCFree(pectx);
307  return r;
308  }
309 
310  /* trailer */
311  pectx = SCCalloc(1, sizeof(*pectx));
312  if (pectx == NULL)
313  return -1;
314  pectx->list_id = list_id;
315  pectx->mpm_ctx = mpm_ctx;
316  pectx->transforms = &mpm_reg->v2.transforms;
317 
318  r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpTrailer,
319  mpm_reg->v2.alproto, HTP_REQUEST_TRAILER,
320  pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
321  if (r != 0) {
322  SCFree(pectx);
323  }
324  return r;
325 }
326 
327 static int PrefilterMpmHttpHeaderResponseRegister(DetectEngineCtx *de_ctx,
328  SigGroupHead *sgh, MpmCtx *mpm_ctx,
329  const DetectMpmAppLayerRegistery *mpm_reg, int list_id)
330 {
331  SCEnter();
332 
333  /* header */
334  PrefilterMpmHttpHeaderCtx *pectx = SCCalloc(1, sizeof(*pectx));
335  if (pectx == NULL)
336  return -1;
337  pectx->list_id = list_id;
338  pectx->mpm_ctx = mpm_ctx;
339  pectx->transforms = &mpm_reg->v2.transforms;
340 
341  int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpHeader,
342  mpm_reg->v2.alproto, HTP_RESPONSE_HEADERS,
343  pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
344  if (r != 0) {
345  SCFree(pectx);
346  return r;
347  }
348 
349  /* trailer */
350  pectx = SCCalloc(1, sizeof(*pectx));
351  if (pectx == NULL)
352  return -1;
353  pectx->list_id = list_id;
354  pectx->mpm_ctx = mpm_ctx;
355  pectx->transforms = &mpm_reg->v2.transforms;
356 
357  r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpTrailer,
358  mpm_reg->v2.alproto, HTP_RESPONSE_TRAILER,
359  pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
360  if (r != 0) {
361  SCFree(pectx);
362  }
363  return r;
364 }
365 
366 /**
367  * \brief The setup function for the http_header keyword for a signature.
368  *
369  * \param de_ctx Pointer to the detection engine context.
370  * \param s Pointer to signature for the current Signature being parsed
371  * from the rules.
372  * \param m Pointer to the head of the SigMatchs for the current rule
373  * being parsed.
374  * \param arg Pointer to the string holding the keyword value.
375  *
376  * \retval 0 On success.
377  * \retval -1 On failure.
378  */
379 static int DetectHttpHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
380 {
381  return DetectEngineContentModifierBufferSetup(de_ctx, s, arg,
383  g_http_header_buffer_id,
384  ALPROTO_HTTP);
385 }
386 
387 /**
388  * \brief this function setup the http.header keyword used in the rule
389  *
390  * \param de_ctx Pointer to the Detection Engine Context
391  * \param s Pointer to the Signature to which the current keyword belongs
392  * \param str Should hold an empty string always
393  *
394  * \retval 0 On success
395  */
396 static int DetectHttpHeaderSetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str)
397 {
398  if (DetectBufferSetActiveList(s, g_http_header_buffer_id) < 0)
399  return -1;
401  return -1;
402  return 0;
403 }
404 
405 /**
406  * \brief Registers the keyword handlers for the "http_header" keyword.
407  */
409 {
410  /* http_header content modifier */
411  sigmatch_table[DETECT_AL_HTTP_HEADER].name = "http_header";
412  sigmatch_table[DETECT_AL_HTTP_HEADER].desc = "content modifier to match only on the HTTP header-buffer";
413  sigmatch_table[DETECT_AL_HTTP_HEADER].url = DOC_URL DOC_VERSION "/rules/http-keywords.html#http-header-and-http-raw-header";
414  sigmatch_table[DETECT_AL_HTTP_HEADER].Setup = DetectHttpHeaderSetup;
415 #ifdef UNITTESTS
417 #endif
421 
422  /* http.header sticky buffer */
423  sigmatch_table[DETECT_HTTP_HEADER].name = "http.header";
424  sigmatch_table[DETECT_HTTP_HEADER].desc = "sticky buffer to match on the normalized HTTP header-buffer";
425  sigmatch_table[DETECT_HTTP_HEADER].url = DOC_URL DOC_VERSION "/rules/http-keywords.html#http-header";
426  sigmatch_table[DETECT_HTTP_HEADER].Setup = DetectHttpHeaderSetupSticky;
429 
431  SIG_FLAG_TOSERVER, HTP_REQUEST_HEADERS,
432  DetectEngineInspectBufferHttpHeader, NULL);
434  PrefilterMpmHttpHeaderRequestRegister, NULL, ALPROTO_HTTP,
435  0); /* not used, registered twice: HEADERS/TRAILER */
436 
438  SIG_FLAG_TOCLIENT, HTP_RESPONSE_HEADERS,
439  DetectEngineInspectBufferHttpHeader, NULL);
441  PrefilterMpmHttpHeaderResponseRegister, NULL, ALPROTO_HTTP,
442  0); /* not used, registered twice: HEADERS/TRAILER */
443 
445  "http headers");
446 
447  g_http_header_buffer_id = DetectBufferTypeGetByName("http_header");
448 
449  g_keyword_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_header",
451 }
452 
453 /************************************Unittests*********************************/
454 
455 #ifdef UNITTESTS
457 #endif
458 
459 /**
460  * @}
461  */
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect.h:1406
uint16_t flags
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1149
#define SCLogDebug(...)
Definition: util-debug.h:335
int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
uint16_t minlen
Definition: util-mpm.h:99
uint16_t discontinue_matching
Definition: detect.h:1029
void HttpHeaderThreadDataFree(void *data)
int HttpHeaderExpandBuffer(HttpHeaderThreadData *td, HttpHeaderBuffer *buf, uint32_t size)
int AppLayerParserGetStateProgress(uint8_t ipproto, AppProto alproto, void *alstate, uint8_t flags)
get the progress value for a tx/protocol
#define unlikely(expr)
Definition: util-optimize.h:35
const DetectEngineTransforms * transforms
one time registration of keywords at start up
Definition: detect.h:570
uint64_t offset
#define SIGMATCH_INFO_CONTENT_MODIFIER
Definition: detect.h:1353
void DetectHttpHeaderRegister(void)
Registers the keyword handlers for the "http_header" keyword.
Container for matching data for a signature group.
Definition: detect.h:1298
uint32_t buffer_offset
Definition: detect.h:994
InspectionBuffer * InspectionBufferGet(DetectEngineThreadCtx *det_ctx, const int list_id)
const char * name
Definition: detect.h:1163
Signature container.
Definition: detect.h:495
uint8_t response_has_trailers
struct DetectEngineAppInspectionEngine_::@92 v2
main detection engine ctx
Definition: detect.h:723
struct PrefilterMpmHttpHeaderCtx PrefilterMpmHttpHeaderCtx
int DetectBufferTypeGetByName(const char *name)
#define str(s)
#define SCCalloc(nm, a)
Definition: util-mem.h:197
#define SIG_FLAG_TOCLIENT
Definition: detect.h:242
#define SIGMATCH_INFO_STICKY_BUFFER
Definition: detect.h:1355
#define DETECT_ENGINE_INSPECT_SIG_MATCH
uint64_t inspect_offset
Definition: detect.h:349
Data structures and function prototypes for keeping state for the detection engine.
uint8_t mpm_type
Definition: util-mpm.h:90
HttpHeaderBuffer * HttpHeaderGetBufferSpaceForTXID(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, uint64_t tx_id, const int keyword_id, HttpHeaderThreadData **ret_hdr_td)
#define SIG_FLAG_TOSERVER
Definition: detect.h:241
#define SCEnter(...)
Definition: util-debug.h:337
void DetectAppLayerInspectEngineRegister2(const char *name, AppProto alproto, uint32_t dir, int progress, InspectEngineFuncPtr2 Callback2, InspectionBufferGetDataPtr GetData)
register inspect engine at start up time
PrefilterRuleStore pmq
Definition: detect.h:1064
uint16_t alternative
Definition: detect.h:1161
#define STREAM_TOCLIENT
Definition: stream.h:32
struct DetectMpmAppLayerRegistery_::@93 v2
void * HttpHeaderThreadDataInit(void *data)
int DetectEngineContentModifierBufferSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg, int sm_type, int sm_list, AppProto alproto)
Definition: detect-parse.c:143
const char * desc
Definition: detect.h:1165
#define DETECT_ENGINE_INSPECT_SIG_CANT_MATCH
#define BUFFER_SIZE_STEP
uint8_t request_has_trailers
#define DETECT_ENGINE_INSPECT_SIG_NO_MATCH
MpmTableElmt mpm_table[MPM_TABLE_SIZE]
Definition: util-mpm.h:169
#define SCFree(a)
Definition: util-mem.h:228
uint16_t tx_id
DetectEngineTransforms transforms
Definition: detect.h:588
void InspectionBufferSetup(InspectionBuffer *buffer, const uint8_t *data, const uint32_t data_len)
setup the buffer with our initial data
MpmThreadCtx mtcu
Definition: detect.h:1062
void InspectionBufferApplyTransforms(InspectionBuffer *buffer, const DetectEngineTransforms *transforms)
#define SIGMATCH_NOOPT
Definition: detect.h:1331
int DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Flow *f, uint8_t *buffer, uint32_t buffer_len, uint32_t stream_start_offset, uint8_t flags, uint8_t inspection_mode, void *data)
Run the actual payload match functions.
const char * url
Definition: detect.h:1166
#define STREAM_TOSERVER
Definition: stream.h:31
void DetectHttpHeaderRegisterTests(void)
int inspection_recursion_counter
Definition: detect.h:1041
#define DETECT_CI_FLAGS_SINGLE
uint32_t inspect_len
Definition: detect.h:348
int DetectBufferSetActiveList(Signature *s, const int list)
const uint8_t * inspect
Definition: detect.h:347
#define DOC_URL
Definition: suricata.h:86
void DetectBufferTypeSetDescriptionByName(const char *name, const char *desc)
uint32_t(* Search)(const struct MpmCtx_ *, struct MpmThreadCtx_ *, PrefilterRuleStore *, const uint8_t *, uint32_t)
Definition: util-mpm.h:162
#define SCReturn
Definition: util-debug.h:339
int PrefilterAppendTxEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, void(*PrefilterTxFunc)(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f, void *tx, const uint64_t idx, const uint8_t flags), AppProto alproto, int tx_min_progress, void *pectx, void(*FreeFunc)(void *pectx), const char *name)
#define BUFFER_TX_STEP
#define DOC_VERSION
Definition: suricata.h:91
int DetectRegisterThreadCtxGlobalFuncs(const char *name, void *(*InitFunc)(void *), void *data, void(*FreeFunc)(void *))
Register Thread keyword context Funcs (Global)
uint16_t flags
Definition: detect.h:1157
Flow data structure.
Definition: flow.h:324
void(* RegisterTests)(void)
Definition: detect.h:1155
const DetectEngineTransforms * transforms
Definition: detect.h:419
void DetectAppLayerMpmRegister2(const char *name, int direction, int priority, int(*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, const DetectMpmAppLayerRegistery *mpm_reg, int list_id), InspectionBufferGetDataPtr GetData, AppProto alproto, int tx_min_progress)
register a MPM engine