/src/suricata/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 | 74 | { |
94 | | /* http_host content modifier */ |
95 | 74 | sigmatch_table[DETECT_HTTP_HOST_CM].name = "http_host"; |
96 | 74 | sigmatch_table[DETECT_HTTP_HOST_CM].desc = "content modifier to match on the HTTP hostname"; |
97 | | // no doc url for this obsolete keyword, see http.host |
98 | 74 | sigmatch_table[DETECT_HTTP_HOST_CM].Setup = DetectHttpHHSetup; |
99 | | #ifdef UNITTESTS |
100 | | sigmatch_table[DETECT_HTTP_HOST_CM].RegisterTests = DetectHttpHHRegisterTests; |
101 | | #endif |
102 | 74 | sigmatch_table[DETECT_HTTP_HOST_CM].flags |= SIGMATCH_NOOPT | SIGMATCH_INFO_CONTENT_MODIFIER; |
103 | 74 | sigmatch_table[DETECT_HTTP_HOST_CM].alternative = DETECT_HTTP_HOST; |
104 | | |
105 | | /* http.host sticky buffer */ |
106 | 74 | sigmatch_table[DETECT_HTTP_HOST].name = "http.host"; |
107 | 74 | sigmatch_table[DETECT_HTTP_HOST].desc = "sticky buffer to match on the HTTP Host buffer"; |
108 | 74 | sigmatch_table[DETECT_HTTP_HOST].url = "/rules/http-keywords.html#http-host"; |
109 | 74 | sigmatch_table[DETECT_HTTP_HOST].Setup = DetectHttpHostSetup; |
110 | 74 | sigmatch_table[DETECT_HTTP_HOST].flags |= SIGMATCH_NOOPT|SIGMATCH_INFO_STICKY_BUFFER; |
111 | | |
112 | 74 | DetectAppLayerInspectEngineRegister("http_host", ALPROTO_HTTP1, SIG_FLAG_TOSERVER, |
113 | 74 | HTP_REQUEST_PROGRESS_HEADERS, DetectEngineInspectBufferGeneric, GetData); |
114 | | |
115 | 74 | DetectAppLayerMpmRegister("http_host", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, |
116 | 74 | GetData, ALPROTO_HTTP1, HTP_REQUEST_PROGRESS_HEADERS); |
117 | | |
118 | 74 | DetectAppLayerInspectEngineRegisterSubState("http_host", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, |
119 | 74 | HTTP2TxTypeStream, HTTP2ProgHeaders, DetectEngineInspectBufferGeneric, GetData2); |
120 | | |
121 | 74 | DetectAppLayerMpmRegisterSubState("http_host", SIG_FLAG_TOSERVER, 2, |
122 | 74 | PrefilterGenericMpmRegister, GetData2, ALPROTO_HTTP2, HTTP2TxTypeStream, |
123 | 74 | HTTP2ProgHeaders); |
124 | | |
125 | 74 | DetectBufferTypeRegisterValidateCallback("http_host", |
126 | 74 | DetectHttpHostValidateCallback); |
127 | | |
128 | 74 | DetectBufferTypeSetDescriptionByName("http_host", |
129 | 74 | "http host"); |
130 | | |
131 | 74 | g_http2_thread_id = SCDetectRegisterThreadCtxGlobalFuncs( |
132 | 74 | "http_host", SCDetectThreadBufDataInit, NULL, SCDetectThreadBufDataFree); |
133 | | |
134 | 74 | g_http_host_buffer_id = DetectBufferTypeGetByName("http_host"); |
135 | | |
136 | | /* http_raw_host content modifier */ |
137 | 74 | sigmatch_table[DETECT_HTTP_RAW_HOST].name = "http_raw_host"; |
138 | 74 | sigmatch_table[DETECT_HTTP_RAW_HOST].desc = "content modifier to match on the HTTP host header " |
139 | 74 | "or the raw hostname from the HTTP uri"; |
140 | | // no doc url for this obsolete keyword, see http.host.raw |
141 | 74 | sigmatch_table[DETECT_HTTP_RAW_HOST].Setup = DetectHttpHRHSetup; |
142 | 74 | sigmatch_table[DETECT_HTTP_RAW_HOST].flags |= SIGMATCH_NOOPT | SIGMATCH_INFO_CONTENT_MODIFIER; |
143 | 74 | sigmatch_table[DETECT_HTTP_RAW_HOST].alternative = DETECT_HTTP_HOST_RAW; |
144 | | |
145 | | /* http.host sticky buffer */ |
146 | 74 | sigmatch_table[DETECT_HTTP_HOST_RAW].name = "http.host.raw"; |
147 | 74 | sigmatch_table[DETECT_HTTP_HOST_RAW].desc = "sticky buffer to match on the HTTP host header or the raw hostname from the HTTP uri"; |
148 | 74 | sigmatch_table[DETECT_HTTP_HOST_RAW].url = "/rules/http-keywords.html#http-host-raw"; |
149 | 74 | sigmatch_table[DETECT_HTTP_HOST_RAW].Setup = DetectHttpHostRawSetupSticky; |
150 | 74 | sigmatch_table[DETECT_HTTP_HOST_RAW].flags |= SIGMATCH_NOOPT|SIGMATCH_INFO_STICKY_BUFFER; |
151 | | |
152 | 74 | DetectAppLayerInspectEngineRegister("http_raw_host", ALPROTO_HTTP1, SIG_FLAG_TOSERVER, |
153 | 74 | HTP_REQUEST_PROGRESS_HEADERS, DetectEngineInspectBufferGeneric, GetRawData); |
154 | | |
155 | 74 | DetectAppLayerMpmRegister("http_raw_host", SIG_FLAG_TOSERVER, 2, PrefilterGenericMpmRegister, |
156 | 74 | GetRawData, ALPROTO_HTTP1, HTP_REQUEST_PROGRESS_HEADERS); |
157 | | |
158 | 74 | DetectAppLayerInspectEngineRegisterSubState("http_raw_host", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, |
159 | 74 | HTTP2TxTypeStream, HTTP2ProgHeaders, DetectEngineInspectBufferGeneric, GetRawData2); |
160 | | |
161 | 74 | DetectAppLayerMpmRegisterSubState("http_raw_host", SIG_FLAG_TOSERVER, 2, |
162 | 74 | PrefilterGenericMpmRegister, GetRawData2, ALPROTO_HTTP2, HTTP2TxTypeStream, |
163 | 74 | HTTP2ProgHeaders); |
164 | | |
165 | 74 | DetectBufferTypeSetDescriptionByName("http_raw_host", |
166 | 74 | "http raw host header"); |
167 | | |
168 | 74 | g_http2_raw_thread_id = SCDetectRegisterThreadCtxGlobalFuncs( |
169 | 74 | "http_raw_host", SCDetectThreadBufDataInit, NULL, SCDetectThreadBufDataFree); |
170 | | |
171 | 74 | g_http_raw_host_buffer_id = DetectBufferTypeGetByName("http_raw_host"); |
172 | 74 | } |
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.97k | { |
189 | 1.97k | return DetectEngineContentModifierBufferSetup( |
190 | 1.97k | de_ctx, s, arg, DETECT_HTTP_HOST_CM, g_http_host_buffer_id, ALPROTO_HTTP1); |
191 | 1.97k | } |
192 | | |
193 | | static bool DetectHttpHostValidateCallback( |
194 | | const Signature *s, const char **sigerror, const DetectBufferType *dbt) |
195 | 17.6k | { |
196 | 60.3k | for (uint32_t x = 0; x < s->init_data->buffer_index; x++) { |
197 | 42.8k | if (s->init_data->buffers[x].id != (uint32_t)dbt->id) |
198 | 28.2k | continue; |
199 | 14.6k | const SigMatch *sm = s->init_data->buffers[x].head; |
200 | 34.2k | for (; sm != NULL; sm = sm->next) { |
201 | 19.7k | if (sm->type == DETECT_CONTENT) { |
202 | 3.91k | DetectContentData *cd = (DetectContentData *)sm->ctx; |
203 | 3.91k | if (cd->flags & DETECT_CONTENT_NOCASE) { |
204 | 8 | *sigerror = "http.host keyword " |
205 | 8 | "specified along with \"nocase\". " |
206 | 8 | "The hostname buffer is normalized " |
207 | 8 | "to lowercase, specifying " |
208 | 8 | "nocase is redundant."; |
209 | 8 | SCLogWarning("rule %u: %s", s->id, *sigerror); |
210 | 8 | return false; |
211 | 3.90k | } else { |
212 | 3.90k | uint32_t u; |
213 | 20.0k | for (u = 0; u < cd->content_len; u++) { |
214 | 16.2k | if (isupper(cd->content[u])) |
215 | 104 | break; |
216 | 16.2k | } |
217 | 3.90k | if (u != cd->content_len) { |
218 | 104 | *sigerror = "A pattern with " |
219 | 104 | "uppercase characters detected for http.host. " |
220 | 104 | "The hostname buffer is normalized to lowercase, " |
221 | 104 | "please specify a lowercase pattern."; |
222 | 104 | SCLogWarning("rule %u: %s", s->id, *sigerror); |
223 | 104 | return false; |
224 | 104 | } |
225 | 3.90k | } |
226 | 3.91k | } |
227 | 19.7k | } |
228 | 14.6k | } |
229 | | |
230 | 17.5k | return true; |
231 | 17.6k | } |
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 | 46.3k | { |
244 | 46.3k | if (SCDetectBufferSetActiveList(de_ctx, s, g_http_host_buffer_id) < 0) |
245 | 456 | return -1; |
246 | 45.8k | if (SCDetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0) |
247 | 236 | return -1; |
248 | 45.6k | return 0; |
249 | 45.8k | } |
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 | 154 | { |
255 | 154 | InspectionBuffer *buffer = SCInspectionBufferGet(det_ctx, list_id); |
256 | 154 | if (buffer->inspect == NULL) { |
257 | 143 | htp_tx_t *tx = (htp_tx_t *)txv; |
258 | | |
259 | 143 | if (htp_tx_request_hostname(tx) == NULL) |
260 | 93 | return NULL; |
261 | | |
262 | 50 | const uint32_t data_len = (uint32_t)bstr_len(htp_tx_request_hostname(tx)); |
263 | 50 | const uint8_t *data = bstr_ptr(htp_tx_request_hostname(tx)); |
264 | | |
265 | 50 | SCInspectionBufferSetupAndApplyTransforms( |
266 | 50 | det_ctx, list_id, buffer, data, data_len, transforms); |
267 | 50 | } |
268 | | |
269 | 61 | return buffer; |
270 | 154 | } |
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 | 579 | { |
276 | 579 | InspectionBuffer *buffer = SCInspectionBufferGet(det_ctx, list_id); |
277 | 579 | if (buffer->inspect == NULL) { |
278 | 431 | uint32_t b_len = 0; |
279 | 431 | const uint8_t *b = NULL; |
280 | 431 | void *thread_buf = SCDetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_thread_id); |
281 | 431 | if (thread_buf == NULL) |
282 | 0 | return NULL; |
283 | 431 | if (SCHttp2TxGetHostNorm(txv, &b, &b_len, thread_buf) != 1) |
284 | 238 | return NULL; |
285 | 193 | if (b == NULL || b_len == 0) |
286 | 1 | return NULL; |
287 | | |
288 | 192 | SCInspectionBufferSetupAndApplyTransforms(det_ctx, list_id, buffer, b, b_len, transforms); |
289 | 192 | } |
290 | | |
291 | 340 | return buffer; |
292 | 579 | } |
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 | 1.21k | { |
298 | 1.21k | InspectionBuffer *buffer = SCInspectionBufferGet(det_ctx, list_id); |
299 | 1.21k | if (buffer->inspect == NULL) { |
300 | 1.19k | uint32_t b_len = 0; |
301 | 1.19k | const uint8_t *b = NULL; |
302 | 1.19k | void *thread_buf = |
303 | 1.19k | SCDetectThreadCtxGetGlobalKeywordThreadCtx(det_ctx, g_http2_raw_thread_id); |
304 | 1.19k | if (thread_buf == NULL) |
305 | 0 | return NULL; |
306 | | |
307 | 1.19k | if (SCHttp2TxGetHost(txv, &b, &b_len, thread_buf) != 1) |
308 | 766 | return NULL; |
309 | 429 | if (b == NULL || b_len == 0) |
310 | 0 | return NULL; |
311 | | |
312 | 429 | SCInspectionBufferSetupAndApplyTransforms(det_ctx, list_id, buffer, b, b_len, transforms); |
313 | 429 | } |
314 | | |
315 | 445 | return buffer; |
316 | 1.21k | } |
317 | | |
318 | | /** |
319 | | * \brief The setup function for the http_raw_host keyword for a signature. |
320 | | * |
321 | | * \param de_ctx Pointer to the detection engine context. |
322 | | * \param s Pointer to the signature for the current Signature being |
323 | | * parsed from the rules. |
324 | | * \param m Pointer to the head of the SigMatch for the current rule |
325 | | * being parsed. |
326 | | * \param arg Pointer to the string holding the keyword value. |
327 | | * |
328 | | * \retval 0 On success |
329 | | * \retval -1 On failure |
330 | | */ |
331 | | int DetectHttpHRHSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) |
332 | 617 | { |
333 | 617 | return DetectEngineContentModifierBufferSetup( |
334 | 617 | de_ctx, s, arg, DETECT_HTTP_RAW_HOST, g_http_raw_host_buffer_id, ALPROTO_HTTP1); |
335 | 617 | } |
336 | | |
337 | | /** |
338 | | * \brief this function setup the http.host keyword used in the rule |
339 | | * |
340 | | * \param de_ctx Pointer to the Detection Engine Context |
341 | | * \param s Pointer to the Signature to which the current keyword belongs |
342 | | * \param str Should hold an empty string always |
343 | | * |
344 | | * \retval 0 On success |
345 | | */ |
346 | | static int DetectHttpHostRawSetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str) |
347 | 4.01k | { |
348 | 4.01k | if (SCDetectBufferSetActiveList(de_ctx, s, g_http_raw_host_buffer_id) < 0) |
349 | 2 | return -1; |
350 | 4.01k | if (SCDetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0) |
351 | 188 | return -1; |
352 | 3.82k | return 0; |
353 | 4.01k | } |
354 | | |
355 | | static InspectionBuffer *GetRawData(DetectEngineThreadCtx *det_ctx, |
356 | | const DetectEngineTransforms *transforms, Flow *_f, |
357 | | const uint8_t _flow_flags, void *txv, const int list_id) |
358 | 2.97k | { |
359 | 2.97k | InspectionBuffer *buffer = SCInspectionBufferGet(det_ctx, list_id); |
360 | 2.97k | if (buffer->inspect == NULL) { |
361 | 2.97k | htp_tx_t *tx = (htp_tx_t *)txv; |
362 | | |
363 | 2.97k | const uint8_t *data = NULL; |
364 | 2.97k | uint32_t data_len = 0; |
365 | | |
366 | 2.97k | if (htp_uri_hostname(htp_tx_parsed_uri(tx)) == NULL) { |
367 | 1.53k | if (htp_tx_request_headers(tx) == NULL) |
368 | 0 | return NULL; |
369 | | |
370 | 1.53k | const htp_header_t *h = htp_tx_request_header(tx, "Host"); |
371 | 1.53k | if (h == NULL || htp_header_value(h) == NULL) |
372 | 798 | return NULL; |
373 | | |
374 | 735 | data = htp_header_value_ptr(h); |
375 | 735 | data_len = (uint32_t)htp_header_value_len(h); |
376 | 1.44k | } else { |
377 | 1.44k | data = (const uint8_t *)bstr_ptr(htp_uri_hostname(htp_tx_parsed_uri(tx))); |
378 | 1.44k | data_len = (uint32_t)bstr_len(htp_uri_hostname(htp_tx_parsed_uri(tx))); |
379 | 1.44k | } |
380 | | |
381 | 2.18k | SCInspectionBufferSetupAndApplyTransforms( |
382 | 2.18k | det_ctx, list_id, buffer, data, data_len, transforms); |
383 | 2.18k | } |
384 | | |
385 | 2.18k | return buffer; |
386 | 2.97k | } |
387 | | |
388 | | /************************************Unittests*********************************/ |
389 | | |
390 | | #ifdef UNITTESTS |
391 | | #include "tests/detect-http-host.c" |
392 | | #endif /* UNITTESTS */ |
393 | | |
394 | | /** |
395 | | * @} |
396 | | */ |