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, s, engine->smd,
194  NULL, f, (uint8_t *)data, data_len, offset,
196  SCLogDebug("r = %d", r);
197  if (r == 1) {
199  }
200 end:
201  if (flags & STREAM_TOSERVER) {
202  if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, txv, flags) > HTP_REQUEST_HEADERS)
204  } else {
205  if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP, txv, flags) > HTP_RESPONSE_HEADERS)
207  }
209 }
210 
211 typedef struct PrefilterMpmHttpHeaderCtx {
212  int list_id;
213  const MpmCtx *mpm_ctx;
216 
217 /** \brief Generic Mpm prefilter callback
218  *
219  * \param det_ctx detection engine thread ctx
220  * \param p packet to inspect
221  * \param f flow to inspect
222  * \param txv tx to inspect
223  * \param pectx inspection context
224  */
225 static void PrefilterMpmHttpHeader(DetectEngineThreadCtx *det_ctx,
226  const void *pectx,
227  Packet *p, Flow *f, void *txv,
228  const uint64_t idx, const uint8_t flags)
229 {
230  SCEnter();
231 
232  const PrefilterMpmHttpHeaderCtx *ctx = pectx;
233  const MpmCtx *mpm_ctx = ctx->mpm_ctx;
234  SCLogDebug("running on list %d", ctx->list_id);
235 
236  const int list_id = ctx->list_id;
237  InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
238  if (buffer->inspect == NULL) {
239  uint32_t rawdata_len = 0;
240  uint8_t *rawdata = GetBufferForTX(txv, idx, det_ctx,
241  f, flags, &rawdata_len);
242  if (rawdata_len == 0)
243  return;
244 
245  /* setup buffer and apply transforms */
246  InspectionBufferSetup(buffer, rawdata, rawdata_len);
248  }
249 
250  const uint32_t data_len = buffer->inspect_len;
251  const uint8_t *data = buffer->inspect;
252 
253  SCLogDebug("mpm'ing buffer:");
254  //PrintRawDataFp(stdout, data, data_len);
255 
256  if (data != NULL && data_len >= mpm_ctx->minlen) {
257  (void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx,
258  &det_ctx->mtcu, &det_ctx->pmq, data, data_len);
259  }
260 }
261 
262 static void PrefilterMpmHttpTrailer(DetectEngineThreadCtx *det_ctx,
263  const void *pectx,
264  Packet *p, Flow *f, void *txv,
265  const uint64_t idx, const uint8_t flags)
266 {
267  SCEnter();
268 
269  htp_tx_t *tx = txv;
270  const HtpTxUserData *htud = (const HtpTxUserData *)htp_tx_get_user_data(tx);
271  /* if the request wasn't flagged as having a trailer, we skip */
272  if (htud && (
273  ((flags & STREAM_TOSERVER) && !htud->request_has_trailers) ||
274  ((flags & STREAM_TOCLIENT) && !htud->response_has_trailers))) {
275  SCReturn;
276  }
277  PrefilterMpmHttpHeader(det_ctx, pectx, p, f, txv, idx, flags);
278  SCReturn;
279 }
280 
281 static void PrefilterMpmHttpHeaderFree(void *ptr)
282 {
283  SCFree(ptr);
284 }
285 
286 static int PrefilterMpmHttpHeaderRequestRegister(DetectEngineCtx *de_ctx,
287  SigGroupHead *sgh, MpmCtx *mpm_ctx,
288  const DetectBufferMpmRegistery *mpm_reg, int list_id)
289 {
290  SCEnter();
291 
292  /* header */
293  PrefilterMpmHttpHeaderCtx *pectx = SCCalloc(1, sizeof(*pectx));
294  if (pectx == NULL)
295  return -1;
296  pectx->list_id = list_id;
297  pectx->mpm_ctx = mpm_ctx;
298  pectx->transforms = &mpm_reg->transforms;
299 
300  int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpHeader,
301  mpm_reg->app_v2.alproto, HTP_REQUEST_HEADERS,
302  pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
303  if (r != 0) {
304  SCFree(pectx);
305  return r;
306  }
307 
308  /* trailer */
309  pectx = SCCalloc(1, sizeof(*pectx));
310  if (pectx == NULL)
311  return -1;
312  pectx->list_id = list_id;
313  pectx->mpm_ctx = mpm_ctx;
314  pectx->transforms = &mpm_reg->transforms;
315 
316  r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpTrailer,
317  mpm_reg->app_v2.alproto, HTP_REQUEST_TRAILER,
318  pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
319  if (r != 0) {
320  SCFree(pectx);
321  }
322  return r;
323 }
324 
325 static int PrefilterMpmHttpHeaderResponseRegister(DetectEngineCtx *de_ctx,
326  SigGroupHead *sgh, MpmCtx *mpm_ctx,
327  const DetectBufferMpmRegistery *mpm_reg, int list_id)
328 {
329  SCEnter();
330 
331  /* header */
332  PrefilterMpmHttpHeaderCtx *pectx = SCCalloc(1, sizeof(*pectx));
333  if (pectx == NULL)
334  return -1;
335  pectx->list_id = list_id;
336  pectx->mpm_ctx = mpm_ctx;
337  pectx->transforms = &mpm_reg->transforms;
338 
339  int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpHeader,
340  mpm_reg->app_v2.alproto, HTP_RESPONSE_HEADERS,
341  pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
342  if (r != 0) {
343  SCFree(pectx);
344  return r;
345  }
346 
347  /* trailer */
348  pectx = SCCalloc(1, sizeof(*pectx));
349  if (pectx == NULL)
350  return -1;
351  pectx->list_id = list_id;
352  pectx->mpm_ctx = mpm_ctx;
353  pectx->transforms = &mpm_reg->transforms;
354 
355  r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpTrailer,
356  mpm_reg->app_v2.alproto, HTP_RESPONSE_TRAILER,
357  pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname);
358  if (r != 0) {
359  SCFree(pectx);
360  }
361  return r;
362 }
363 
364 /**
365  * \brief The setup function for the http_header keyword for a signature.
366  *
367  * \param de_ctx Pointer to the detection engine context.
368  * \param s Pointer to signature for the current Signature being parsed
369  * from the rules.
370  * \param m Pointer to the head of the SigMatchs for the current rule
371  * being parsed.
372  * \param arg Pointer to the string holding the keyword value.
373  *
374  * \retval 0 On success.
375  * \retval -1 On failure.
376  */
377 static int DetectHttpHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
378 {
379  return DetectEngineContentModifierBufferSetup(de_ctx, s, arg,
381  g_http_header_buffer_id,
382  ALPROTO_HTTP);
383 }
384 
385 /**
386  * \brief this function setup the http.header keyword used in the rule
387  *
388  * \param de_ctx Pointer to the Detection Engine Context
389  * \param s Pointer to the Signature to which the current keyword belongs
390  * \param str Should hold an empty string always
391  *
392  * \retval 0 On success
393  */
394 static int DetectHttpHeaderSetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str)
395 {
396  if (DetectBufferSetActiveList(s, g_http_header_buffer_id) < 0)
397  return -1;
399  return -1;
400  return 0;
401 }
402 
403 /**
404  * \brief Registers the keyword handlers for the "http_header" keyword.
405  */
407 {
408  /* http_header content modifier */
409  sigmatch_table[DETECT_AL_HTTP_HEADER].name = "http_header";
410  sigmatch_table[DETECT_AL_HTTP_HEADER].desc = "content modifier to match only on the HTTP header-buffer";
411  sigmatch_table[DETECT_AL_HTTP_HEADER].url = DOC_URL DOC_VERSION "/rules/http-keywords.html#http-header-and-http-raw-header";
412  sigmatch_table[DETECT_AL_HTTP_HEADER].Setup = DetectHttpHeaderSetup;
413 #ifdef UNITTESTS
415 #endif
419 
420  /* http.header sticky buffer */
421  sigmatch_table[DETECT_HTTP_HEADER].name = "http.header";
422  sigmatch_table[DETECT_HTTP_HEADER].desc = "sticky buffer to match on the normalized HTTP header-buffer";
423  sigmatch_table[DETECT_HTTP_HEADER].url = DOC_URL DOC_VERSION "/rules/http-keywords.html#http-header";
424  sigmatch_table[DETECT_HTTP_HEADER].Setup = DetectHttpHeaderSetupSticky;
427 
429  SIG_FLAG_TOSERVER, HTP_REQUEST_HEADERS,
430  DetectEngineInspectBufferHttpHeader, NULL);
432  PrefilterMpmHttpHeaderRequestRegister, NULL, ALPROTO_HTTP,
433  0); /* not used, registered twice: HEADERS/TRAILER */
434 
436  SIG_FLAG_TOCLIENT, HTP_RESPONSE_HEADERS,
437  DetectEngineInspectBufferHttpHeader, NULL);
439  PrefilterMpmHttpHeaderResponseRegister, NULL, ALPROTO_HTTP,
440  0); /* not used, registered twice: HEADERS/TRAILER */
441 
443  "http headers");
444 
445  g_http_header_buffer_id = DetectBufferTypeGetByName("http_header");
446 
447  g_keyword_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_header",
449 }
450 
451 /************************************Unittests*********************************/
452 
453 #ifdef UNITTESTS
455 #endif
456 
457 /**
458  * @}
459  */
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect.h:1439
uint16_t flags
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1179
#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:1060
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
DetectEngineTransforms transforms
Definition: detect.h:613
int DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Packet *p, Flow *f, uint8_t *buffer, uint32_t buffer_len, uint32_t stream_start_offset, uint8_t flags, uint8_t inspection_mode)
Run the actual payload match functions.
uint64_t offset
#define SIGMATCH_INFO_CONTENT_MODIFIER
Definition: detect.h:1384
void DetectHttpHeaderRegister(void)
Registers the keyword handlers for the "http_header" keyword.
Container for matching data for a signature group.
Definition: detect.h:1329
uint32_t buffer_offset
Definition: detect.h:1025
InspectionBuffer * InspectionBufferGet(DetectEngineThreadCtx *det_ctx, const int list_id)
const char * name
Definition: detect.h:1193
Signature container.
Definition: detect.h:517
void DetectAppLayerMpmRegister2(const char *name, int direction, int priority, int(*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, const DetectBufferMpmRegistery *mpm_reg, int list_id), InspectionBufferGetDataPtr GetData, AppProto alproto, int tx_min_progress)
register a MPM engine
uint8_t response_has_trailers
struct DetectEngineAppInspectionEngine_::@92 v2
main detection engine ctx
Definition: detect.h:756
const DetectEngineTransforms * transforms
struct PrefilterMpmHttpHeaderCtx PrefilterMpmHttpHeaderCtx
int DetectBufferTypeGetByName(const char *name)
#define str(s)
#define SCCalloc(nm, a)
Definition: util-mem.h:253
#define SIG_FLAG_TOCLIENT
Definition: detect.h:233
#define SIGMATCH_INFO_STICKY_BUFFER
Definition: detect.h:1386
#define DETECT_ENGINE_INSPECT_SIG_MATCH
uint64_t inspect_offset
Definition: detect.h:339
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)
one time registration of keywords at start up
Definition: detect.h:600
#define SIG_FLAG_TOSERVER
Definition: detect.h:232
#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:1095
uint16_t alternative
Definition: detect.h:1191
#define STREAM_TOCLIENT
Definition: stream.h:32
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:1195
#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:322
uint16_t tx_id
struct DetectBufferMpmRegistery_::@94::@96 app_v2
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:1093
void InspectionBufferApplyTransforms(InspectionBuffer *buffer, const DetectEngineTransforms *transforms)
#define SIGMATCH_NOOPT
Definition: detect.h:1362
const char * url
Definition: detect.h:1196
#define STREAM_TOSERVER
Definition: stream.h:31
void DetectHttpHeaderRegisterTests(void)
int inspection_recursion_counter
Definition: detect.h:1072
#define DETECT_CI_FLAGS_SINGLE
uint32_t inspect_len
Definition: detect.h:340
int DetectBufferSetActiveList(Signature *s, const int list)
const uint8_t * inspect
Definition: detect.h:338
#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:1187
Flow data structure.
Definition: flow.h:325
void(* RegisterTests)(void)
Definition: detect.h:1185
const DetectEngineTransforms * transforms
Definition: detect.h:411