/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 | 17.0k | { |
74 | 17.0k | *buffer_len = 0; |
75 | | |
76 | 17.0k | HttpHeaderThreadData *hdr_td = NULL; |
77 | 17.0k | HttpHeaderBuffer *buf = |
78 | 17.0k | HttpHeaderGetBufferSpace(det_ctx, f, flags, g_keyword_thread_id, &hdr_td); |
79 | 17.0k | if (unlikely(buf == NULL)) { |
80 | 0 | return NULL; |
81 | 0 | } |
82 | | |
83 | 17.0k | htp_table_t *headers; |
84 | 17.0k | if (flags & STREAM_TOSERVER) { |
85 | 11.3k | if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, tx, flags) <= |
86 | 11.3k | HTP_REQUEST_HEADERS) |
87 | 3.38k | return NULL; |
88 | 7.96k | headers = tx->request_headers; |
89 | 7.96k | } else { |
90 | 5.65k | if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, tx, flags) <= |
91 | 5.65k | HTP_RESPONSE_HEADERS) |
92 | 1.08k | return NULL; |
93 | 4.57k | headers = tx->response_headers; |
94 | 4.57k | } |
95 | 12.5k | if (headers == NULL) |
96 | 0 | return NULL; |
97 | | |
98 | 12.5k | size_t i = 0; |
99 | 12.5k | size_t no_of_headers = htp_table_size(headers); |
100 | 44.4k | for (; i < no_of_headers; i++) { |
101 | 31.9k | htp_header_t *h = htp_table_get_index(headers, i, NULL); |
102 | 31.9k | size_t size1 = bstr_size(h->name); |
103 | 31.9k | size_t size2 = bstr_size(h->value); |
104 | | |
105 | 31.9k | if (flags & STREAM_TOSERVER) { |
106 | 17.3k | if (size1 == 6 && |
107 | 943 | SCMemcmpLowercase("cookie", bstr_ptr(h->name), 6) == 0) { |
108 | 176 | continue; |
109 | 176 | } |
110 | 17.3k | } else { |
111 | 14.5k | if (size1 == 10 && |
112 | 1.27k | SCMemcmpLowercase("set-cookie", bstr_ptr(h->name), 10) == 0) { |
113 | 0 | continue; |
114 | 0 | } |
115 | 14.5k | } |
116 | | |
117 | 31.7k | size_t size = size1 + size2 + 4; |
118 | | #if 0 |
119 | | if (i + 1 == no_of_headers) |
120 | | size += 2; |
121 | | #endif |
122 | 31.7k | if (size + buf->len > buf->size) { |
123 | 118 | if (HttpHeaderExpandBuffer(hdr_td, buf, size) != 0) { |
124 | 0 | return NULL; |
125 | 0 | } |
126 | 118 | } |
127 | | |
128 | 31.7k | memcpy(buf->buffer + buf->len, bstr_ptr(h->name), bstr_size(h->name)); |
129 | 31.7k | buf->len += bstr_size(h->name); |
130 | 31.7k | buf->buffer[buf->len++] = ':'; |
131 | 31.7k | buf->buffer[buf->len++] = ' '; |
132 | 31.7k | memcpy(buf->buffer + buf->len, bstr_ptr(h->value), bstr_size(h->value)); |
133 | 31.7k | buf->len += bstr_size(h->value); |
134 | 31.7k | buf->buffer[buf->len++] = '\r'; |
135 | 31.7k | 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 | 31.7k | } |
143 | | |
144 | 12.5k | *buffer_len = buf->len; |
145 | 12.5k | return buf->buffer; |
146 | 12.5k | } |
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 | 2.15k | { |
152 | 2.15k | InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); |
153 | 2.15k | if (buffer->inspect == NULL) { |
154 | 2.15k | uint32_t b_len = 0; |
155 | 2.15k | const uint8_t *b = NULL; |
156 | | |
157 | 2.15k | void *thread_buf = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_thread_id); |
158 | 2.15k | if (thread_buf == NULL) |
159 | 0 | return NULL; |
160 | 2.15k | if (SCHttp2TxGetHeaders(txv, flow_flags, &b, &b_len, thread_buf) != 1) |
161 | 1.77k | return NULL; |
162 | 384 | if (b == NULL || b_len == 0) |
163 | 0 | return NULL; |
164 | | |
165 | 384 | InspectionBufferSetup(det_ctx, list_id, buffer, b, b_len); |
166 | 384 | InspectionBufferApplyTransforms(buffer, transforms); |
167 | 384 | } |
168 | | |
169 | 387 | return buffer; |
170 | 2.15k | } |
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 | 11.7k | { |
179 | 11.7k | SCEnter(); |
180 | | |
181 | 11.7k | const int list_id = engine->sm_list; |
182 | 11.7k | InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); |
183 | 11.7k | if (buffer->inspect == NULL) { |
184 | 7.96k | SCLogDebug("setting up inspect buffer %d", list_id); |
185 | | |
186 | | /* if prefilter didn't already run, we need to consider transformations */ |
187 | 7.96k | const DetectEngineTransforms *transforms = NULL; |
188 | 7.96k | if (!engine->mpm) { |
189 | 3.96k | transforms = engine->v2.transforms; |
190 | 3.96k | } |
191 | | |
192 | 7.96k | uint32_t rawdata_len = 0; |
193 | 7.96k | uint8_t *rawdata = GetBufferForTX(txv, det_ctx, f, flags, &rawdata_len); |
194 | 7.96k | if (rawdata_len == 0) { |
195 | 5.08k | SCLogDebug("no data"); |
196 | 5.08k | goto end; |
197 | 5.08k | } |
198 | | /* setup buffer and apply transforms */ |
199 | 2.87k | InspectionBufferSetup(det_ctx, list_id, buffer, rawdata, rawdata_len); |
200 | 2.87k | InspectionBufferApplyTransforms(buffer, transforms); |
201 | 2.87k | } |
202 | | |
203 | 6.65k | const uint32_t data_len = buffer->inspect_len; |
204 | 6.65k | const uint8_t *data = buffer->inspect; |
205 | 6.65k | const uint64_t offset = buffer->inspect_offset; |
206 | | |
207 | 6.65k | det_ctx->discontinue_matching = 0; |
208 | 6.65k | det_ctx->buffer_offset = 0; |
209 | 6.65k | det_ctx->inspection_recursion_counter = 0; |
210 | | |
211 | | /* Inspect all the uricontents fetched on each |
212 | | * transaction at the app layer */ |
213 | 6.65k | int r = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, |
214 | 6.65k | NULL, f, (uint8_t *)data, data_len, offset, |
215 | 6.65k | DETECT_CI_FLAGS_SINGLE, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); |
216 | 6.65k | SCLogDebug("r = %d", r); |
217 | 6.65k | if (r == 1) { |
218 | 4.86k | return DETECT_ENGINE_INSPECT_SIG_MATCH; |
219 | 4.86k | } |
220 | 6.88k | end: |
221 | 6.88k | if (flags & STREAM_TOSERVER) { |
222 | 4.03k | if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, txv, flags) > |
223 | 4.03k | HTP_REQUEST_HEADERS) |
224 | 2.25k | return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH; |
225 | 4.03k | } else { |
226 | 2.84k | if (AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, txv, flags) > |
227 | 2.84k | HTP_RESPONSE_HEADERS) |
228 | 2.34k | return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH; |
229 | 2.84k | } |
230 | 2.28k | return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; |
231 | 6.88k | } |
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.1k | { |
250 | 15.1k | SCEnter(); |
251 | | |
252 | 15.1k | const PrefilterMpmHttpHeaderCtx *ctx = pectx; |
253 | 15.1k | const MpmCtx *mpm_ctx = ctx->mpm_ctx; |
254 | 15.1k | SCLogDebug("running on list %d", ctx->list_id); |
255 | | |
256 | 15.1k | const int list_id = ctx->list_id; |
257 | 15.1k | InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); |
258 | 15.1k | if (buffer->inspect == NULL) { |
259 | 14.6k | uint32_t rawdata_len = 0; |
260 | 14.6k | uint8_t *rawdata = GetBufferForTX(txv, det_ctx, f, flags, &rawdata_len); |
261 | 14.6k | if (rawdata_len == 0) |
262 | 7.76k | return; |
263 | | |
264 | | /* setup buffer and apply transforms */ |
265 | 6.89k | InspectionBufferSetup(det_ctx, list_id, buffer, rawdata, rawdata_len); |
266 | 6.89k | InspectionBufferApplyTransforms(buffer, ctx->transforms); |
267 | 6.89k | } |
268 | | |
269 | 7.42k | const uint32_t data_len = buffer->inspect_len; |
270 | 7.42k | const uint8_t *data = buffer->inspect; |
271 | | |
272 | 7.42k | SCLogDebug("mpm'ing buffer:"); |
273 | | //PrintRawDataFp(stdout, data, data_len); |
274 | | |
275 | 7.42k | if (data != NULL && data_len >= mpm_ctx->minlen) { |
276 | 6.39k | (void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx, |
277 | 6.39k | &det_ctx->mtcu, &det_ctx->pmq, data, data_len); |
278 | 6.39k | PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len); |
279 | 6.39k | } |
280 | 7.42k | } |
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.84k | { |
285 | 6.84k | SCEnter(); |
286 | | |
287 | 6.84k | htp_tx_t *tx = txv; |
288 | 6.84k | 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.84k | if (htud && ( |
291 | 6.84k | ((flags & STREAM_TOSERVER) && !htud->request_has_trailers) || |
292 | 6.55k | ((flags & STREAM_TOCLIENT) && !htud->response_has_trailers))) { |
293 | 6.55k | SCReturn; |
294 | 6.55k | } |
295 | 296 | PrefilterMpmHttpHeader(det_ctx, pectx, p, f, txv, idx, _txd, flags); |
296 | 296 | SCReturn; |
297 | 6.84k | } |
298 | | |
299 | | static void PrefilterMpmHttpHeaderFree(void *ptr) |
300 | 11.1k | { |
301 | 11.1k | SCFree(ptr); |
302 | 11.1k | } |
303 | | |
304 | | static int PrefilterMpmHttpHeaderRequestRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, |
305 | | MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id) |
306 | 2.49k | { |
307 | 2.49k | SCEnter(); |
308 | | |
309 | | /* header */ |
310 | 2.49k | PrefilterMpmHttpHeaderCtx *pectx = SCCalloc(1, sizeof(*pectx)); |
311 | 2.49k | if (pectx == NULL) |
312 | 0 | return -1; |
313 | 2.49k | pectx->list_id = list_id; |
314 | 2.49k | pectx->mpm_ctx = mpm_ctx; |
315 | 2.49k | pectx->transforms = &mpm_reg->transforms; |
316 | | |
317 | 2.49k | int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpHeader, |
318 | 2.49k | mpm_reg->app_v2.alproto, HTP_REQUEST_HEADERS, |
319 | 2.49k | pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname); |
320 | 2.49k | if (r != 0) { |
321 | 0 | SCFree(pectx); |
322 | 0 | return r; |
323 | 0 | } |
324 | | |
325 | | /* trailer */ |
326 | 2.49k | pectx = SCCalloc(1, sizeof(*pectx)); |
327 | 2.49k | if (pectx == NULL) |
328 | 0 | return -1; |
329 | 2.49k | pectx->list_id = list_id; |
330 | 2.49k | pectx->mpm_ctx = mpm_ctx; |
331 | 2.49k | pectx->transforms = &mpm_reg->transforms; |
332 | | |
333 | 2.49k | r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpTrailer, |
334 | 2.49k | mpm_reg->app_v2.alproto, HTP_REQUEST_TRAILER, |
335 | 2.49k | pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname); |
336 | 2.49k | if (r != 0) { |
337 | 0 | SCFree(pectx); |
338 | 0 | } |
339 | 2.49k | return r; |
340 | 2.49k | } |
341 | | |
342 | | static int PrefilterMpmHttpHeaderResponseRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, |
343 | | MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id) |
344 | 1.39k | { |
345 | 1.39k | SCEnter(); |
346 | | |
347 | | /* header */ |
348 | 1.39k | PrefilterMpmHttpHeaderCtx *pectx = SCCalloc(1, sizeof(*pectx)); |
349 | 1.39k | if (pectx == NULL) |
350 | 0 | return -1; |
351 | 1.39k | pectx->list_id = list_id; |
352 | 1.39k | pectx->mpm_ctx = mpm_ctx; |
353 | 1.39k | pectx->transforms = &mpm_reg->transforms; |
354 | | |
355 | 1.39k | int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpHeader, |
356 | 1.39k | mpm_reg->app_v2.alproto, HTP_RESPONSE_HEADERS, |
357 | 1.39k | pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname); |
358 | 1.39k | if (r != 0) { |
359 | 0 | SCFree(pectx); |
360 | 0 | return r; |
361 | 0 | } |
362 | | |
363 | | /* trailer */ |
364 | 1.39k | pectx = SCCalloc(1, sizeof(*pectx)); |
365 | 1.39k | if (pectx == NULL) |
366 | 0 | return -1; |
367 | 1.39k | pectx->list_id = list_id; |
368 | 1.39k | pectx->mpm_ctx = mpm_ctx; |
369 | 1.39k | pectx->transforms = &mpm_reg->transforms; |
370 | | |
371 | 1.39k | r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmHttpTrailer, |
372 | 1.39k | mpm_reg->app_v2.alproto, HTP_RESPONSE_TRAILER, |
373 | 1.39k | pectx, PrefilterMpmHttpHeaderFree, mpm_reg->pname); |
374 | 1.39k | if (r != 0) { |
375 | 0 | SCFree(pectx); |
376 | 0 | } |
377 | 1.39k | return r; |
378 | 1.39k | } |
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.50k | { |
395 | 1.50k | return DetectEngineContentModifierBufferSetup( |
396 | 1.50k | de_ctx, s, arg, DETECT_AL_HTTP_HEADER, g_http_header_buffer_id, ALPROTO_HTTP1); |
397 | 1.50k | } |
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 | 12.0k | { |
410 | 12.0k | if (DetectBufferSetActiveList(de_ctx, s, g_http_header_buffer_id) < 0) |
411 | 111 | return -1; |
412 | 11.9k | if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0) |
413 | 213 | return -1; |
414 | 11.7k | return 0; |
415 | 11.9k | } |
416 | | |
417 | | /** |
418 | | * \brief Registers the keyword handlers for the "http_header" keyword. |
419 | | */ |
420 | | void DetectHttpHeaderRegister(void) |
421 | 73 | { |
422 | | /* http_header content modifier */ |
423 | 73 | sigmatch_table[DETECT_AL_HTTP_HEADER].name = "http_header"; |
424 | 73 | sigmatch_table[DETECT_AL_HTTP_HEADER].desc = "content modifier to match only on the HTTP header-buffer"; |
425 | 73 | sigmatch_table[DETECT_AL_HTTP_HEADER].url = "/rules/http-keywords.html#http-header-and-http-raw-header"; |
426 | 73 | sigmatch_table[DETECT_AL_HTTP_HEADER].Setup = DetectHttpHeaderSetup; |
427 | | #ifdef UNITTESTS |
428 | | sigmatch_table[DETECT_AL_HTTP_HEADER].RegisterTests = DetectHttpHeaderRegisterTests; |
429 | | #endif |
430 | 73 | sigmatch_table[DETECT_AL_HTTP_HEADER].flags |= SIGMATCH_NOOPT ; |
431 | 73 | sigmatch_table[DETECT_AL_HTTP_HEADER].flags |= SIGMATCH_INFO_CONTENT_MODIFIER; |
432 | 73 | sigmatch_table[DETECT_AL_HTTP_HEADER].alternative = DETECT_HTTP_HEADER; |
433 | | |
434 | | /* http.header sticky buffer */ |
435 | 73 | sigmatch_table[DETECT_HTTP_HEADER].name = "http.header"; |
436 | 73 | sigmatch_table[DETECT_HTTP_HEADER].desc = "sticky buffer to match on the normalized HTTP header-buffer"; |
437 | 73 | sigmatch_table[DETECT_HTTP_HEADER].url = "/rules/http-keywords.html#http-header-and-http-raw-header"; |
438 | 73 | sigmatch_table[DETECT_HTTP_HEADER].Setup = DetectHttpHeaderSetupSticky; |
439 | 73 | sigmatch_table[DETECT_HTTP_HEADER].flags |= SIGMATCH_NOOPT; |
440 | 73 | sigmatch_table[DETECT_HTTP_HEADER].flags |= SIGMATCH_INFO_STICKY_BUFFER; |
441 | | |
442 | 73 | DetectAppLayerInspectEngineRegister2("http_header", ALPROTO_HTTP1, SIG_FLAG_TOSERVER, |
443 | 73 | HTP_REQUEST_HEADERS, DetectEngineInspectBufferHttpHeader, NULL); |
444 | 73 | DetectAppLayerMpmRegister2("http_header", SIG_FLAG_TOSERVER, 2, |
445 | 73 | PrefilterMpmHttpHeaderRequestRegister, NULL, ALPROTO_HTTP1, |
446 | 73 | 0); /* not used, registered twice: HEADERS/TRAILER */ |
447 | | |
448 | 73 | DetectAppLayerInspectEngineRegister2("http_header", ALPROTO_HTTP1, SIG_FLAG_TOCLIENT, |
449 | 73 | HTP_RESPONSE_HEADERS, DetectEngineInspectBufferHttpHeader, NULL); |
450 | 73 | DetectAppLayerMpmRegister2("http_header", SIG_FLAG_TOCLIENT, 2, |
451 | 73 | PrefilterMpmHttpHeaderResponseRegister, NULL, ALPROTO_HTTP1, |
452 | 73 | 0); /* not used, registered twice: HEADERS/TRAILER */ |
453 | | |
454 | 73 | DetectAppLayerInspectEngineRegister2("http_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, |
455 | 73 | HTTP2StateDataClient, DetectEngineInspectBufferGeneric, GetBuffer2ForTX); |
456 | 73 | DetectAppLayerMpmRegister2("http_header", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, |
457 | 73 | GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataClient); |
458 | | |
459 | 73 | DetectAppLayerInspectEngineRegister2("http_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, |
460 | 73 | HTTP2StateDataServer, DetectEngineInspectBufferGeneric, GetBuffer2ForTX); |
461 | 73 | DetectAppLayerMpmRegister2("http_header", SIG_FLAG_TOCLIENT, 2, PrefilterGenericMpmRegister, |
462 | 73 | GetBuffer2ForTX, ALPROTO_HTTP2, HTTP2StateDataServer); |
463 | | |
464 | 73 | DetectBufferTypeSetDescriptionByName("http_header", |
465 | 73 | "http headers"); |
466 | | |
467 | 73 | g_http_header_buffer_id = DetectBufferTypeGetByName("http_header"); |
468 | | |
469 | 73 | g_keyword_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_header", |
470 | 73 | HttpHeaderThreadDataInit, &g_td_config, HttpHeaderThreadDataFree); |
471 | 73 | g_http2_thread_id = DetectRegisterThreadCtxGlobalFuncs( |
472 | 73 | "http2.header", SCHttp2ThreadBufDataInit, NULL, SCHttp2ThreadBufDataFree); |
473 | 73 | } |
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 | 4.51k | { |
486 | 4.51k | SCEnter(); |
487 | | |
488 | 4.51k | InspectionBuffer *buffer = |
489 | 4.51k | InspectionBufferMultipleForListGet(det_ctx, list_id, cbdata->local_id); |
490 | 4.51k | if (buffer == NULL) |
491 | 1 | return NULL; |
492 | 4.51k | if (buffer->initialized) |
493 | 111 | return buffer; |
494 | | |
495 | 4.40k | uint32_t b_len = 0; |
496 | 4.40k | const uint8_t *b = NULL; |
497 | 4.40k | int kw_thread_id; |
498 | 4.40k | if (flags & STREAM_TOSERVER) { |
499 | 2.73k | kw_thread_id = g_h2_request_header_thread_id; |
500 | 2.73k | } else { |
501 | 1.67k | kw_thread_id = g_h2_response_header_thread_id; |
502 | 1.67k | } |
503 | 4.40k | void *hdr_td = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, kw_thread_id); |
504 | 4.40k | if (unlikely(hdr_td == NULL)) { |
505 | 0 | return NULL; |
506 | 0 | } |
507 | | |
508 | 4.40k | if (SCHttp2TxGetHeader(hdr_td, cbdata->txv, flags, cbdata->local_id, &b, &b_len) != 1) { |
509 | 1.41k | InspectionBufferSetupMultiEmpty(buffer); |
510 | 1.41k | return NULL; |
511 | 1.41k | } |
512 | 2.98k | if (b == NULL || b_len == 0) { |
513 | 0 | InspectionBufferSetupMultiEmpty(buffer); |
514 | 0 | return NULL; |
515 | 0 | } |
516 | | |
517 | 2.98k | InspectionBufferSetupMulti(buffer, transforms, b, b_len); |
518 | | |
519 | 2.98k | SCReturnPtr(buffer, "InspectionBuffer"); |
520 | 2.98k | } |
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 | 387 | { |
525 | 387 | SCEnter(); |
526 | | |
527 | 387 | const PrefilterMpmListId *ctx = (const PrefilterMpmListId *)pectx; |
528 | 387 | const MpmCtx *mpm_ctx = ctx->mpm_ctx; |
529 | 387 | const int list_id = ctx->list_id; |
530 | | |
531 | 387 | uint32_t local_id = 0; |
532 | | |
533 | 3.37k | while (1) { |
534 | | // loop until we get a NULL |
535 | | |
536 | 3.37k | struct MpmListIdDataArgs cbdata = { local_id, txv }; |
537 | 3.37k | InspectionBuffer *buffer = |
538 | 3.37k | GetHttp2HeaderData(det_ctx, flags, ctx->transforms, f, &cbdata, list_id); |
539 | 3.37k | if (buffer == NULL) |
540 | 387 | break; |
541 | | |
542 | 2.98k | if (buffer->inspect_len >= mpm_ctx->minlen) { |
543 | 2.75k | (void)mpm_table[mpm_ctx->mpm_type].Search( |
544 | 2.75k | mpm_ctx, &det_ctx->mtcu, &det_ctx->pmq, buffer->inspect, buffer->inspect_len); |
545 | 2.75k | PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len); |
546 | 2.75k | } |
547 | | |
548 | 2.98k | local_id++; |
549 | 2.98k | } |
550 | 387 | } |
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 | 1.14k | { |
556 | 1.14k | uint32_t local_id = 0; |
557 | | |
558 | 1.14k | const DetectEngineTransforms *transforms = NULL; |
559 | 1.14k | if (!engine->mpm) { |
560 | 0 | transforms = engine->v2.transforms; |
561 | 0 | } |
562 | | |
563 | 1.14k | while (1) { |
564 | 1.14k | struct MpmListIdDataArgs cbdata = { |
565 | 1.14k | local_id, |
566 | 1.14k | txv, |
567 | 1.14k | }; |
568 | 1.14k | InspectionBuffer *buffer = |
569 | 1.14k | GetHttp2HeaderData(det_ctx, flags, transforms, f, &cbdata, engine->sm_list); |
570 | | |
571 | 1.14k | if (buffer == NULL || buffer->inspect == NULL) |
572 | 1.14k | break; |
573 | | |
574 | 0 | det_ctx->buffer_offset = 0; |
575 | 0 | det_ctx->discontinue_matching = 0; |
576 | 0 | det_ctx->inspection_recursion_counter = 0; |
577 | |
|
578 | 0 | const int match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, NULL, f, |
579 | 0 | (uint8_t *)buffer->inspect, buffer->inspect_len, buffer->inspect_offset, |
580 | 0 | DETECT_CI_FLAGS_SINGLE, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); |
581 | 0 | if (match == 1) { |
582 | 0 | return DETECT_ENGINE_INSPECT_SIG_MATCH; |
583 | 0 | } |
584 | 0 | local_id++; |
585 | 0 | } |
586 | | |
587 | 1.14k | return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; |
588 | 1.14k | } |
589 | | |
590 | | static int PrefilterMpmHttp2HeaderRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, |
591 | | MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id) |
592 | 807 | { |
593 | 807 | PrefilterMpmListId *pectx = SCCalloc(1, sizeof(*pectx)); |
594 | 807 | if (pectx == NULL) |
595 | 0 | return -1; |
596 | 807 | pectx->list_id = list_id; |
597 | 807 | pectx->mpm_ctx = mpm_ctx; |
598 | 807 | pectx->transforms = &mpm_reg->transforms; |
599 | | |
600 | 807 | return PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxHttp2Header, mpm_reg->app_v2.alproto, |
601 | 807 | mpm_reg->app_v2.tx_min_progress, pectx, PrefilterMpmHttpHeaderFree, mpm_reg->name); |
602 | 807 | } |
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 | 292k | { |
620 | 292k | HttpMultiBufHeaderThreadData *td = SCCalloc(1, sizeof(*td)); |
621 | | |
622 | | /* This return value check to satisfy our Cocci malloc checks. */ |
623 | 292k | 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 | 292k | return td; |
629 | 292k | } |
630 | | |
631 | | static void HttpMultiBufHeaderThreadDataFree(void *data) |
632 | 292k | { |
633 | 292k | HttpMultiBufHeaderThreadData *td = data; |
634 | 293k | for (size_t i = 0; i < td->cap; i++) { |
635 | 663 | SCFree(td->items[i].buffer); |
636 | 663 | } |
637 | 292k | SCFree(td->items); |
638 | 292k | SCFree(td); |
639 | 292k | } |
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 | 8.57k | { |
645 | 8.57k | SCEnter(); |
646 | | |
647 | 8.57k | InspectionBuffer *buffer = |
648 | 8.57k | InspectionBufferMultipleForListGet(det_ctx, list_id, cbdata->local_id); |
649 | 8.57k | if (buffer == NULL) |
650 | 0 | return NULL; |
651 | 8.57k | if (buffer->initialized) |
652 | 2.60k | return buffer; |
653 | | |
654 | 5.97k | int kw_thread_id; |
655 | 5.97k | if (flags & STREAM_TOSERVER) { |
656 | 5.62k | kw_thread_id = g_request_header_thread_id; |
657 | 5.62k | } else { |
658 | 351 | kw_thread_id = g_response_header_thread_id; |
659 | 351 | } |
660 | 5.97k | HttpMultiBufHeaderThreadData *hdr_td = |
661 | 5.97k | DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, kw_thread_id); |
662 | 5.97k | if (unlikely(hdr_td == NULL)) { |
663 | 0 | return NULL; |
664 | 0 | } |
665 | | |
666 | 5.97k | htp_tx_t *tx = (htp_tx_t *)cbdata->txv; |
667 | 5.97k | htp_table_t *headers; |
668 | 5.97k | if (flags & STREAM_TOSERVER) { |
669 | 5.62k | headers = tx->request_headers; |
670 | 5.62k | } else { |
671 | 351 | headers = tx->response_headers; |
672 | 351 | } |
673 | 5.97k | size_t no_of_headers = htp_table_size(headers); |
674 | 5.97k | if (cbdata->local_id == 0) { |
675 | | // We initialize a big buffer on first item |
676 | | // Then, we will just use parts of it |
677 | 2.18k | hdr_td->len = 0; |
678 | 2.18k | if (hdr_td->cap < no_of_headers) { |
679 | 157 | void *new_buffer = SCRealloc(hdr_td->items, no_of_headers * sizeof(HttpMultiBufItem)); |
680 | 157 | if (unlikely(new_buffer == NULL)) { |
681 | 0 | return NULL; |
682 | 0 | } |
683 | 157 | hdr_td->items = new_buffer; |
684 | | // zeroes the new part of the items |
685 | 157 | memset(hdr_td->items + hdr_td->cap, 0, |
686 | 157 | (no_of_headers - hdr_td->cap) * sizeof(HttpMultiBufItem)); |
687 | 157 | hdr_td->cap = no_of_headers; |
688 | 157 | } |
689 | 5.98k | for (size_t i = 0; i < no_of_headers; i++) { |
690 | 3.79k | htp_header_t *h = htp_table_get_index(headers, i, NULL); |
691 | 3.79k | size_t size1 = bstr_size(h->name); |
692 | 3.79k | size_t size2 = bstr_size(h->value); |
693 | 3.79k | size_t size = size1 + size2 + 2; |
694 | 3.79k | if (hdr_td->items[i].len < size) { |
695 | | // Use realloc, as this pointer is not freed until HttpMultiBufHeaderThreadDataFree |
696 | 925 | void *tmp = SCRealloc(hdr_td->items[i].buffer, size); |
697 | 925 | if (unlikely(tmp == NULL)) { |
698 | 0 | return NULL; |
699 | 0 | } |
700 | 925 | hdr_td->items[i].buffer = tmp; |
701 | 925 | } |
702 | 3.79k | memcpy(hdr_td->items[i].buffer, bstr_ptr(h->name), size1); |
703 | 3.79k | hdr_td->items[i].buffer[size1] = ':'; |
704 | 3.79k | hdr_td->items[i].buffer[size1 + 1] = ' '; |
705 | 3.79k | memcpy(hdr_td->items[i].buffer + size1 + 2, bstr_ptr(h->value), size2); |
706 | 3.79k | hdr_td->items[i].len = size; |
707 | 3.79k | } |
708 | 2.18k | hdr_td->len = no_of_headers; |
709 | 2.18k | } |
710 | | |
711 | | // cbdata->local_id is the index of the requested header buffer |
712 | | // hdr_td->len is the number of header buffers |
713 | 5.97k | if (cbdata->local_id < hdr_td->len) { |
714 | | // we have one valid header buffer |
715 | 3.79k | InspectionBufferSetupMulti(buffer, transforms, hdr_td->items[cbdata->local_id].buffer, |
716 | 3.79k | hdr_td->items[cbdata->local_id].len); |
717 | 3.79k | SCReturnPtr(buffer, "InspectionBuffer"); |
718 | 3.79k | } // else there are no more header buffer to get |
719 | 2.18k | InspectionBufferSetupMultiEmpty(buffer); |
720 | 2.18k | return NULL; |
721 | 5.97k | } |
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 | 2.13k | { |
726 | 2.13k | SCEnter(); |
727 | | |
728 | 2.13k | const PrefilterMpmListId *ctx = (const PrefilterMpmListId *)pectx; |
729 | 2.13k | const MpmCtx *mpm_ctx = ctx->mpm_ctx; |
730 | 2.13k | const int list_id = ctx->list_id; |
731 | | |
732 | 2.13k | uint32_t local_id = 0; |
733 | | |
734 | 5.91k | while (1) { |
735 | | // loop until we get a NULL |
736 | | |
737 | 5.91k | struct MpmListIdDataArgs cbdata = { local_id, txv }; |
738 | 5.91k | InspectionBuffer *buffer = |
739 | 5.91k | GetHttp1HeaderData(det_ctx, flags, ctx->transforms, f, &cbdata, list_id); |
740 | 5.91k | if (buffer == NULL) |
741 | 2.13k | break; |
742 | | |
743 | 3.77k | if (buffer->inspect_len >= mpm_ctx->minlen) { |
744 | 3.36k | (void)mpm_table[mpm_ctx->mpm_type].Search( |
745 | 3.36k | mpm_ctx, &det_ctx->mtcu, &det_ctx->pmq, buffer->inspect, buffer->inspect_len); |
746 | 3.36k | PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len); |
747 | 3.36k | } |
748 | | |
749 | 3.77k | local_id++; |
750 | 3.77k | } |
751 | 2.13k | } |
752 | | |
753 | | static int PrefilterMpmHttp1HeaderRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, |
754 | | MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id) |
755 | 804 | { |
756 | 804 | PrefilterMpmListId *pectx = SCCalloc(1, sizeof(*pectx)); |
757 | 804 | if (pectx == NULL) |
758 | 0 | return -1; |
759 | 804 | pectx->list_id = list_id; |
760 | 804 | pectx->mpm_ctx = mpm_ctx; |
761 | 804 | pectx->transforms = &mpm_reg->transforms; |
762 | | |
763 | 804 | return PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxHttp1Header, mpm_reg->app_v2.alproto, |
764 | 804 | mpm_reg->app_v2.tx_min_progress, pectx, PrefilterMpmHttpHeaderFree, mpm_reg->name); |
765 | 804 | } |
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 | 2.08k | { |
771 | 2.08k | uint32_t local_id = 0; |
772 | | |
773 | 2.08k | const DetectEngineTransforms *transforms = NULL; |
774 | 2.08k | if (!engine->mpm) { |
775 | 16 | transforms = engine->v2.transforms; |
776 | 16 | } |
777 | | |
778 | 2.66k | while (1) { |
779 | 2.66k | struct MpmListIdDataArgs cbdata = { |
780 | 2.66k | local_id, |
781 | 2.66k | txv, |
782 | 2.66k | }; |
783 | 2.66k | InspectionBuffer *buffer = |
784 | 2.66k | GetHttp1HeaderData(det_ctx, flags, transforms, f, &cbdata, engine->sm_list); |
785 | | |
786 | 2.66k | if (buffer == NULL || buffer->inspect == NULL) |
787 | 1.09k | break; |
788 | | |
789 | 1.57k | det_ctx->buffer_offset = 0; |
790 | 1.57k | det_ctx->discontinue_matching = 0; |
791 | 1.57k | det_ctx->inspection_recursion_counter = 0; |
792 | | |
793 | 1.57k | const int match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, NULL, f, |
794 | 1.57k | (uint8_t *)buffer->inspect, buffer->inspect_len, buffer->inspect_offset, |
795 | 1.57k | DETECT_CI_FLAGS_SINGLE, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); |
796 | 1.57k | if (match == 1) { |
797 | 989 | return DETECT_ENGINE_INSPECT_SIG_MATCH; |
798 | 989 | } |
799 | 586 | local_id++; |
800 | 586 | } |
801 | | |
802 | 1.09k | return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; |
803 | 2.08k | } |
804 | | |
805 | | static int DetectHTTPRequestHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) |
806 | 6.14k | { |
807 | 6.14k | if (DetectBufferSetActiveList(de_ctx, s, g_http_request_header_buffer_id) < 0) |
808 | 121 | return -1; |
809 | | |
810 | 6.02k | if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) != 0) |
811 | 121 | return -1; |
812 | | |
813 | 5.90k | return 0; |
814 | 6.02k | } |
815 | | |
816 | | void DetectHttpRequestHeaderRegister(void) |
817 | 73 | { |
818 | 73 | sigmatch_table[DETECT_HTTP_REQUEST_HEADER].name = "http.request_header"; |
819 | 73 | sigmatch_table[DETECT_HTTP_REQUEST_HEADER].desc = |
820 | 73 | "sticky buffer to match on only one HTTP header name and value"; |
821 | 73 | sigmatch_table[DETECT_HTTP_REQUEST_HEADER].url = "/rules/http-keywords.html#request_header"; |
822 | 73 | sigmatch_table[DETECT_HTTP_REQUEST_HEADER].Setup = DetectHTTPRequestHeaderSetup; |
823 | 73 | sigmatch_table[DETECT_HTTP_REQUEST_HEADER].flags |= |
824 | 73 | SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER; |
825 | | |
826 | 73 | DetectAppLayerMpmRegister2("http_request_header", SIG_FLAG_TOSERVER, 2, |
827 | 73 | PrefilterMpmHttp2HeaderRegister, NULL, ALPROTO_HTTP2, HTTP2StateOpen); |
828 | 73 | DetectAppLayerInspectEngineRegister2("http_request_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, |
829 | 73 | HTTP2StateOpen, DetectEngineInspectHttp2Header, NULL); |
830 | 73 | DetectAppLayerMpmRegister2("http_request_header", SIG_FLAG_TOSERVER, 2, |
831 | 73 | PrefilterMpmHttp1HeaderRegister, NULL, ALPROTO_HTTP1, HTP_REQUEST_HEADERS); |
832 | 73 | DetectAppLayerInspectEngineRegister2("http_request_header", ALPROTO_HTTP1, SIG_FLAG_TOSERVER, |
833 | 73 | HTP_REQUEST_HEADERS, DetectEngineInspectHttp1Header, NULL); |
834 | 73 | DetectBufferTypeSetDescriptionByName("http_request_header", "HTTP header name and value"); |
835 | 73 | g_http_request_header_buffer_id = DetectBufferTypeGetByName("http_request_header"); |
836 | 73 | DetectBufferTypeSupportsMultiInstance("http_request_header"); |
837 | 73 | g_request_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_request_header", |
838 | 73 | HttpMultiBufHeaderThreadDataInit, NULL, HttpMultiBufHeaderThreadDataFree); |
839 | 73 | g_h2_request_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http2_request_header", |
840 | 73 | SCHttp2ThreadMultiBufDataInit, NULL, SCHttp2ThreadMultiBufDataFree); |
841 | 73 | } |
842 | | |
843 | | static int DetectHTTPResponseHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) |
844 | 450 | { |
845 | 450 | if (DetectBufferSetActiveList(de_ctx, s, g_http_response_header_buffer_id) < 0) |
846 | 11 | return -1; |
847 | | |
848 | 439 | if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) != 0) |
849 | 12 | return -1; |
850 | | |
851 | 427 | return 0; |
852 | 439 | } |
853 | | |
854 | | void DetectHttpResponseHeaderRegister(void) |
855 | 73 | { |
856 | 73 | sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].name = "http.response_header"; |
857 | 73 | sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].desc = |
858 | 73 | "sticky buffer to match on only one HTTP header name and value"; |
859 | 73 | sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].url = "/rules/http2-keywords.html#response_header"; |
860 | 73 | sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].Setup = DetectHTTPResponseHeaderSetup; |
861 | 73 | sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].flags |= |
862 | 73 | SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER; |
863 | | |
864 | 73 | DetectAppLayerMpmRegister2("http_response_header", SIG_FLAG_TOCLIENT, 2, |
865 | 73 | PrefilterMpmHttp2HeaderRegister, NULL, ALPROTO_HTTP2, HTTP2StateOpen); |
866 | 73 | DetectAppLayerInspectEngineRegister2("http_response_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, |
867 | 73 | HTTP2StateOpen, DetectEngineInspectHttp2Header, NULL); |
868 | 73 | DetectAppLayerMpmRegister2("http_response_header", SIG_FLAG_TOCLIENT, 2, |
869 | 73 | PrefilterMpmHttp1HeaderRegister, NULL, ALPROTO_HTTP1, HTP_RESPONSE_HEADERS); |
870 | 73 | DetectAppLayerInspectEngineRegister2("http_response_header", ALPROTO_HTTP1, SIG_FLAG_TOCLIENT, |
871 | 73 | HTP_RESPONSE_HEADERS, DetectEngineInspectHttp1Header, NULL); |
872 | | |
873 | 73 | DetectBufferTypeSetDescriptionByName("http_response_header", "HTTP header name and value"); |
874 | 73 | g_http_response_header_buffer_id = DetectBufferTypeGetByName("http_response_header"); |
875 | 73 | DetectBufferTypeSupportsMultiInstance("http_response_header"); |
876 | 73 | g_response_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http_response_header", |
877 | 73 | HttpMultiBufHeaderThreadDataInit, NULL, HttpMultiBufHeaderThreadDataFree); |
878 | 73 | g_h2_response_header_thread_id = DetectRegisterThreadCtxGlobalFuncs("http2_response_header", |
879 | | SCHttp2ThreadMultiBufDataInit, NULL, SCHttp2ThreadMultiBufDataFree); |
880 | 73 | } |
881 | | |
882 | | /************************************Unittests*********************************/ |
883 | | |
884 | | #ifdef UNITTESTS |
885 | | #include "tests/detect-http-header.c" |
886 | | #endif |
887 | | |
888 | | /** |
889 | | * @} |
890 | | */ |