/src/suricata7/src/detect-http-header.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2022 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" |
42 | | #include "detect-engine-prefilter.h" |
43 | | #include "detect-engine-content-inspection.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 | | #include "util-profiling.h" |
51 | | #include "util-validate.h" |
52 | | |
53 | | #include "app-layer.h" |
54 | | #include "app-layer-parser.h" |
55 | | |
56 | | #include "app-layer-htp.h" |
57 | | #include "detect-http-header.h" |
58 | | #include "detect-http-header-common.h" |
59 | | |
60 | | static int DetectHttpHeaderSetup(DetectEngineCtx *, Signature *, const char *); |
61 | | #ifdef UNITTESTS |
62 | | static void DetectHttpHeaderRegisterTests(void); |
63 | | #endif |
64 | | static int g_http_header_buffer_id = 0; |
65 | | static int g_keyword_thread_id = 0; |
66 | | static int g_http2_thread_id = 0; |
67 | | |
68 | | #define BUFFER_SIZE_STEP 1024 |
69 | | static HttpHeaderThreadDataConfig g_td_config = { BUFFER_SIZE_STEP }; |
70 | | |
71 | | static uint8_t *GetBufferForTX( |
72 | | htp_tx_t *tx, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, uint32_t *buffer_len) |
73 | 20.6k | { |
74 | 20.6k | *buffer_len = 0; |
75 | | |
76 | 20.6k | HttpHeaderThreadData *hdr_td = NULL; |
77 | 20.6k | HttpHeaderBuffer *buf = |
78 | 20.6k | HttpHeaderGetBufferSpace(det_ctx, f, flags, g_keyword_thread_id, &hdr_td); |
79 | 20.6k | if (unlikely(buf == NULL)) { |
80 | 0 | return NULL; |
81 | 0 | } |
82 | | |
83 | 20.6k | htp_table_t *headers; |
84 | 20.6k | if (flags & STREAM_TOSERVER) { |
85 | 13.1k | if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, tx, flags) <= |
86 | 13.1k | HTP_REQUEST_HEADERS) |
87 | 3.09k | return NULL; |
88 | 10.0k | headers = tx->request_headers; |
89 | 10.0k | } else { |
90 | 7.50k | if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, tx, flags) <= |
91 | 7.50k | HTP_RESPONSE_HEADERS) |
92 | 1.32k | return NULL; |
93 | 6.17k | headers = tx->response_headers; |
94 | 6.17k | } |
95 | 16.2k | if (headers == NULL) |
96 | 0 | return NULL; |
97 | | |
98 | 16.2k | size_t i = 0; |
99 | 16.2k | size_t no_of_headers = htp_table_size(headers); |
100 | 62.1k | for (; i < no_of_headers; i++) { |
101 | 45.8k | htp_header_t *h = htp_table_get_index(headers, i, NULL); |
102 | 45.8k | size_t size1 = bstr_size(h->name); |
103 | 45.8k | size_t size2 = bstr_size(h->value); |
104 | | |
105 | 45.8k | if (flags & STREAM_TOSERVER) { |
106 | 26.3k | if (size1 == 6 && |
107 | 2.44k | SCMemcmpLowercase("cookie", bstr_ptr(h->name), 6) == 0) { |
108 | 212 | continue; |
109 | 212 | } |
110 | 26.3k | } else { |
111 | 19.5k | if (size1 == 10 && |
112 | 1.52k | SCMemcmpLowercase("set-cookie", bstr_ptr(h->name), 10) == 0) { |
113 | 10 | continue; |
114 | 10 | } |
115 | 19.5k | } |
116 | | |
117 | 45.6k | size_t size = size1 + size2 + 4; |
118 | | #if 0 |
119 | | if (i + 1 == no_of_headers) |
120 | | size += 2; |
121 | | #endif |
122 | 45.6k | if (size + buf->len > buf->size) { |
123 | 113 | if (HttpHeaderExpandBuffer(hdr_td, buf, size) != 0) { |
124 | 0 | return NULL; |
125 | 0 | } |
126 | 113 | } |
127 | | |
128 | 45.6k | memcpy(buf->buffer + buf->len, bstr_ptr(h->name), bstr_size(h->name)); |
129 | 45.6k | buf->len += bstr_size(h->name); |
130 | 45.6k | buf->buffer[buf->len++] = ':'; |
131 | 45.6k | buf->buffer[buf->len++] = ' '; |
132 | 45.6k | memcpy(buf->buffer + buf->len, bstr_ptr(h->value), bstr_size(h->value)); |
133 | 45.6k | buf->len += bstr_size(h->value); |
134 | 45.6k | buf->buffer[buf->len++] = '\r'; |
135 | 45.6k | buf->buffer[buf->len++] = '\n'; |
136 | | #if 0 // looks like this breaks existing rules |
137 | | if (i + 1 == no_of_headers) { |
138 | | buf->buffer[buf->len++] = '\r'; |
139 | | buf->buffer[buf->len++] = '\n'; |
140 | | } |
141 | | #endif |
142 | 45.6k | } |
143 | | |
144 | 16.2k | *buffer_len = buf->len; |
145 | 16.2k | return buf->buffer; |
146 | 16.2k | } |
147 | | |
148 | | static InspectionBuffer *GetBuffer2ForTX(DetectEngineThreadCtx *det_ctx, |
149 | | const DetectEngineTransforms *transforms, Flow *_f, const uint8_t flow_flags, void *txv, |
150 | | const int list_id) |
151 | 1.77k | { |
152 | 1.77k | InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); |
153 | 1.77k | if (buffer->inspect == NULL) { |
154 | 1.76k | uint32_t b_len = 0; |
155 | 1.76k | const uint8_t *b = NULL; |
156 | | |
157 | 1.76k | void *thread_buf = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_thread_id); |
158 | 1.76k | if (thread_buf == NULL) |
159 | 0 | return NULL; |
160 | 1.76k | if (SCHttp2TxGetHeaders(txv, flow_flags, &b, &b_len, thread_buf) != 1) |
161 | 1.15k | return NULL; |
162 | 615 | if (b == NULL || b_len == 0) |
163 | 0 | return NULL; |
164 | | |
165 | 615 | InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len); |
166 | 615 | InspectionBufferApplyTransforms(buffer, transforms); |
167 | 615 | } |
168 | | |
169 | 617 | return buffer; |
170 | 1.77k | } |
171 | | |
172 | | /** \internal |
173 | | * \brief custom inspect function to utilize the cached headers |
174 | | */ |
175 | | static uint8_t DetectEngineInspectBufferHttpHeader(DetectEngineCtx *de_ctx, |
176 | | DetectEngineThreadCtx *det_ctx, const DetectEngineAppInspectionEngine *engine, |
177 | | const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id) |
178 | 15.4k | { |
179 | 15.4k | SCEnter(); |
180 | | |
181 | 15.4k | const int list_id = engine->sm_list; |
182 | 15.4k | InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); |
183 | 15.4k | if (buffer->inspect == NULL) { |
184 | 12.0k | SCLogDebug("setting up inspect buffer %d", list_id); |
185 | | |
186 | | /* if prefilter didn't already run, we need to consider transformations */ |
187 | 12.0k | const DetectEngineTransforms *transforms = NULL; |
188 | 12.0k | if (!engine->mpm) { |
189 | 8.25k | transforms = engine->v2.transforms; |
190 | 8.25k | } |
191 | | |
192 | 12.0k | uint32_t rawdata_len = 0; |
193 | 12.0k | uint8_t *rawdata = GetBufferForTX(txv, det_ctx, f, flags, &rawdata_len); |
194 | 12.0k | if (rawdata_len == 0) { |
195 | 5.74k | SCLogDebug("no data"); |
196 | 5.74k | goto end; |
197 | 5.74k | } |
198 | | /* setup buffer and apply transforms */ |
199 | 6.34k | InspectionBufferSetup(det_ctx, list_id, buffer, rawdata, rawdata_len); |
200 | 6.34k | InspectionBufferApplyTransforms(buffer, transforms); |
201 | 6.34k | } |
202 | | |
203 | 9.69k | const uint32_t data_len = buffer->inspect_len; |
204 | 9.69k | const uint8_t *data = buffer->inspect; |
205 | 9.69k | const uint64_t offset = buffer->inspect_offset; |
206 | | |
207 | 9.69k | det_ctx->discontinue_matching = 0; |
208 | 9.69k | det_ctx->buffer_offset = 0; |
209 | 9.69k | det_ctx->inspection_recursion_counter = 0; |
210 | | |
211 | | /* Inspect all the uricontents fetched on each |
212 | | * transaction at the app layer */ |
213 | 9.69k | int r = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, |
214 | 9.69k | NULL, f, (uint8_t *)data, data_len, offset, |
215 | 9.69k | DETECT_CI_FLAGS_SINGLE, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); |
216 | 9.69k | SCLogDebug("r = %d", r); |
217 | 9.69k | if (r == 1) { |
218 | 6.50k | return DETECT_ENGINE_INSPECT_SIG_MATCH; |
219 | 6.50k | } |
220 | 8.92k | end: |
221 | 8.92k | if (flags & STREAM_TOSERVER) { |
222 | 4.64k | if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, txv, flags) > |
223 | 4.64k | HTP_REQUEST_HEADERS) |
224 | 2.84k | return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH; |
225 | 4.64k | } else { |
226 | 4.28k | if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, txv, flags) > |
227 | 4.28k | HTP_RESPONSE_HEADERS) |
228 | 3.40k | return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH; |
229 | 4.28k | } |
230 | 2.67k | return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; |
231 | 8.92k | } |
232 | | |
233 | | typedef struct PrefilterMpmHttpHeaderCtx { |
234 | | int list_id; |
235 | | const MpmCtx *mpm_ctx; |
236 | | const DetectEngineTransforms *transforms; |
237 | | } PrefilterMpmHttpHeaderCtx; |
238 | | |
239 | | /** \brief Generic Mpm prefilter callback |
240 | | * |
241 | | * \param det_ctx detection engine thread ctx |
242 | | * \param p packet to inspect |
243 | | * \param f flow to inspect |
244 | | * \param txv tx to inspect |
245 | | * \param pectx inspection context |
246 | | */ |
247 | | static void PrefilterMpmHttpHeader(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, |
248 | | Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags) |
249 | 15.2k | { |
250 | 15.2k | SCEnter(); |
251 | | |
252 | 15.2k | const PrefilterMpmHttpHeaderCtx *ctx = pectx; |
253 | 15.2k | const MpmCtx *mpm_ctx = ctx->mpm_ctx; |
254 | 15.2k | SCLogDebug("running on list %d", ctx->list_id); |
255 | | |
256 | 15.2k | const int list_id = ctx->list_id; |
257 | 15.2k | InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); |
258 | 15.2k | if (buffer->inspect == NULL) { |
259 | 14.8k | uint32_t rawdata_len = 0; |
260 | 14.8k | uint8_t *rawdata = GetBufferForTX(txv, det_ctx, f, flags, &rawdata_len); |
261 | 14.8k | if (rawdata_len == 0) |
262 | 7.87k | return; |
263 | | |
264 | | /* setup buffer and apply transforms */ |
265 | 7.01k | InspectionBufferSetup(det_ctx, list_id, buffer, rawdata, rawdata_len); |
266 | 7.01k | InspectionBufferApplyTransforms(buffer, ctx->transforms); |
267 | 7.01k | } |
268 | | |
269 | 7.37k | const uint32_t data_len = buffer->inspect_len; |
270 | 7.37k | const uint8_t *data = buffer->inspect; |
271 | | |
272 | 7.37k | SCLogDebug("mpm'ing buffer:"); |
273 | | //PrintRawDataFp(stdout, data, data_len); |
274 | | |
275 | 7.37k | if (data != NULL && data_len >= mpm_ctx->minlen) { |
276 | 6.13k | (void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx, |
277 | 6.13k | &det_ctx->mtcu, &det_ctx->pmq, data, data_len); |
278 | 6.13k | PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len); |
279 | 6.13k | } |
280 | 7.37k | } |
281 | | |
282 | | static void PrefilterMpmHttpTrailer(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, |
283 | | Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags) |
284 | 6.75k | { |
285 | 6.75k | SCEnter(); |
286 | | |
287 | 6.75k | htp_tx_t *tx = txv; |
288 | 6.75k | const HtpTxUserData *htud = (const HtpTxUserData *)htp_tx_get_user_data(tx); |
289 | | /* if the request wasn't flagged as having a trailer, we skip */ |
290 | 6.75k | if (htud && ( |
291 | 6.75k | ((flags & STREAM_TOSERVER) && !htud->request_has_trailers) || |
292 | 6.67k | ((flags & STREAM_TOCLIENT) && !htud->response_has_trailers))) { |
293 | 6.67k | SCReturn; |
294 | 6.67k | } |
295 | 82 | PrefilterMpmHttpHeader(det_ctx, pectx, p, f, txv, idx, _txd, flags); |
296 | 82 | SCReturn; |
297 | 6.75k | } |
298 | | |
299 | | static void PrefilterMpmHttpHeaderFree(void *ptr) |
300 | 12.1k | { |
301 | 12.1k | SCFree(ptr); |
302 | 12.1k | } |
303 | | |
304 | | static int PrefilterMpmHttpHeaderRequestRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, |
305 | | MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id) |
306 | 2.85k | { |
307 | 2.85k | SCEnter(); |
308 | | |
309 | | /* header */ |
310 | 2.85k | PrefilterMpmHttpHeaderCtx *pectx = SCCalloc(1, sizeof(*pectx)); |
311 | 2.85k | if (pectx == NULL) |
312 | 0 | return -1; |
313 | 2.85k | pectx->list_id = list_id; |
314 | 2.85k | pectx->mpm_ctx = mpm_ctx; |
315 | 2.85k | pectx->transforms = &mpm_reg->transforms; |
316 | | |
317 | 2.85k | int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpHeader, |
318 | 2.85k | mpm_reg->app_v2.alproto, HTP_REQUEST_HEADERS, |
319 | 2.85k | pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname); |
320 | 2.85k | if (r != 0) { |
321 | 0 | SCFree(pectx); |
322 | 0 | return r; |
323 | 0 | } |
324 | | |
325 | | /* trailer */ |
326 | 2.85k | pectx = SCCalloc(1, sizeof(*pectx)); |
327 | 2.85k | if (pectx == NULL) |
328 | 0 | return -1; |
329 | 2.85k | pectx->list_id = list_id; |
330 | 2.85k | pectx->mpm_ctx = mpm_ctx; |
331 | 2.85k | pectx->transforms = &mpm_reg->transforms; |
332 | | |
333 | 2.85k | r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpTrailer, |
334 | 2.85k | mpm_reg->app_v2.alproto, HTP_REQUEST_TRAILER, |
335 | 2.85k | pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname); |
336 | 2.85k | if (r != 0) { |
337 | 0 | SCFree(pectx); |
338 | 0 | } |
339 | 2.85k | return r; |
340 | 2.85k | } |
341 | | |
342 | | static int PrefilterMpmHttpHeaderResponseRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, |
343 | | MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id) |
344 | 1.57k | { |
345 | 1.57k | SCEnter(); |
346 | | |
347 | | /* header */ |
348 | 1.57k | PrefilterMpmHttpHeaderCtx *pectx = SCCalloc(1, sizeof(*pectx)); |
349 | 1.57k | if (pectx == NULL) |
350 | 0 | return -1; |
351 | 1.57k | pectx->list_id = list_id; |
352 | 1.57k | pectx->mpm_ctx = mpm_ctx; |
353 | 1.57k | pectx->transforms = &mpm_reg->transforms; |
354 | | |
355 | 1.57k | int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpHeader, |
356 | 1.57k | mpm_reg->app_v2.alproto, HTP_RESPONSE_HEADERS, |
357 | 1.57k | pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname); |
358 | 1.57k | if (r != 0) { |
359 | 0 | SCFree(pectx); |
360 | 0 | return r; |
361 | 0 | } |
362 | | |
363 | | /* trailer */ |
364 | 1.57k | pectx = SCCalloc(1, sizeof(*pectx)); |
365 | 1.57k | if (pectx == NULL) |
366 | 0 | return -1; |
367 | 1.57k | pectx->list_id = list_id; |
368 | 1.57k | pectx->mpm_ctx = mpm_ctx; |
369 | 1.57k | pectx->transforms = &mpm_reg->transforms; |
370 | | |
371 | 1.57k | r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpTrailer, |
372 | 1.57k | mpm_reg->app_v2.alproto, HTP_RESPONSE_TRAILER, |
373 | 1.57k | pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname); |
374 | 1.57k | if (r != 0) { |
375 | 0 | SCFree(pectx); |
376 | 0 | } |
377 | 1.57k | return r; |
378 | 1.57k | } |
379 | | |
380 | | /** |
381 | | * \brief The setup function for the http_header keyword for a signature. |
382 | | * |
383 | | * \param de_ctx Pointer to the detection engine context. |
384 | | * \param s Pointer to signature for the current Signature being parsed |
385 | | * from the rules. |
386 | | * \param m Pointer to the head of the SigMatchs for the current rule |
387 | | * being parsed. |
388 | | * \param arg Pointer to the string holding the keyword value. |
389 | | * |
390 | | * \retval 0 On success. |
391 | | * \retval -1 On failure. |
392 | | */ |
393 | | static int DetectHttpHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) |
394 | 1.79k | { |
395 | 1.79k | return DetectEngineContentModifierBufferSetup( |
396 | 1.79k | de_ctx, s, arg, DETECT_AL_HTTP_HEADER, g_http_header_buffer_id, ALPROTO_HTTP1); |
397 | 1.79k | } |
398 | | |
399 | | /** |
400 | | * \brief this function setup the http.header keyword used in the rule |
401 | | * |
402 | | * \param de_ctx Pointer to the Detection Engine Context |
403 | | * \param s Pointer to the Signature to which the current keyword belongs |
404 | | * \param str Should hold an empty string always |
405 | | * |
406 | | * \retval 0 On success |
407 | | */ |
408 | | static int DetectHttpHeaderSetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str) |
409 | 6.38k | { |
410 | 6.38k | if (DetectBufferSetActiveList(de_ctx, s, g_http_header_buffer_id) < 0) |
411 | 137 | return -1; |
412 | 6.25k | if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0) |
413 | 97 | return -1; |
414 | 6.15k | return 0; |
415 | 6.25k | } |
416 | | |
417 | | /** |
418 | | * \brief Registers the keyword handlers for the "http_header" keyword. |
419 | | */ |
420 | | void DetectHttpHeaderRegister(void) |
421 | 74 | { |
422 | | /* http_header content modifier */ |
423 | 74 | sigmatch_table[DETECT_AL_HTTP_HEADER].name = "http_header"; |
424 | 74 | sigmatch_table[DETECT_AL_HTTP_HEADER].desc = "content modifier to match only on the HTTP header-buffer"; |
425 | 74 | sigmatch_table[DETECT_AL_HTTP_HEADER].url = "/rules/http-keywords.html#http-header-and-http-raw-header"; |
426 | 74 | sigmatch_table[DETECT_AL_HTTP_HEADER].Setup = DetectHttpHeaderSetup; |
427 | | #ifdef UNITTESTS |
428 | | sigmatch_table[DETECT_AL_HTTP_HEADER].RegisterTests = DetectHttpHeaderRegisterTests; |
429 | | #endif |
430 | 74 | sigmatch_table[DETECT_AL_HTTP_HEADER].flags |= SIGMATCH_NOOPT ; |
431 | 74 | sigmatch_table[DETECT_AL_HTTP_HEADER].flags |= SIGMATCH_INFO_CONTENT_MODIFIER; |
432 | 74 | sigmatch_table[DETECT_AL_HTTP_HEADER].alternative = DETECT_HTTP_HEADER; |
433 | | |
434 | | /* http.header sticky buffer */ |
435 | 74 | sigmatch_table[DETECT_HTTP_HEADER].name = "http.header"; |
436 | 74 | sigmatch_table[DETECT_HTTP_HEADER].desc = "sticky buffer to match on the normalized HTTP header-buffer"; |
437 | 74 | sigmatch_table[DETECT_HTTP_HEADER].url = "/rules/http-keywords.html#http-header-and-http-raw-header"; |
438 | 74 | sigmatch_table[DETECT_HTTP_HEADER].Setup = DetectHttpHeaderSetupSticky; |
439 | 74 | sigmatch_table[DETECT_HTTP_HEADER].flags |= SIGMATCH_NOOPT; |
440 | 74 | sigmatch_table[DETECT_HTTP_HEADER].flags |= SIGMATCH_INFO_STICKY_BUFFER; |
441 | | |
442 | 74 | DetectAppLayerInspectEngineRegister2("http_header", ALPROTO_HTTP1, SIG_FLAG_TOSERVER, |
443 | 74 | HTP_REQUEST_HEADERS, DetectEngineInspectBufferHttpHeader, NULL); |
444 | 74 | DetectAppLayerMpmRegister2("http_header", SIG_FLAG_TOSERVER, 2, |
445 | 74 | PrefilterMpmHttpHeaderRequestRegister, NULL, ALPROTO_HTTP1, |
446 | 74 | 0); /* not used, registered twice: HEADERS/TRAILER */ |
447 | | |
448 | 74 | DetectAppLayerInspectEngineRegister2("http_header", ALPROTO_HTTP1, SIG_FLAG_TOCLIENT, |
449 | 74 | HTP_RESPONSE_HEADERS, DetectEngineInspectBufferHttpHeader, NULL); |
450 | 74 | DetectAppLayerMpmRegister2("http_header", SIG_FLAG_TOCLIENT, 2, |
451 | 74 | PrefilterMpmHttpHeaderResponseRegister, NULL, ALPROTO_HTTP1, |
452 | 74 | 0); /* not used, registered twice: HEADERS/TRAILER */ |
453 | | |
454 | 74 | DetectAppLayerInspectEngineRegister2("http_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, |
455 | 74 | HTTP2StateOpen, DetectEngineInspectBufferGeneric, GetBuffer2ForTX); |
456 | 74 | DetectAppLayerMpmRegister2("http_header", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, |
457 | 74 | GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateOpen); |
458 | | |
459 | 74 | DetectAppLayerInspectEngineRegister2("http_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, |
460 | 74 | HTTP2StateDataServer, DetectEngineInspectBufferGeneric, GetBuffer2ForTX); |
461 | 74 | DetectAppLayerMpmRegister2("http_header", SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister, |
462 | 74 | GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataServer); |
463 | | |
464 | 74 | DetectBufferTypeSetDescriptionByName("http_header", |
465 | 74 | "http headers"); |
466 | | |
467 | 74 | g_http_header_buffer_id = DetectBufferTypeGetByName("http_header"); |
468 | | |
469 | 74 | g_keyword_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_header", |
470 | 74 | HttpHeaderThreadDataInit, &g_td_config, HttpHeaderThreadDataFree); |
471 | 74 | g_http2_thread_id = DetectRegisterThreadCtxGlobalFuncs( |
472 | 74 | "http2.header", SCHttp2ThreadBufDataInit, NULL, SCHttp2ThreadBufDataFree); |
473 | 74 | } |
474 | | |
475 | | static int g_http_request_header_buffer_id = 0; |
476 | | static int g_http_response_header_buffer_id = 0; |
477 | | static int g_request_header_thread_id = 0; |
478 | | static int g_response_header_thread_id = 0; |
479 | | static int g_h2_request_header_thread_id = 0; |
480 | | static int g_h2_response_header_thread_id = 0; |
481 | | |
482 | | static InspectionBuffer *GetHttp2HeaderData(DetectEngineThreadCtx *det_ctx, const uint8_t flags, |
483 | | const DetectEngineTransforms *transforms, Flow *_f, const struct MpmListIdDataArgs *cbdata, |
484 | | int list_id) |
485 | 2.91k | { |
486 | 2.91k | SCEnter(); |
487 | | |
488 | 2.91k | InspectionBuffer *buffer = |
489 | 2.91k | InspectionBufferMultipleForListGet(det_ctx, list_id, cbdata->local_id); |
490 | 2.91k | if (buffer == NULL) |
491 | 0 | return NULL; |
492 | 2.91k | if (buffer->initialized) |
493 | 168 | return buffer; |
494 | | |
495 | 2.74k | uint32_t b_len = 0; |
496 | 2.74k | const uint8_t *b = NULL; |
497 | 2.74k | int kw_thread_id; |
498 | 2.74k | if (flags & STREAM_TOSERVER) { |
499 | 2.74k | kw_thread_id = g_h2_request_header_thread_id; |
500 | 2.74k | } else { |
501 | 6 | kw_thread_id = g_h2_response_header_thread_id; |
502 | 6 | } |
503 | 2.74k | void *hdr_td = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, kw_thread_id); |
504 | 2.74k | if (unlikely(hdr_td == NULL)) { |
505 | 0 | return NULL; |
506 | 0 | } |
507 | | |
508 | 2.74k | if (SCHttp2TxGetHeader(hdr_td, cbdata->txv, flags, cbdata->local_id, &b, &b_len) != 1) { |
509 | 785 | InspectionBufferSetupMultiEmpty(buffer); |
510 | 785 | return NULL; |
511 | 785 | } |
512 | 1.96k | if (b == NULL || b_len == 0) { |
513 | 0 | InspectionBufferSetupMultiEmpty(buffer); |
514 | 0 | return NULL; |
515 | 0 | } |
516 | | |
517 | 1.96k | InspectionBufferSetupMulti(buffer, transforms, b, b_len); |
518 | | |
519 | 1.96k | SCReturnPtr(buffer, "InspectionBuffer"); |
520 | 1.96k | } |
521 | | |
522 | | static void PrefilterTxHttp2Header(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, |
523 | | Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags) |
524 | 698 | { |
525 | 698 | SCEnter(); |
526 | | |
527 | 698 | const PrefilterMpmListId *ctx = (const PrefilterMpmListId *)pectx; |
528 | 698 | const MpmCtx *mpm_ctx = ctx->mpm_ctx; |
529 | 698 | const int list_id = ctx->list_id; |
530 | | |
531 | 698 | uint32_t local_id = 0; |
532 | | |
533 | 2.66k | while (1) { |
534 | | // loop until we get a NULL |
535 | | |
536 | 2.66k | struct MpmListIdDataArgs cbdata = { local_id, txv }; |
537 | 2.66k | InspectionBuffer *buffer = |
538 | 2.66k | GetHttp2HeaderData(det_ctx, flags, ctx->transforms, f, &cbdata, list_id); |
539 | 2.66k | if (buffer == NULL) |
540 | 698 | break; |
541 | | |
542 | 1.96k | if (buffer->inspect_len >= mpm_ctx->minlen) { |
543 | 1.77k | (void)mpm_table[mpm_ctx->mpm_type].Search( |
544 | 1.77k | mpm_ctx, &det_ctx->mtcu, &det_ctx->pmq, buffer->inspect, buffer->inspect_len); |
545 | 1.77k | PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len); |
546 | 1.77k | } |
547 | | |
548 | 1.96k | local_id++; |
549 | 1.96k | } |
550 | 698 | } |
551 | | |
552 | | static uint8_t DetectEngineInspectHttp2Header(DetectEngineCtx *de_ctx, |
553 | | DetectEngineThreadCtx *det_ctx, const DetectEngineAppInspectionEngine *engine, |
554 | | const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id) |
555 | 254 | { |
556 | 254 | uint32_t local_id = 0; |
557 | | |
558 | 254 | const DetectEngineTransforms *transforms = NULL; |
559 | 254 | if (!engine->mpm) { |
560 | 0 | transforms = engine->v2.transforms; |
561 | 0 | } |
562 | | |
563 | 255 | while (1) { |
564 | 255 | struct MpmListIdDataArgs cbdata = { |
565 | 255 | local_id, |
566 | 255 | txv, |
567 | 255 | }; |
568 | 255 | InspectionBuffer *buffer = |
569 | 255 | GetHttp2HeaderData(det_ctx, flags, transforms, f, &cbdata, engine->sm_list); |
570 | | |
571 | 255 | if (buffer == NULL || buffer->inspect == NULL) |
572 | 253 | break; |
573 | | |
574 | 2 | det_ctx->buffer_offset = 0; |
575 | 2 | det_ctx->discontinue_matching = 0; |
576 | 2 | det_ctx->inspection_recursion_counter = 0; |
577 | | |
578 | 2 | const int match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, NULL, f, |
579 | 2 | (uint8_t *)buffer->inspect, buffer->inspect_len, buffer->inspect_offset, |
580 | 2 | DETECT_CI_FLAGS_SINGLE, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); |
581 | 2 | if (match == 1) { |
582 | 1 | return DETECT_ENGINE_INSPECT_SIG_MATCH; |
583 | 1 | } |
584 | 1 | local_id++; |
585 | 1 | } |
586 | | |
587 | 253 | return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; |
588 | 254 | } |
589 | | |
590 | | static int PrefilterMpmHttp2HeaderRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, |
591 | | MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id) |
592 | 889 | { |
593 | 889 | PrefilterMpmListId *pectx = SCCalloc(1, sizeof(*pectx)); |
594 | 889 | if (pectx == NULL) |
595 | 0 | return -1; |
596 | 889 | pectx->list_id = list_id; |
597 | 889 | pectx->mpm_ctx = mpm_ctx; |
598 | 889 | pectx->transforms = &mpm_reg->transforms; |
599 | | |
600 | 889 | return PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxHttp2Header, mpm_reg->app_v2.alproto, |
601 | 889 | mpm_reg->app_v2.tx_min_progress, pectx, PrefilterMpmHttpHeaderFree, mpm_reg->name); |
602 | 889 | } |
603 | | |
604 | | typedef struct HttpMultiBufItem { |
605 | | uint8_t *buffer; |
606 | | size_t len; |
607 | | } HttpMultiBufItem; |
608 | | |
609 | | typedef struct HttpMultiBufHeaderThreadData { |
610 | | // array of items, being defined as a buffer with its length just above |
611 | | HttpMultiBufItem *items; |
612 | | // capacity of items (size of allocation) |
613 | | size_t cap; |
614 | | // length of items (number in use) |
615 | | size_t len; |
616 | | } HttpMultiBufHeaderThreadData; |
617 | | |
618 | | static void *HttpMultiBufHeaderThreadDataInit(void *data) |
619 | 255k | { |
620 | 255k | HttpMultiBufHeaderThreadData *td = SCCalloc(1, sizeof(*td)); |
621 | | |
622 | | /* This return value check to satisfy our Cocci malloc checks. */ |
623 | 255k | if (td == NULL) { |
624 | 0 | SCLogError("failed to allocate %" PRIuMAX " bytes: %s", (uintmax_t)sizeof(*td), |
625 | 0 | strerror(errno)); |
626 | 0 | return NULL; |
627 | 0 | } |
628 | 255k | return td; |
629 | 255k | } |
630 | | |
631 | | static void HttpMultiBufHeaderThreadDataFree(void *data) |
632 | 255k | { |
633 | 255k | HttpMultiBufHeaderThreadData *td = data; |
634 | 256k | for (size_t i = 0; i < td->cap; i++) { |
635 | 793 | SCFree(td->items[i].buffer); |
636 | 793 | } |
637 | 255k | SCFree(td->items); |
638 | 255k | SCFree(td); |
639 | 255k | } |
640 | | |
641 | | static InspectionBuffer *GetHttp1HeaderData(DetectEngineThreadCtx *det_ctx, const uint8_t flags, |
642 | | const DetectEngineTransforms *transforms, Flow *f, const struct MpmListIdDataArgs *cbdata, |
643 | | int list_id) |
644 | 15.6k | { |
645 | 15.6k | SCEnter(); |
646 | | |
647 | 15.6k | InspectionBuffer *buffer = |
648 | 15.6k | InspectionBufferMultipleForListGet(det_ctx, list_id, cbdata->local_id); |
649 | 15.6k | if (buffer == NULL) |
650 | 0 | return NULL; |
651 | 15.6k | if (buffer->initialized) |
652 | 4.76k | return buffer; |
653 | | |
654 | 10.8k | int kw_thread_id; |
655 | 10.8k | if (flags & STREAM_TOSERVER) { |
656 | 10.6k | kw_thread_id = g_request_header_thread_id; |
657 | 10.6k | } else { |
658 | 214 | kw_thread_id = g_response_header_thread_id; |
659 | 214 | } |
660 | 10.8k | HttpMultiBufHeaderThreadData *hdr_td = |
661 | 10.8k | DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, kw_thread_id); |
662 | 10.8k | if (unlikely(hdr_td == NULL)) { |
663 | 0 | return NULL; |
664 | 0 | } |
665 | | |
666 | 10.8k | htp_tx_t *tx = (htp_tx_t *)cbdata->txv; |
667 | 10.8k | htp_table_t *headers; |
668 | 10.8k | if (flags & STREAM_TOSERVER) { |
669 | 10.6k | headers = tx->request_headers; |
670 | 10.6k | } else { |
671 | 214 | headers = tx->response_headers; |
672 | 214 | } |
673 | 10.8k | size_t no_of_headers = htp_table_size(headers); |
674 | 10.8k | if (cbdata->local_id == 0) { |
675 | | // We initialize a big buffer on first item |
676 | | // Then, we will just use parts of it |
677 | 3.16k | hdr_td->len = 0; |
678 | 3.16k | if (hdr_td->cap < no_of_headers) { |
679 | 196 | void *new_buffer = SCRealloc(hdr_td->items, no_of_headers * sizeof(HttpMultiBufItem)); |
680 | 196 | if (unlikely(new_buffer == NULL)) { |
681 | 0 | return NULL; |
682 | 0 | } |
683 | 196 | hdr_td->items = new_buffer; |
684 | | // zeroes the new part of the items |
685 | 196 | memset(hdr_td->items + hdr_td->cap, 0, |
686 | 196 | (no_of_headers - hdr_td->cap) * sizeof(HttpMultiBufItem)); |
687 | 196 | hdr_td->cap = no_of_headers; |
688 | 196 | } |
689 | 10.8k | for (size_t i = 0; i < no_of_headers; i++) { |
690 | 7.66k | htp_header_t *h = htp_table_get_index(headers, i, NULL); |
691 | 7.66k | size_t size1 = bstr_size(h->name); |
692 | 7.66k | size_t size2 = bstr_size(h->value); |
693 | 7.66k | size_t size = size1 + size2 + 2; |
694 | 7.66k | if (hdr_td->items[i].len < size) { |
695 | | // Use realloc, as this pointer is not freed until HttpMultiBufHeaderThreadDataFree |
696 | 1.51k | void *tmp = SCRealloc(hdr_td->items[i].buffer, size); |
697 | 1.51k | if (unlikely(tmp == NULL)) { |
698 | 0 | return NULL; |
699 | 0 | } |
700 | 1.51k | hdr_td->items[i].buffer = tmp; |
701 | 1.51k | } |
702 | 7.66k | memcpy(hdr_td->items[i].buffer, bstr_ptr(h->name), size1); |
703 | 7.66k | hdr_td->items[i].buffer[size1] = ':'; |
704 | 7.66k | hdr_td->items[i].buffer[size1 + 1] = ' '; |
705 | 7.66k | memcpy(hdr_td->items[i].buffer + size1 + 2, bstr_ptr(h->value), size2); |
706 | 7.66k | hdr_td->items[i].len = size; |
707 | 7.66k | } |
708 | 3.16k | hdr_td->len = no_of_headers; |
709 | 3.16k | } |
710 | | |
711 | | // cbdata->local_id is the index of the requested header buffer |
712 | | // hdr_td->len is the number of header buffers |
713 | 10.8k | if (cbdata->local_id < hdr_td->len) { |
714 | | // we have one valid header buffer |
715 | 7.66k | InspectionBufferSetupMulti(buffer, transforms, hdr_td->items[cbdata->local_id].buffer, |
716 | 7.66k | hdr_td->items[cbdata->local_id].len); |
717 | 7.66k | SCReturnPtr(buffer, "InspectionBuffer"); |
718 | 7.66k | } // else there are no more header buffer to get |
719 | 3.16k | InspectionBufferSetupMultiEmpty(buffer); |
720 | 3.16k | return NULL; |
721 | 10.8k | } |
722 | | |
723 | | static void PrefilterTxHttp1Header(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, |
724 | | Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags) |
725 | 3.12k | { |
726 | 3.12k | SCEnter(); |
727 | | |
728 | 3.12k | const PrefilterMpmListId *ctx = (const PrefilterMpmListId *)pectx; |
729 | 3.12k | const MpmCtx *mpm_ctx = ctx->mpm_ctx; |
730 | 3.12k | const int list_id = ctx->list_id; |
731 | | |
732 | 3.12k | uint32_t local_id = 0; |
733 | | |
734 | 10.7k | while (1) { |
735 | | // loop until we get a NULL |
736 | | |
737 | 10.7k | struct MpmListIdDataArgs cbdata = { local_id, txv }; |
738 | 10.7k | InspectionBuffer *buffer = |
739 | 10.7k | GetHttp1HeaderData(det_ctx, flags, ctx->transforms, f, &cbdata, list_id); |
740 | 10.7k | if (buffer == NULL) |
741 | 3.12k | break; |
742 | | |
743 | 7.64k | if (buffer->inspect_len >= mpm_ctx->minlen) { |
744 | 7.27k | (void)mpm_table[mpm_ctx->mpm_type].Search( |
745 | 7.27k | mpm_ctx, &det_ctx->mtcu, &det_ctx->pmq, buffer->inspect, buffer->inspect_len); |
746 | 7.27k | PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len); |
747 | 7.27k | } |
748 | | |
749 | 7.64k | local_id++; |
750 | 7.64k | } |
751 | 3.12k | } |
752 | | |
753 | | static int PrefilterMpmHttp1HeaderRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, |
754 | | MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id) |
755 | 886 | { |
756 | 886 | PrefilterMpmListId *pectx = SCCalloc(1, sizeof(*pectx)); |
757 | 886 | if (pectx == NULL) |
758 | 0 | return -1; |
759 | 886 | pectx->list_id = list_id; |
760 | 886 | pectx->mpm_ctx = mpm_ctx; |
761 | 886 | pectx->transforms = &mpm_reg->transforms; |
762 | | |
763 | 886 | return PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxHttp1Header, mpm_reg->app_v2.alproto, |
764 | 886 | mpm_reg->app_v2.tx_min_progress, pectx, PrefilterMpmHttpHeaderFree, mpm_reg->name); |
765 | 886 | } |
766 | | |
767 | | static uint8_t DetectEngineInspectHttp1Header(DetectEngineCtx *de_ctx, |
768 | | DetectEngineThreadCtx *det_ctx, const DetectEngineAppInspectionEngine *engine, |
769 | | const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id) |
770 | 3.16k | { |
771 | 3.16k | uint32_t local_id = 0; |
772 | | |
773 | 3.16k | const DetectEngineTransforms *transforms = NULL; |
774 | 3.16k | if (!engine->mpm) { |
775 | 21 | transforms = engine->v2.transforms; |
776 | 21 | } |
777 | | |
778 | 4.82k | while (1) { |
779 | 4.82k | struct MpmListIdDataArgs cbdata = { |
780 | 4.82k | local_id, |
781 | 4.82k | txv, |
782 | 4.82k | }; |
783 | 4.82k | InspectionBuffer *buffer = |
784 | 4.82k | GetHttp1HeaderData(det_ctx, flags, transforms, f, &cbdata, engine->sm_list); |
785 | | |
786 | 4.82k | if (buffer == NULL || buffer->inspect == NULL) |
787 | 1.31k | break; |
788 | | |
789 | 3.51k | det_ctx->buffer_offset = 0; |
790 | 3.51k | det_ctx->discontinue_matching = 0; |
791 | 3.51k | det_ctx->inspection_recursion_counter = 0; |
792 | | |
793 | 3.51k | const int match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, NULL, f, |
794 | 3.51k | (uint8_t *)buffer->inspect, buffer->inspect_len, buffer->inspect_offset, |
795 | 3.51k | DETECT_CI_FLAGS_SINGLE, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); |
796 | 3.51k | if (match == 1) { |
797 | 1.85k | return DETECT_ENGINE_INSPECT_SIG_MATCH; |
798 | 1.85k | } |
799 | 1.66k | local_id++; |
800 | 1.66k | } |
801 | | |
802 | 1.31k | return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; |
803 | 3.16k | } |
804 | | |
805 | | static int DetectHTTPRequestHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) |
806 | 5.72k | { |
807 | 5.72k | if (DetectBufferSetActiveList(de_ctx, s, g_http_request_header_buffer_id) < 0) |
808 | 99 | return -1; |
809 | | |
810 | 5.62k | if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) != 0) |
811 | 158 | return -1; |
812 | | |
813 | 5.46k | return 0; |
814 | 5.62k | } |
815 | | |
816 | | void DetectHttpRequestHeaderRegister(void) |
817 | 74 | { |
818 | 74 | sigmatch_table[DETECT_HTTP_REQUEST_HEADER].name = "http.request_header"; |
819 | 74 | sigmatch_table[DETECT_HTTP_REQUEST_HEADER].desc = |
820 | 74 | "sticky buffer to match on only one HTTP header name and value"; |
821 | 74 | sigmatch_table[DETECT_HTTP_REQUEST_HEADER].url = "/rules/http-keywords.html#request_header"; |
822 | 74 | sigmatch_table[DETECT_HTTP_REQUEST_HEADER].Setup = DetectHTTPRequestHeaderSetup; |
823 | 74 | sigmatch_table[DETECT_HTTP_REQUEST_HEADER].flags |= |
824 | 74 | SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER; |
825 | | |
826 | 74 | DetectAppLayerMpmRegister2("http_request_header", SIG_FLAG_TOSERVER, 2, |
827 | 74 | PrefilterMpmHttp2HeaderRegister, NULL, ALPROTO_HTTP2, HTTP2StateOpen); |
828 | 74 | DetectAppLayerInspectEngineRegister2("http_request_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, |
829 | 74 | HTTP2StateOpen, DetectEngineInspectHttp2Header, NULL); |
830 | 74 | DetectAppLayerMpmRegister2("http_request_header", SIG_FLAG_TOSERVER, 2, |
831 | 74 | PrefilterMpmHttp1HeaderRegister, NULL, ALPROTO_HTTP1, HTP_REQUEST_HEADERS); |
832 | 74 | DetectAppLayerInspectEngineRegister2("http_request_header", ALPROTO_HTTP1, SIG_FLAG_TOSERVER, |
833 | 74 | HTP_REQUEST_HEADERS, DetectEngineInspectHttp1Header, NULL); |
834 | 74 | DetectBufferTypeSetDescriptionByName("http_request_header", "HTTP header name and value"); |
835 | 74 | g_http_request_header_buffer_id = DetectBufferTypeGetByName("http_request_header"); |
836 | 74 | DetectBufferTypeSupportsMultiInstance("http_request_header"); |
837 | 74 | g_request_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_request_header", |
838 | 74 | HttpMultiBufHeaderThreadDataInit, NULL, HttpMultiBufHeaderThreadDataFree); |
839 | 74 | g_h2_request_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http2_request_header", |
840 | 74 | SCHttp2ThreadMultiBufDataInit, NULL, SCHttp2ThreadMultiBufDataFree); |
841 | 74 | } |
842 | | |
843 | | static int DetectHTTPResponseHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) |
844 | 323 | { |
845 | 323 | if (DetectBufferSetActiveList(de_ctx, s, g_http_response_header_buffer_id) < 0) |
846 | 11 | return -1; |
847 | | |
848 | 312 | if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) != 0) |
849 | 26 | return -1; |
850 | | |
851 | 286 | return 0; |
852 | 312 | } |
853 | | |
854 | | void DetectHttpResponseHeaderRegister(void) |
855 | 74 | { |
856 | 74 | sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].name = "http.response_header"; |
857 | 74 | sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].desc = |
858 | 74 | "sticky buffer to match on only one HTTP header name and value"; |
859 | 74 | sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].url = "/rules/http2-keywords.html#response_header"; |
860 | 74 | sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].Setup = DetectHTTPResponseHeaderSetup; |
861 | 74 | sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].flags |= |
862 | 74 | SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER; |
863 | | |
864 | 74 | DetectAppLayerMpmRegister2("http_response_header", SIG_FLAG_TOCLIENT, 2, |
865 | 74 | PrefilterMpmHttp2HeaderRegister, NULL, ALPROTO_HTTP2, HTTP2StateOpen); |
866 | 74 | DetectAppLayerInspectEngineRegister2("http_response_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, |
867 | 74 | HTTP2StateOpen, DetectEngineInspectHttp2Header, NULL); |
868 | 74 | DetectAppLayerMpmRegister2("http_response_header", SIG_FLAG_TOCLIENT, 2, |
869 | 74 | PrefilterMpmHttp1HeaderRegister, NULL, ALPROTO_HTTP1, HTP_RESPONSE_HEADERS); |
870 | 74 | DetectAppLayerInspectEngineRegister2("http_response_header", ALPROTO_HTTP1, SIG_FLAG_TOCLIENT, |
871 | 74 | HTP_RESPONSE_HEADERS, DetectEngineInspectHttp1Header, NULL); |
872 | | |
873 | 74 | DetectBufferTypeSetDescriptionByName("http_response_header", "HTTP header name and value"); |
874 | 74 | g_http_response_header_buffer_id = DetectBufferTypeGetByName("http_response_header"); |
875 | 74 | DetectBufferTypeSupportsMultiInstance("http_response_header"); |
876 | 74 | g_response_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_response_header", |
877 | 74 | HttpMultiBufHeaderThreadDataInit, NULL, HttpMultiBufHeaderThreadDataFree); |
878 | 74 | g_h2_response_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http2_response_header", |
879 | | SCHttp2ThreadMultiBufDataInit, NULL, SCHttp2ThreadMultiBufDataFree); |
880 | 74 | } |
881 | | |
882 | | /************************************Unittests*********************************/ |
883 | | |
884 | | #ifdef UNITTESTS |
885 | | #include "tests/detect-http-header.c" |
886 | | #endif |
887 | | |
888 | | /** |
889 | | * @} |
890 | | */ |