/src/suricata8/src/detect-http-host.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2019 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 Anoop Saldanha <anoopsaldanha@gmail.com> |
29 | | * |
30 | | * Implements support for the http_host 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-buffer.h" |
41 | | #include "detect-engine-mpm.h" |
42 | | #include "detect-engine-prefilter.h" |
43 | | #include "detect-content.h" |
44 | | #include "detect-pcre.h" |
45 | | |
46 | | #include "flow.h" |
47 | | #include "flow-var.h" |
48 | | #include "flow-util.h" |
49 | | |
50 | | #include "util-debug.h" |
51 | | #include "util-unittest.h" |
52 | | #include "util-unittest-helper.h" |
53 | | #include "util-spm.h" |
54 | | |
55 | | #include "app-layer.h" |
56 | | #include "app-layer-parser.h" |
57 | | |
58 | | #include "app-layer-htp.h" |
59 | | #include "stream-tcp.h" |
60 | | #include "detect-http-host.h" |
61 | | |
62 | | static int DetectHttpHHSetup(DetectEngineCtx *, Signature *, const char *); |
63 | | #ifdef UNITTESTS |
64 | | static void DetectHttpHHRegisterTests(void); |
65 | | #endif |
66 | | static bool DetectHttpHostValidateCallback( |
67 | | const Signature *s, const char **sigerror, const DetectBufferType *dbt); |
68 | | static int DetectHttpHostSetup(DetectEngineCtx *, Signature *, const char *); |
69 | | static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx, |
70 | | const DetectEngineTransforms *transforms, |
71 | | Flow *_f, const uint8_t _flow_flags, |
72 | | void *txv, const int list_id); |
73 | | static InspectionBuffer *GetData2(DetectEngineThreadCtx *det_ctx, |
74 | | const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv, |
75 | | const int list_id); |
76 | | static int DetectHttpHRHSetup(DetectEngineCtx *, Signature *, const char *); |
77 | | static int g_http_raw_host_buffer_id = 0; |
78 | | static int DetectHttpHostRawSetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str); |
79 | | static InspectionBuffer *GetRawData(DetectEngineThreadCtx *det_ctx, |
80 | | const DetectEngineTransforms *transforms, Flow *_f, |
81 | | const uint8_t _flow_flags, void *txv, const int list_id); |
82 | | static InspectionBuffer *GetRawData2(DetectEngineThreadCtx *det_ctx, |
83 | | const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv, |
84 | | const int list_id); |
85 | | static int g_http_host_buffer_id = 0; |
86 | | static int g_http2_thread_id = 0; |
87 | | static int g_http2_raw_thread_id = 0; |
88 | | |
89 | | /** |
90 | | * \brief Registers the keyword handlers for the "http_host" keyword. |
91 | | */ |
92 | | void DetectHttpHHRegister(void) |
93 | 79 | { |
94 | | /* http_host content modifier */ |
95 | 79 | sigmatch_table[DETECT_HTTP_HOST_CM].name = "http_host"; |
96 | 79 | sigmatch_table[DETECT_HTTP_HOST_CM].desc = "content modifier to match on the HTTP hostname"; |
97 | 79 | sigmatch_table[DETECT_HTTP_HOST_CM].url = |
98 | 79 | "/rules/http-keywords.html#http-host-and-http-raw-host"; |
99 | 79 | sigmatch_table[DETECT_HTTP_HOST_CM].Setup = DetectHttpHHSetup; |
100 | | #ifdef UNITTESTS |
101 | | sigmatch_table[DETECT_HTTP_HOST_CM].RegisterTests = DetectHttpHHRegisterTests; |
102 | | #endif |
103 | 79 | sigmatch_table[DETECT_HTTP_HOST_CM].flags |= SIGMATCH_NOOPT | SIGMATCH_INFO_CONTENT_MODIFIER; |
104 | 79 | sigmatch_table[DETECT_HTTP_HOST_CM].alternative = DETECT_HTTP_HOST; |
105 | | |
106 | | /* http.host sticky buffer */ |
107 | 79 | sigmatch_table[DETECT_HTTP_HOST].name = "http.host"; |
108 | 79 | sigmatch_table[DETECT_HTTP_HOST].desc = "sticky buffer to match on the HTTP Host buffer"; |
109 | 79 | sigmatch_table[DETECT_HTTP_HOST].url = "/rules/http-keywords.html#http-host-and-http-raw-host"; |
110 | 79 | sigmatch_table[DETECT_HTTP_HOST].Setup = DetectHttpHostSetup; |
111 | 79 | sigmatch_table[DETECT_HTTP_HOST].flags |= SIGMATCH_NOOPT|SIGMATCH_INFO_STICKY_BUFFER; |
112 | | |
113 | 79 | DetectAppLayerInspectEngineRegister("http_host", ALPROTO_HTTP1, SIG_FLAG_TOSERVER, |
114 | 79 | HTP_REQUEST_PROGRESS_HEADERS, DetectEngineInspectBufferGeneric, GetData); |
115 | | |
116 | 79 | DetectAppLayerMpmRegister("http_host", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, |
117 | 79 | GetData, ALPROTO_HTTP1, HTP_REQUEST_PROGRESS_HEADERS); |
118 | | |
119 | 79 | DetectAppLayerInspectEngineRegister("http_host", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, |
120 | 79 | HTTP2StateOpen, DetectEngineInspectBufferGeneric, GetData2); |
121 | | |
122 | 79 | DetectAppLayerMpmRegister("http_host", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, |
123 | 79 | GetData2, ALPROTO_HTTP2, HTTP2StateOpen); |
124 | | |
125 | 79 | DetectBufferTypeRegisterValidateCallback("http_host", |
126 | 79 | DetectHttpHostValidateCallback); |
127 | | |
128 | 79 | DetectBufferTypeSetDescriptionByName("http_host", |
129 | 79 | "http host"); |
130 | | |
131 | 79 | g_http2_thread_id = DetectRegisterThreadCtxGlobalFuncs( |
132 | 79 | "http_host", SCHttp2ThreadBufDataInit, NULL, SCHttp2ThreadBufDataFree); |
133 | | |
134 | 79 | g_http_host_buffer_id = DetectBufferTypeGetByName("http_host"); |
135 | | |
136 | | /* http_raw_host content modifier */ |
137 | 79 | sigmatch_table[DETECT_HTTP_RAW_HOST].name = "http_raw_host"; |
138 | 79 | sigmatch_table[DETECT_HTTP_RAW_HOST].desc = "content modifier to match on the HTTP host header " |
139 | 79 | "or the raw hostname from the HTTP uri"; |
140 | 79 | sigmatch_table[DETECT_HTTP_RAW_HOST].url = |
141 | 79 | "/rules/http-keywords.html#http-host-and-http-raw-host"; |
142 | 79 | sigmatch_table[DETECT_HTTP_RAW_HOST].Setup = DetectHttpHRHSetup; |
143 | 79 | sigmatch_table[DETECT_HTTP_RAW_HOST].flags |= SIGMATCH_NOOPT | SIGMATCH_INFO_CONTENT_MODIFIER; |
144 | 79 | sigmatch_table[DETECT_HTTP_RAW_HOST].alternative = DETECT_HTTP_HOST_RAW; |
145 | | |
146 | | /* http.host sticky buffer */ |
147 | 79 | sigmatch_table[DETECT_HTTP_HOST_RAW].name = "http.host.raw"; |
148 | 79 | sigmatch_table[DETECT_HTTP_HOST_RAW].desc = "sticky buffer to match on the HTTP host header or the raw hostname from the HTTP uri"; |
149 | 79 | sigmatch_table[DETECT_HTTP_HOST_RAW].url = "/rules/http-keywords.html#http-host-and-http-raw-host"; |
150 | 79 | sigmatch_table[DETECT_HTTP_HOST_RAW].Setup = DetectHttpHostRawSetupSticky; |
151 | 79 | sigmatch_table[DETECT_HTTP_HOST_RAW].flags |= SIGMATCH_NOOPT|SIGMATCH_INFO_STICKY_BUFFER; |
152 | | |
153 | 79 | DetectAppLayerInspectEngineRegister("http_raw_host", ALPROTO_HTTP1, SIG_FLAG_TOSERVER, |
154 | 79 | HTP_REQUEST_PROGRESS_HEADERS, DetectEngineInspectBufferGeneric, GetRawData); |
155 | | |
156 | 79 | DetectAppLayerMpmRegister("http_raw_host", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, |
157 | 79 | GetRawData, ALPROTO_HTTP1, HTP_REQUEST_PROGRESS_HEADERS); |
158 | | |
159 | 79 | DetectAppLayerInspectEngineRegister("http_raw_host", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, |
160 | 79 | HTTP2StateOpen, DetectEngineInspectBufferGeneric, GetRawData2); |
161 | | |
162 | 79 | DetectAppLayerMpmRegister("http_raw_host", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, |
163 | 79 | GetRawData2, ALPROTO_HTTP2, HTTP2StateOpen); |
164 | | |
165 | 79 | DetectBufferTypeSetDescriptionByName("http_raw_host", |
166 | 79 | "http raw host header"); |
167 | | |
168 | 79 | g_http2_raw_thread_id = DetectRegisterThreadCtxGlobalFuncs( |
169 | 79 | "http_raw_host", SCHttp2ThreadBufDataInit, NULL, SCHttp2ThreadBufDataFree); |
170 | | |
171 | 79 | g_http_raw_host_buffer_id = DetectBufferTypeGetByName("http_raw_host"); |
172 | 79 | } |
173 | | |
174 | | /** |
175 | | * \brief The setup function for the http_host keyword for a signature. |
176 | | * |
177 | | * \param de_ctx Pointer to the detection engine context. |
178 | | * \param s Pointer to the signature for the current Signature being |
179 | | * parsed from the rules. |
180 | | * \param m Pointer to the head of the SigMatch for the current rule |
181 | | * being parsed. |
182 | | * \param arg Pointer to the string holding the keyword value. |
183 | | * |
184 | | * \retval 0 On success |
185 | | * \retval -1 On failure |
186 | | */ |
187 | | static int DetectHttpHHSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) |
188 | 1.21k | { |
189 | 1.21k | return DetectEngineContentModifierBufferSetup( |
190 | 1.21k | de_ctx, s, arg, DETECT_HTTP_HOST_CM, g_http_host_buffer_id, ALPROTO_HTTP1); |
191 | 1.21k | } |
192 | | |
193 | | static bool DetectHttpHostValidateCallback( |
194 | | const Signature *s, const char **sigerror, const DetectBufferType *dbt) |
195 | 11.1k | { |
196 | 23.3k | for (uint32_t x = 0; x < s->init_data->buffer_index; x++) { |
197 | 12.2k | if (s->init_data->buffers[x].id != (uint32_t)dbt->id) |
198 | 1.06k | continue; |
199 | 11.1k | const SigMatch *sm = s->init_data->buffers[x].head; |
200 | 26.9k | for (; sm != NULL; sm = sm->next) { |
201 | 15.8k | if (sm->type == DETECT_CONTENT) { |
202 | 2.77k | DetectContentData *cd = (DetectContentData *)sm->ctx; |
203 | 2.77k | if (cd->flags & DETECT_CONTENT_NOCASE) { |
204 | 6 | *sigerror = "http.host keyword " |
205 | 6 | "specified along with \"nocase\". " |
206 | 6 | "The hostname buffer is normalized " |
207 | 6 | "to lowercase, specifying " |
208 | 6 | "nocase is redundant."; |
209 | 6 | SCLogWarning("rule %u: %s", s->id, *sigerror); |
210 | 6 | return false; |
211 | 2.77k | } else { |
212 | 2.77k | uint32_t u; |
213 | 17.1k | for (u = 0; u < cd->content_len; u++) { |
214 | 14.4k | if (isupper(cd->content[u])) |
215 | 66 | break; |
216 | 14.4k | } |
217 | 2.77k | if (u != cd->content_len) { |
218 | 66 | *sigerror = "A pattern with " |
219 | 66 | "uppercase characters detected for http.host. " |
220 | 66 | "The hostname buffer is normalized to lowercase, " |
221 | 66 | "please specify a lowercase pattern."; |
222 | 66 | SCLogWarning("rule %u: %s", s->id, *sigerror); |
223 | 66 | return false; |
224 | 66 | } |
225 | 2.77k | } |
226 | 2.77k | } |
227 | 15.8k | } |
228 | 11.1k | } |
229 | | |
230 | 11.1k | return true; |
231 | 11.1k | } |
232 | | |
233 | | /** |
234 | | * \brief this function setup the http.host keyword used in the rule |
235 | | * |
236 | | * \param de_ctx Pointer to the Detection Engine Context |
237 | | * \param s Pointer to the Signature to which the current keyword belongs |
238 | | * \param str Should hold an empty string always |
239 | | * |
240 | | * \retval 0 On success |
241 | | */ |
242 | | static int DetectHttpHostSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str) |
243 | 30.5k | { |
244 | 30.5k | if (SCDetectBufferSetActiveList(de_ctx, s, g_http_host_buffer_id) < 0) |
245 | 123 | return -1; |
246 | 30.4k | if (SCDetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0) |
247 | 232 | return -1; |
248 | 30.2k | return 0; |
249 | 30.4k | } |
250 | | |
251 | | static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx, |
252 | | const DetectEngineTransforms *transforms, Flow *_f, |
253 | | const uint8_t _flow_flags, void *txv, const int list_id) |
254 | 1.96k | { |
255 | 1.96k | InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); |
256 | 1.96k | if (buffer->inspect == NULL) { |
257 | 1.94k | htp_tx_t *tx = (htp_tx_t *)txv; |
258 | | |
259 | 1.94k | if (htp_tx_request_hostname(tx) == NULL) |
260 | 1.56k | return NULL; |
261 | | |
262 | 376 | const uint32_t data_len = (uint32_t)bstr_len(htp_tx_request_hostname(tx)); |
263 | 376 | const uint8_t *data = bstr_ptr(htp_tx_request_hostname(tx)); |
264 | | |
265 | 376 | InspectionBufferSetupAndApplyTransforms( |
266 | 376 | det_ctx, list_id, buffer, data, data_len, transforms); |
267 | 376 | } |
268 | | |
269 | 395 | return buffer; |
270 | 1.96k | } |
271 | | |
272 | | static InspectionBuffer *GetData2(DetectEngineThreadCtx *det_ctx, |
273 | | const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv, |
274 | | const int list_id) |
275 | 194 | { |
276 | 194 | InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); |
277 | 194 | if (buffer->inspect == NULL) { |
278 | 118 | uint32_t b_len = 0; |
279 | 118 | const uint8_t *b = NULL; |
280 | 118 | void *thread_buf = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_thread_id); |
281 | 118 | if (thread_buf == NULL) |
282 | 0 | return NULL; |
283 | 118 | if (SCHttp2TxGetHostNorm(txv, &b, &b_len, thread_buf) != 1) |
284 | 14 | return NULL; |
285 | 104 | if (b == NULL || b_len == 0) |
286 | 1 | return NULL; |
287 | | |
288 | 103 | InspectionBufferSetupAndApplyTransforms(det_ctx, list_id, buffer, b, b_len, transforms); |
289 | 103 | } |
290 | | |
291 | 179 | return buffer; |
292 | 194 | } |
293 | | |
294 | | static InspectionBuffer *GetRawData2(DetectEngineThreadCtx *det_ctx, |
295 | | const DetectEngineTransforms *transforms, Flow *_f, const uint8_t _flow_flags, void *txv, |
296 | | const int list_id) |
297 | 150 | { |
298 | 150 | InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); |
299 | 150 | if (buffer->inspect == NULL) { |
300 | 141 | uint32_t b_len = 0; |
301 | 141 | const uint8_t *b = NULL; |
302 | 141 | void *thread_buf = DetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_raw_thread_id); |
303 | 141 | if (thread_buf == NULL) |
304 | 0 | return NULL; |
305 | | |
306 | 141 | if (SCHttp2TxGetHost(txv, &b, &b_len, thread_buf) != 1) |
307 | 8 | return NULL; |
308 | 133 | if (b == NULL || b_len == 0) |
309 | 0 | return NULL; |
310 | | |
311 | 133 | InspectionBufferSetupAndApplyTransforms(det_ctx, list_id, buffer, b, b_len, transforms); |
312 | 133 | } |
313 | | |
314 | 142 | return buffer; |
315 | 150 | } |
316 | | |
317 | | /** |
318 | | * \brief The setup function for the http_raw_host keyword for a signature. |
319 | | * |
320 | | * \param de_ctx Pointer to the detection engine context. |
321 | | * \param s Pointer to the signature for the current Signature being |
322 | | * parsed from the rules. |
323 | | * \param m Pointer to the head of the SigMatch for the current rule |
324 | | * being parsed. |
325 | | * \param arg Pointer to the string holding the keyword value. |
326 | | * |
327 | | * \retval 0 On success |
328 | | * \retval -1 On failure |
329 | | */ |
330 | | int DetectHttpHRHSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) |
331 | 381 | { |
332 | 381 | return DetectEngineContentModifierBufferSetup( |
333 | 381 | de_ctx, s, arg, DETECT_HTTP_RAW_HOST, g_http_raw_host_buffer_id, ALPROTO_HTTP1); |
334 | 381 | } |
335 | | |
336 | | /** |
337 | | * \brief this function setup the http.host keyword used in the rule |
338 | | * |
339 | | * \param de_ctx Pointer to the Detection Engine Context |
340 | | * \param s Pointer to the Signature to which the current keyword belongs |
341 | | * \param str Should hold an empty string always |
342 | | * |
343 | | * \retval 0 On success |
344 | | */ |
345 | | static int DetectHttpHostRawSetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str) |
346 | 1.61k | { |
347 | 1.61k | if (SCDetectBufferSetActiveList(de_ctx, s, g_http_raw_host_buffer_id) < 0) |
348 | 1 | return -1; |
349 | 1.61k | if (SCDetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0) |
350 | 10 | return -1; |
351 | 1.60k | return 0; |
352 | 1.61k | } |
353 | | |
354 | | static InspectionBuffer *GetRawData(DetectEngineThreadCtx *det_ctx, |
355 | | const DetectEngineTransforms *transforms, Flow *_f, |
356 | | const uint8_t _flow_flags, void *txv, const int list_id) |
357 | 2.39k | { |
358 | 2.39k | InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id); |
359 | 2.39k | if (buffer->inspect == NULL) { |
360 | 2.39k | htp_tx_t *tx = (htp_tx_t *)txv; |
361 | | |
362 | 2.39k | const uint8_t *data = NULL; |
363 | 2.39k | uint32_t data_len = 0; |
364 | | |
365 | 2.39k | if (htp_uri_hostname(htp_tx_parsed_uri(tx)) == NULL) { |
366 | 1.17k | if (htp_tx_request_headers(tx) == NULL) |
367 | 0 | return NULL; |
368 | | |
369 | 1.17k | const htp_header_t *h = htp_tx_request_header(tx, "Host"); |
370 | 1.17k | if (h == NULL || htp_header_value(h) == NULL) |
371 | 508 | return NULL; |
372 | | |
373 | 671 | data = htp_header_value_ptr(h); |
374 | 671 | data_len = (uint32_t)htp_header_value_len(h); |
375 | 1.21k | } else { |
376 | 1.21k | data = (const uint8_t *)bstr_ptr(htp_uri_hostname(htp_tx_parsed_uri(tx))); |
377 | 1.21k | data_len = (uint32_t)bstr_len(htp_uri_hostname(htp_tx_parsed_uri(tx))); |
378 | 1.21k | } |
379 | | |
380 | 1.88k | InspectionBufferSetupAndApplyTransforms( |
381 | 1.88k | det_ctx, list_id, buffer, data, data_len, transforms); |
382 | 1.88k | } |
383 | | |
384 | 1.88k | return buffer; |
385 | 2.39k | } |
386 | | |
387 | | /************************************Unittests*********************************/ |
388 | | |
389 | | #ifdef UNITTESTS |
390 | | #include "tests/detect-http-host.c" |
391 | | #endif /* UNITTESTS */ |
392 | | |
393 | | /** |
394 | | * @} |
395 | | */ |