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