/src/suricata7/src/detect-pcre.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 | | * \file |
20 | | * |
21 | | * \author Victor Julien <victor@inliniac.net> |
22 | | * |
23 | | * Implements the pcre keyword |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | #include "decode.h" |
28 | | #include "detect.h" |
29 | | |
30 | | #include "pkt-var.h" |
31 | | #include "flow-var.h" |
32 | | #include "flow-util.h" |
33 | | |
34 | | #include "detect-pcre.h" |
35 | | #include "detect-flowvar.h" |
36 | | |
37 | | #include "detect-parse.h" |
38 | | #include "detect-content.h" |
39 | | #include "detect-engine.h" |
40 | | #include "detect-engine-sigorder.h" |
41 | | #include "detect-engine-mpm.h" |
42 | | #include "detect-engine-state.h" |
43 | | #include "detect-engine-build.h" |
44 | | |
45 | | #include "util-var-name.h" |
46 | | #include "util-unittest-helper.h" |
47 | | #include "util-debug.h" |
48 | | #include "util-unittest.h" |
49 | | #include "util-print.h" |
50 | | #include "util-pool.h" |
51 | | |
52 | | #include "conf.h" |
53 | | #include "app-layer.h" |
54 | | #include "app-layer-htp.h" |
55 | | #include "stream.h" |
56 | | #include "stream-tcp.h" |
57 | | #include "stream-tcp-private.h" |
58 | | #include "stream-tcp-reassemble.h" |
59 | | #include "app-layer-protos.h" |
60 | | #include "app-layer-parser.h" |
61 | | #include "util-pages.h" |
62 | | |
63 | | /* pcre named substring capture supports only 32byte names, A-z0-9 plus _ |
64 | | * and needs to start with non-numeric. */ |
65 | 34 | #define PARSE_CAPTURE_REGEX "\\(\\?P\\<([A-z]+)\\_([A-z0-9_]+)\\>" |
66 | 34 | #define PARSE_REGEX "(?<!\\\\)/(.*(?<!(?<!\\\\)\\\\))/([^\"]*)" |
67 | | |
68 | | static int pcre_match_limit = 0; |
69 | | static int pcre_match_limit_recursion = 0; |
70 | | |
71 | | static DetectParseRegex *parse_regex; |
72 | | static DetectParseRegex *parse_capture_regex; |
73 | | |
74 | | #ifdef PCRE2_HAVE_JIT |
75 | | static int pcre2_use_jit = 1; |
76 | | #endif |
77 | | |
78 | | // TODOpcre2 pcre2_jit_stack_create ? |
79 | | |
80 | | /* \brief Helper function for using pcre2_match with/without JIT |
81 | | */ |
82 | | static inline int DetectPcreExec(DetectEngineThreadCtx *det_ctx, DetectPcreData *pd, |
83 | | const char *str, const size_t strlen, int start_offset, int options, |
84 | | pcre2_match_data *match) |
85 | 410k | { |
86 | 410k | return pcre2_match(pd->parse_regex.regex, (PCRE2_SPTR8)str, strlen, start_offset, options, |
87 | 410k | match, pd->parse_regex.context); |
88 | 410k | } |
89 | | |
90 | | static int DetectPcreSetup (DetectEngineCtx *, Signature *, const char *); |
91 | | static void DetectPcreFree(DetectEngineCtx *, void *); |
92 | | #ifdef UNITTESTS |
93 | | static void DetectPcreRegisterTests(void); |
94 | | #endif |
95 | | |
96 | | void DetectPcreRegister (void) |
97 | 34 | { |
98 | 34 | sigmatch_table[DETECT_PCRE].name = "pcre"; |
99 | 34 | sigmatch_table[DETECT_PCRE].desc = "match on regular expression"; |
100 | 34 | sigmatch_table[DETECT_PCRE].url = "/rules/payload-keywords.html#pcre-perl-compatible-regular-expressions"; |
101 | 34 | sigmatch_table[DETECT_PCRE].Match = NULL; |
102 | 34 | sigmatch_table[DETECT_PCRE].Setup = DetectPcreSetup; |
103 | 34 | sigmatch_table[DETECT_PCRE].Free = DetectPcreFree; |
104 | | #ifdef UNITTESTS |
105 | | sigmatch_table[DETECT_PCRE].RegisterTests = DetectPcreRegisterTests; |
106 | | #endif |
107 | 34 | sigmatch_table[DETECT_PCRE].flags = (SIGMATCH_QUOTES_OPTIONAL|SIGMATCH_HANDLE_NEGATION); |
108 | | |
109 | 34 | intmax_t val = 0; |
110 | | |
111 | 34 | if (!ConfGetInt("pcre.match-limit", &val)) { |
112 | 34 | pcre_match_limit = SC_MATCH_LIMIT_DEFAULT; |
113 | 34 | SCLogDebug("Using PCRE match-limit setting of: %i", pcre_match_limit); |
114 | 34 | } |
115 | 0 | else { |
116 | 0 | pcre_match_limit = val; |
117 | 0 | if (pcre_match_limit != SC_MATCH_LIMIT_DEFAULT) { |
118 | 0 | SCLogInfo("Using PCRE match-limit setting of: %i", pcre_match_limit); |
119 | 0 | } else { |
120 | 0 | SCLogDebug("Using PCRE match-limit setting of: %i", pcre_match_limit); |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | 34 | val = 0; |
125 | | |
126 | 34 | if (!ConfGetInt("pcre.match-limit-recursion", &val)) { |
127 | 34 | pcre_match_limit_recursion = SC_MATCH_LIMIT_RECURSION_DEFAULT; |
128 | 34 | SCLogDebug("Using PCRE match-limit-recursion setting of: %i", pcre_match_limit_recursion); |
129 | 34 | } |
130 | 0 | else { |
131 | 0 | pcre_match_limit_recursion = val; |
132 | 0 | if (pcre_match_limit_recursion != SC_MATCH_LIMIT_RECURSION_DEFAULT) { |
133 | 0 | SCLogInfo("Using PCRE match-limit-recursion setting of: %i", pcre_match_limit_recursion); |
134 | 0 | } else { |
135 | 0 | SCLogDebug("Using PCRE match-limit-recursion setting of: %i", pcre_match_limit_recursion); |
136 | 0 | } |
137 | 0 | } |
138 | | |
139 | 34 | parse_regex = DetectSetupPCRE2(PARSE_REGEX, 0); |
140 | 34 | if (parse_regex == NULL) { |
141 | 0 | FatalError("pcre2 compile failed for parse_regex"); |
142 | 0 | } |
143 | | |
144 | | /* setup the capture regex, as it needs PCRE2_UNGREEDY we do it manually */ |
145 | | /* pkt_http_ua should be pkt, http_ua, for this reason the UNGREEDY */ |
146 | 34 | parse_capture_regex = DetectSetupPCRE2(PARSE_CAPTURE_REGEX, PCRE2_UNGREEDY); |
147 | 34 | if (parse_capture_regex == NULL) { |
148 | 0 | FatalError("pcre2 compile failed for parse_capture_regex"); |
149 | 0 | } |
150 | | |
151 | 34 | #ifdef PCRE2_HAVE_JIT |
152 | 34 | if (PageSupportsRWX() == 0) { |
153 | 0 | SCLogConfig("PCRE2 won't use JIT as OS doesn't allow RWX pages"); |
154 | 0 | pcre2_use_jit = 0; |
155 | 0 | } |
156 | 34 | #endif |
157 | | |
158 | 34 | return; |
159 | 34 | } |
160 | | |
161 | | /** |
162 | | * \brief Match a regex on a single payload. |
163 | | * |
164 | | * \param det_ctx Thread detection ctx. |
165 | | * \param s Signature. |
166 | | * \param sm Sig match to match against. |
167 | | * \param p Packet to set PktVars if any. |
168 | | * \param f Flow to set FlowVars if any. |
169 | | * \param payload Payload to inspect. |
170 | | * \param payload_len Length of the payload. |
171 | | * |
172 | | * \retval 1 Match. |
173 | | * \retval 0 No match. |
174 | | */ |
175 | | int DetectPcrePayloadMatch(DetectEngineThreadCtx *det_ctx, const Signature *s, |
176 | | const SigMatchData *smd, Packet *p, Flow *f, |
177 | | const uint8_t *payload, uint32_t payload_len) |
178 | 221k | { |
179 | 221k | SCEnter(); |
180 | 221k | int ret = 0; |
181 | 221k | const uint8_t *ptr = NULL; |
182 | 221k | uint32_t len = 0; |
183 | 221k | PCRE2_SIZE capture_len = 0; |
184 | | |
185 | 221k | DetectPcreData *pe = (DetectPcreData *)smd->ctx; |
186 | | |
187 | 221k | if (pe->flags & DETECT_PCRE_RELATIVE) { |
188 | 2.77k | ptr = payload + det_ctx->buffer_offset; |
189 | 2.77k | len = payload_len - det_ctx->buffer_offset; |
190 | 218k | } else { |
191 | 218k | ptr = payload; |
192 | 218k | len = payload_len; |
193 | 218k | } |
194 | | |
195 | 221k | int start_offset = 0; |
196 | 221k | if (det_ctx->pcre_match_start_offset != 0) { |
197 | 2.31k | start_offset = (payload + det_ctx->pcre_match_start_offset - ptr); |
198 | 2.31k | } |
199 | | |
200 | | /* run the actual pcre detection */ |
201 | 221k | pcre2_match_data *match = |
202 | 221k | (pcre2_match_data *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, pe->thread_ctx_id); |
203 | | |
204 | 221k | ret = DetectPcreExec(det_ctx, pe, (char *)ptr, len, start_offset, 0, match); |
205 | 221k | SCLogDebug("ret %d (negating %s)", ret, (pe->flags & DETECT_PCRE_NEGATE) ? "set" : "not set"); |
206 | | |
207 | 221k | if (ret == PCRE2_ERROR_NOMATCH) { |
208 | 182k | if (pe->flags & DETECT_PCRE_NEGATE) { |
209 | | /* regex didn't match with negate option means we |
210 | | * consider it a match */ |
211 | 154k | ret = 1; |
212 | 154k | } else { |
213 | 28.0k | ret = 0; |
214 | 28.0k | } |
215 | 182k | } else if (ret >= 0) { |
216 | 36.7k | if (pe->flags & DETECT_PCRE_NEGATE) { |
217 | | /* regex matched but we're negated, so not |
218 | | * considering it a match */ |
219 | 20.9k | ret = 0; |
220 | 20.9k | } else { |
221 | | /* regex matched and we're not negated, |
222 | | * considering it a match */ |
223 | | |
224 | 15.7k | SCLogDebug("ret %d pe->idx %u", ret, pe->idx); |
225 | | |
226 | | /* see if we need to do substring capturing. */ |
227 | 15.7k | if (ret > 1 && pe->idx != 0) { |
228 | 2 | uint8_t x; |
229 | 4 | for (x = 0; x < pe->idx; x++) { |
230 | 2 | SCLogDebug("capturing %u", x); |
231 | 2 | const char *pcre2_str_ptr = NULL; |
232 | 2 | ret = pcre2_substring_get_bynumber( |
233 | 2 | match, x + 1, (PCRE2_UCHAR8 **)&pcre2_str_ptr, &capture_len); |
234 | 2 | if (unlikely(ret != 0)) { |
235 | 0 | pcre2_substring_free((PCRE2_UCHAR8 *)pcre2_str_ptr); |
236 | 0 | continue; |
237 | 0 | } |
238 | | /* store max 64k. Errors are ignored */ |
239 | 2 | capture_len = (capture_len < 0xffff) ? (uint16_t)capture_len : 0xffff; |
240 | 2 | uint8_t *str_ptr = SCMalloc(capture_len); |
241 | 2 | if (unlikely(str_ptr == NULL)) { |
242 | 0 | pcre2_substring_free((PCRE2_UCHAR8 *)pcre2_str_ptr); |
243 | 0 | continue; |
244 | 0 | } |
245 | 2 | memcpy(str_ptr, pcre2_str_ptr, capture_len); |
246 | 2 | pcre2_substring_free((PCRE2_UCHAR8 *)pcre2_str_ptr); |
247 | | |
248 | 2 | SCLogDebug("data %p/%u, type %u id %u p %p", |
249 | 2 | str_ptr, ret, pe->captypes[x], pe->capids[x], p); |
250 | | |
251 | 2 | if (pe->captypes[x] == VAR_TYPE_PKT_VAR_KV) { |
252 | | /* get the value, as first capture is the key */ |
253 | 0 | const char *pcre2_str_ptr2 = NULL; |
254 | | /* key length is limited to 256 chars */ |
255 | 0 | uint16_t key_len = (capture_len < 0xff) ? (uint16_t)capture_len : 0xff; |
256 | 0 | int ret2 = pcre2_substring_get_bynumber( |
257 | 0 | match, x + 2, (PCRE2_UCHAR8 **)&pcre2_str_ptr2, &capture_len); |
258 | |
|
259 | 0 | if (unlikely(ret2 != 0)) { |
260 | 0 | SCFree(str_ptr); |
261 | 0 | pcre2_substring_free((PCRE2_UCHAR8 *)pcre2_str_ptr2); |
262 | 0 | break; |
263 | 0 | } |
264 | 0 | capture_len = (capture_len < 0xffff) ? (uint16_t)capture_len : 0xffff; |
265 | 0 | uint8_t *str_ptr2 = SCMalloc(capture_len); |
266 | 0 | if (unlikely(str_ptr2 == NULL)) { |
267 | 0 | SCFree(str_ptr); |
268 | 0 | pcre2_substring_free((PCRE2_UCHAR8 *)pcre2_str_ptr2); |
269 | 0 | break; |
270 | 0 | } |
271 | 0 | memcpy(str_ptr2, pcre2_str_ptr2, capture_len); |
272 | 0 | pcre2_substring_free((PCRE2_UCHAR8 *)pcre2_str_ptr2); |
273 | |
|
274 | 0 | (void)DetectVarStoreMatchKeyValue(det_ctx, (uint8_t *)str_ptr, key_len, |
275 | 0 | (uint8_t *)str_ptr2, (uint16_t)capture_len, |
276 | 0 | DETECT_VAR_TYPE_PKT_POSTMATCH); |
277 | |
|
278 | 2 | } else if (pe->captypes[x] == VAR_TYPE_PKT_VAR) { |
279 | 0 | (void)DetectVarStoreMatch(det_ctx, pe->capids[x], (uint8_t *)str_ptr, |
280 | 0 | (uint16_t)capture_len, DETECT_VAR_TYPE_PKT_POSTMATCH); |
281 | |
|
282 | 2 | } else if (pe->captypes[x] == VAR_TYPE_FLOW_VAR && f != NULL) { |
283 | 2 | (void)DetectVarStoreMatch(det_ctx, pe->capids[x], (uint8_t *)str_ptr, |
284 | 2 | (uint16_t)capture_len, DETECT_VAR_TYPE_FLOW_POSTMATCH); |
285 | 2 | } else { |
286 | 0 | BUG_ON(1); // Impossible captype |
287 | 0 | SCFree(str_ptr); |
288 | 0 | } |
289 | 2 | } |
290 | 2 | } |
291 | | |
292 | 15.7k | PCRE2_SIZE *ov = pcre2_get_ovector_pointer(match); |
293 | | /* update offset for pcre RELATIVE */ |
294 | 15.7k | det_ctx->buffer_offset = (ptr + ov[1]) - payload; |
295 | 15.7k | det_ctx->pcre_match_start_offset = (ptr + ov[0] + 1) - payload; |
296 | | |
297 | 15.7k | ret = 1; |
298 | 15.7k | } |
299 | | |
300 | 36.7k | } else { |
301 | 2.51k | SCLogDebug("pcre had matching error"); |
302 | 2.51k | ret = 0; |
303 | 2.51k | } |
304 | 221k | SCReturnInt(ret); |
305 | 221k | } |
306 | | |
307 | | static int DetectPcreSetList(int list, int set) |
308 | 399k | { |
309 | 399k | if (list != DETECT_SM_LIST_NOTSET) { |
310 | 82.7k | SCLogError("only one pcre option to specify a buffer type is allowed"); |
311 | 82.7k | return -1; |
312 | 82.7k | } |
313 | 316k | return set; |
314 | 399k | } |
315 | | |
316 | | static int DetectPcreHasUpperCase(const char *re) |
317 | 2.00k | { |
318 | 2.00k | size_t len = strlen(re); |
319 | 2.00k | bool is_meta = false; |
320 | 2.00k | bool is_meta_hex = false; |
321 | 2.00k | int meta_hex_cnt = 0; |
322 | | |
323 | 36.0k | for (size_t i = 0; i < len; i++) { |
324 | 34.4k | if (is_meta_hex) { |
325 | 1.91k | meta_hex_cnt++; |
326 | | |
327 | 1.91k | if (meta_hex_cnt == 2) { |
328 | 948 | is_meta_hex = false; |
329 | 948 | meta_hex_cnt = 0; |
330 | 948 | } |
331 | 32.5k | } else if (is_meta) { |
332 | 6.54k | if (re[i] == 'x') { |
333 | 980 | is_meta_hex = true; |
334 | 5.56k | } else { |
335 | 5.56k | is_meta = false; |
336 | 5.56k | } |
337 | 6.54k | } |
338 | 26.0k | else if (re[i] == '\\') { |
339 | 5.61k | is_meta = true; |
340 | 5.61k | } |
341 | 20.4k | else if (isupper((unsigned char)re[i])) { |
342 | 403 | return 1; |
343 | 403 | } |
344 | 34.4k | } |
345 | | |
346 | 1.60k | return 0; |
347 | 2.00k | } |
348 | | |
349 | | static DetectPcreData *DetectPcreParse (DetectEngineCtx *de_ctx, |
350 | | const char *regexstr, int *sm_list, char *capture_names, |
351 | | size_t capture_names_size, bool negate, AppProto *alproto) |
352 | 320k | { |
353 | 320k | pcre2_match_data *match = NULL; |
354 | 320k | int en; |
355 | 320k | PCRE2_SIZE eo2; |
356 | 320k | int opts = 0; |
357 | 320k | DetectPcreData *pd = NULL; |
358 | 320k | char *op = NULL; |
359 | 320k | int ret = 0, res = 0; |
360 | 320k | int check_host_header = 0; |
361 | 320k | char op_str[64] = ""; |
362 | | |
363 | 320k | int cut_capture = 0; |
364 | 320k | char *fcap = strstr(regexstr, "flow:"); |
365 | 320k | char *pcap = strstr(regexstr, "pkt:"); |
366 | | /* take the size of the whole input as buffer size for the regex we will |
367 | | * extract below. Add 1 to please Coverity's alloc_strlen test. */ |
368 | 320k | size_t slen = strlen(regexstr) + 1; |
369 | 320k | if (fcap || pcap) { |
370 | 15.6k | SCLogDebug("regexstr %s", regexstr); |
371 | | |
372 | 15.6k | if (fcap && !pcap) |
373 | 12.3k | cut_capture = fcap - regexstr; |
374 | 3.27k | else if (pcap && !fcap) |
375 | 2.19k | cut_capture = pcap - regexstr; |
376 | 1.08k | else { |
377 | 1.08k | BUG_ON(pcap == NULL); // added to assist cppcheck |
378 | 1.08k | BUG_ON(fcap == NULL); |
379 | 1.08k | cut_capture = MIN((pcap - regexstr), (fcap - regexstr)); |
380 | 1.08k | } |
381 | | |
382 | 15.6k | SCLogDebug("cut_capture %d", cut_capture); |
383 | | |
384 | 15.6k | if (cut_capture > 1) { |
385 | 14.2k | int offset = cut_capture - 1; |
386 | 19.0k | while (offset) { |
387 | 19.0k | SCLogDebug("regexstr[offset] %c", regexstr[offset]); |
388 | 19.0k | if (regexstr[offset] == ',' || regexstr[offset] == ' ') { |
389 | 4.80k | offset--; |
390 | 4.80k | } |
391 | 14.2k | else |
392 | 14.2k | break; |
393 | 19.0k | } |
394 | | |
395 | 14.2k | if (cut_capture == (offset + 1)) { |
396 | 10.0k | SCLogDebug("missing separators, assume it's part of the regex"); |
397 | 10.0k | } else { |
398 | 4.20k | slen = offset + 1; |
399 | 4.20k | strlcpy(capture_names, regexstr+cut_capture, capture_names_size); |
400 | 4.20k | if (capture_names[strlen(capture_names)-1] == '"') |
401 | 1.12k | capture_names[strlen(capture_names)-1] = '\0'; |
402 | 4.20k | } |
403 | 14.2k | } |
404 | 15.6k | } |
405 | | |
406 | 320k | char re[slen]; |
407 | | |
408 | 320k | match = pcre2_match_data_create_from_pattern(parse_regex->regex, NULL); |
409 | 320k | if (!match) { |
410 | 0 | goto error; |
411 | 0 | } |
412 | | |
413 | 320k | ret = pcre2_match(parse_regex->regex, (PCRE2_SPTR8)regexstr, slen, 0, 0, match, NULL); |
414 | 320k | if (ret <= 0) { |
415 | 5.60k | SCLogError("pcre parse error: %s", regexstr); |
416 | 5.60k | goto error; |
417 | 5.60k | } |
418 | | |
419 | 314k | res = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)re, &slen); |
420 | 314k | if (res < 0) { |
421 | 0 | SCLogError("pcre2_substring_copy_bynumber failed"); |
422 | 0 | pcre2_match_data_free(match); |
423 | 0 | return NULL; |
424 | 0 | } |
425 | | |
426 | 314k | if (ret > 2) { |
427 | 314k | size_t copylen = sizeof(op_str); |
428 | 314k | res = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)op_str, ©len); |
429 | 314k | if (res < 0) { |
430 | 2.48k | SCLogError("pcre2_substring_copy_bynumber failed"); |
431 | 2.48k | pcre2_match_data_free(match); |
432 | 2.48k | return NULL; |
433 | 2.48k | } |
434 | 312k | op = op_str; |
435 | 312k | } |
436 | | //printf("ret %" PRId32 " re \'%s\', op \'%s\'\n", ret, re, op); |
437 | | |
438 | 312k | pd = SCCalloc(1, sizeof(DetectPcreData)); |
439 | 312k | if (unlikely(pd == NULL)) |
440 | 0 | goto error; |
441 | | |
442 | 312k | if (negate) |
443 | 11.2k | pd->flags |= DETECT_PCRE_NEGATE; |
444 | | |
445 | 312k | if (op != NULL) { |
446 | 890k | while (*op) { |
447 | 586k | SCLogDebug("regex option %c", *op); |
448 | | |
449 | 586k | switch (*op) { |
450 | 7.98k | case 'A': |
451 | 7.98k | opts |= PCRE2_ANCHORED; |
452 | 7.98k | break; |
453 | 5.63k | case 'E': |
454 | 5.63k | opts |= PCRE2_DOLLAR_ENDONLY; |
455 | 5.63k | break; |
456 | 32.6k | case 'G': |
457 | 32.6k | opts |= PCRE2_UNGREEDY; |
458 | 32.6k | break; |
459 | | |
460 | 172k | case 'i': |
461 | 172k | opts |= PCRE2_CASELESS; |
462 | 172k | pd->flags |= DETECT_PCRE_CASELESS; |
463 | 172k | break; |
464 | 96.5k | case 'm': |
465 | 96.5k | opts |= PCRE2_MULTILINE; |
466 | 96.5k | break; |
467 | 70.9k | case 's': |
468 | 70.9k | opts |= PCRE2_DOTALL; |
469 | 70.9k | break; |
470 | 3.54k | case 'x': |
471 | 3.54k | opts |= PCRE2_EXTENDED; |
472 | 3.54k | break; |
473 | | |
474 | 21.4k | case 'O': |
475 | 21.4k | pd->flags |= DETECT_PCRE_MATCH_LIMIT; |
476 | 21.4k | break; |
477 | | |
478 | 4.16k | case 'B': /* snort's option */ |
479 | 4.16k | if (*sm_list != DETECT_SM_LIST_NOTSET) { |
480 | 153 | SCLogError("regex modifier 'B' inconsistent with chosen buffer"); |
481 | 153 | goto error; |
482 | 153 | } |
483 | 4.01k | pd->flags |= DETECT_PCRE_RAWBYTES; |
484 | 4.01k | break; |
485 | 20.6k | case 'R': /* snort's option */ |
486 | 20.6k | pd->flags |= DETECT_PCRE_RELATIVE; |
487 | 20.6k | break; |
488 | | |
489 | | /* buffer selection */ |
490 | | |
491 | 25.9k | case 'U': { /* snort's option */ |
492 | 25.9k | if (pd->flags & DETECT_PCRE_RAWBYTES) { |
493 | 60 | SCLogError("regex modifier 'U' inconsistent with 'B'"); |
494 | 60 | goto error; |
495 | 60 | } |
496 | 25.9k | int list = DetectBufferTypeGetByName("http_uri"); |
497 | 25.9k | *sm_list = DetectPcreSetList(*sm_list, list); |
498 | 25.9k | *alproto = ALPROTO_HTTP1; |
499 | 25.9k | break; |
500 | 25.9k | } |
501 | 562 | case 'V': { |
502 | 562 | if (pd->flags & DETECT_PCRE_RAWBYTES) { |
503 | 8 | SCLogError("regex modifier 'V' inconsistent with 'B'"); |
504 | 8 | goto error; |
505 | 8 | } |
506 | 554 | int list = DetectBufferTypeGetByName("http_user_agent"); |
507 | 554 | *sm_list = DetectPcreSetList(*sm_list, list); |
508 | 554 | *alproto = ALPROTO_HTTP1; |
509 | 554 | break; |
510 | 562 | } |
511 | 14.1k | case 'W': { |
512 | 14.1k | if (pd->flags & DETECT_PCRE_RAWBYTES) { |
513 | 68 | SCLogError("regex modifier 'W' inconsistent with 'B'"); |
514 | 68 | goto error; |
515 | 68 | } |
516 | 14.1k | int list = DetectBufferTypeGetByName("http_host"); |
517 | 14.1k | *sm_list = DetectPcreSetList(*sm_list, list); |
518 | 14.1k | *alproto = ALPROTO_HTTP1; |
519 | 14.1k | check_host_header = 1; |
520 | 14.1k | break; |
521 | 14.1k | } |
522 | 5.93k | case 'Z': { |
523 | 5.93k | if (pd->flags & DETECT_PCRE_RAWBYTES) { |
524 | 2 | SCLogError("regex modifier 'Z' inconsistent with 'B'"); |
525 | 2 | goto error; |
526 | 2 | } |
527 | 5.93k | int list = DetectBufferTypeGetByName("http_raw_host"); |
528 | 5.93k | *sm_list = DetectPcreSetList(*sm_list, list); |
529 | 5.93k | *alproto = ALPROTO_HTTP1; |
530 | 5.93k | break; |
531 | 5.93k | } |
532 | 1.18k | case 'H': { /* snort's option */ |
533 | 1.18k | if (pd->flags & DETECT_PCRE_RAWBYTES) { |
534 | 2 | SCLogError("regex modifier 'H' inconsistent with 'B'"); |
535 | 2 | goto error; |
536 | 2 | } |
537 | 1.18k | int list = DetectBufferTypeGetByName("http_header"); |
538 | 1.18k | *sm_list = DetectPcreSetList(*sm_list, list); |
539 | 1.18k | *alproto = ALPROTO_HTTP1; |
540 | 1.18k | break; |
541 | 3.87k | } case 'I': { /* snort's option */ |
542 | 3.87k | if (pd->flags & DETECT_PCRE_RAWBYTES) { |
543 | 13 | SCLogError("regex modifier 'I' inconsistent with 'B'"); |
544 | 13 | goto error; |
545 | 13 | } |
546 | 3.86k | int list = DetectBufferTypeGetByName("http_raw_uri"); |
547 | 3.86k | *sm_list = DetectPcreSetList(*sm_list, list); |
548 | 3.86k | *alproto = ALPROTO_HTTP1; |
549 | 3.86k | break; |
550 | 3.87k | } |
551 | 3.31k | case 'D': { /* snort's option */ |
552 | 3.31k | int list = DetectBufferTypeGetByName("http_raw_header"); |
553 | 3.31k | *sm_list = DetectPcreSetList(*sm_list, list); |
554 | 3.31k | *alproto = ALPROTO_HTTP1; |
555 | 3.31k | break; |
556 | 3.87k | } |
557 | 39.2k | case 'M': { /* snort's option */ |
558 | 39.2k | if (pd->flags & DETECT_PCRE_RAWBYTES) { |
559 | 105 | SCLogError("regex modifier 'M' inconsistent with 'B'"); |
560 | 105 | goto error; |
561 | 105 | } |
562 | 39.1k | int list = DetectBufferTypeGetByName("http_method"); |
563 | 39.1k | *sm_list = DetectPcreSetList(*sm_list, list); |
564 | 39.1k | *alproto = ALPROTO_HTTP1; |
565 | 39.1k | break; |
566 | 39.2k | } |
567 | 2.08k | case 'C': { /* snort's option */ |
568 | 2.08k | if (pd->flags & DETECT_PCRE_RAWBYTES) { |
569 | 13 | SCLogError("regex modifier 'C' inconsistent with 'B'"); |
570 | 13 | goto error; |
571 | 13 | } |
572 | 2.07k | int list = DetectBufferTypeGetByName("http_cookie"); |
573 | 2.07k | *sm_list = DetectPcreSetList(*sm_list, list); |
574 | 2.07k | *alproto = ALPROTO_HTTP1; |
575 | 2.07k | break; |
576 | 2.08k | } |
577 | 12.3k | case 'P': { |
578 | | /* snort's option (http request body inspection) */ |
579 | 12.3k | int list = DetectBufferTypeGetByName("http_client_body"); |
580 | 12.3k | *sm_list = DetectPcreSetList(*sm_list, list); |
581 | 12.3k | *alproto = ALPROTO_HTTP1; |
582 | 12.3k | break; |
583 | 2.08k | } |
584 | 1.41k | case 'Q': { |
585 | 1.41k | int list = DetectBufferTypeGetByName("file_data"); |
586 | | /* suricata extension (http response body inspection) */ |
587 | 1.41k | *sm_list = DetectPcreSetList(*sm_list, list); |
588 | 1.41k | *alproto = ALPROTO_HTTP1; |
589 | 1.41k | break; |
590 | 2.08k | } |
591 | 1.03k | case 'Y': { |
592 | | /* snort's option */ |
593 | 1.03k | int list = DetectBufferTypeGetByName("http_stat_msg"); |
594 | 1.03k | *sm_list = DetectPcreSetList(*sm_list, list); |
595 | 1.03k | *alproto = ALPROTO_HTTP1; |
596 | 1.03k | break; |
597 | 2.08k | } |
598 | 31.6k | case 'S': { |
599 | | /* snort's option */ |
600 | 31.6k | int list = DetectBufferTypeGetByName("http_stat_code"); |
601 | 31.6k | *sm_list = DetectPcreSetList(*sm_list, list); |
602 | 31.6k | *alproto = ALPROTO_HTTP1; |
603 | 31.6k | break; |
604 | 2.08k | } |
605 | 8.09k | default: |
606 | 8.09k | SCLogError("unknown regex modifier '%c'", *op); |
607 | 8.09k | goto error; |
608 | 586k | } |
609 | 578k | op++; |
610 | 578k | } |
611 | 312k | } |
612 | 303k | if (*sm_list == -1) |
613 | 1.02k | goto error; |
614 | | |
615 | 302k | SCLogDebug("DetectPcreParse: \"%s\"", re); |
616 | | |
617 | | /* host header */ |
618 | 302k | if (check_host_header) { |
619 | 11.6k | if (pd->flags & DETECT_PCRE_CASELESS) { |
620 | 11.1k | SCLogWarning("http host pcre(\"W\") " |
621 | 11.1k | "specified along with \"i(caseless)\" modifier. " |
622 | 11.1k | "Since the hostname buffer we match against " |
623 | 11.1k | "is actually lowercase, having a " |
624 | 11.1k | "nocase is redundant."); |
625 | 11.1k | } |
626 | 561 | else if (DetectPcreHasUpperCase(re)) { |
627 | 291 | SCLogError("pcre host(\"W\") " |
628 | 291 | "specified has an uppercase char. " |
629 | 291 | "Since the hostname buffer we match against " |
630 | 291 | "is actually lowercase, please specify an " |
631 | 291 | "all lowercase based pcre."); |
632 | 291 | goto error; |
633 | 291 | } |
634 | 11.6k | } |
635 | | |
636 | | /* Try to compile as if all (...) groups had been meant as (?:...), |
637 | | * which is the common case in most rules. |
638 | | * If we fail because a capture group is later referenced (e.g., \1), |
639 | | * PCRE will let us know. |
640 | | */ |
641 | 302k | if (capture_names == NULL || strlen(capture_names) == 0) |
642 | 298k | opts |= PCRE2_NO_AUTO_CAPTURE; |
643 | | |
644 | 302k | pd->parse_regex.regex = |
645 | 302k | pcre2_compile((PCRE2_SPTR8)re, PCRE2_ZERO_TERMINATED, opts, &en, &eo2, NULL); |
646 | 302k | if (pd->parse_regex.regex == NULL && en == 115) { // reference to nonexistent subpattern |
647 | 59.2k | opts &= ~PCRE2_NO_AUTO_CAPTURE; |
648 | 59.2k | pd->parse_regex.regex = |
649 | 59.2k | pcre2_compile((PCRE2_SPTR8)re, PCRE2_ZERO_TERMINATED, opts, &en, &eo2, NULL); |
650 | 59.2k | } |
651 | 302k | if (pd->parse_regex.regex == NULL) { |
652 | 35.8k | PCRE2_UCHAR errbuffer[256]; |
653 | 35.8k | pcre2_get_error_message(en, errbuffer, sizeof(errbuffer)); |
654 | 35.8k | SCLogError("pcre2 compile of \"%s\" failed at " |
655 | 35.8k | "offset %d: %s", |
656 | 35.8k | regexstr, (int)eo2, errbuffer); |
657 | 35.8k | goto error; |
658 | 35.8k | } |
659 | | |
660 | 266k | #ifdef PCRE2_HAVE_JIT |
661 | 266k | if (pcre2_use_jit) { |
662 | 266k | ret = pcre2_jit_compile(pd->parse_regex.regex, PCRE2_JIT_COMPLETE); |
663 | 266k | if (ret != 0) { |
664 | | /* warning, so we won't print the sig after this. Adding |
665 | | * file and line to the message so the admin can figure |
666 | | * out what sig this is about */ |
667 | 266k | SCLogDebug("PCRE2 JIT compiler does not support: %s. " |
668 | 266k | "Falling back to regular PCRE2 handling (%s:%d)", |
669 | 266k | regexstr, de_ctx->rule_file, de_ctx->rule_line); |
670 | 266k | } |
671 | 266k | } |
672 | 266k | #endif /*PCRE2_HAVE_JIT*/ |
673 | | |
674 | 266k | pd->parse_regex.context = pcre2_match_context_create(NULL); |
675 | 266k | if (pd->parse_regex.context == NULL) { |
676 | 0 | SCLogError("pcre2 could not create match context"); |
677 | 0 | goto error; |
678 | 0 | } |
679 | 266k | pd->parse_regex.match = pcre2_match_data_create_from_pattern(pd->parse_regex.regex, NULL); |
680 | | |
681 | 266k | if (pd->flags & DETECT_PCRE_MATCH_LIMIT) { |
682 | 15.5k | if (pcre_match_limit >= -1) { |
683 | 15.5k | pcre2_set_match_limit(pd->parse_regex.context, pcre_match_limit); |
684 | 15.5k | } |
685 | 15.5k | if (pcre_match_limit_recursion >= -1) { |
686 | | // pcre2_set_depth_limit unsupported on ubuntu 16.04 |
687 | 15.5k | pcre2_set_recursion_limit(pd->parse_regex.context, pcre_match_limit_recursion); |
688 | 15.5k | } |
689 | 250k | } else { |
690 | 250k | pcre2_set_match_limit(pd->parse_regex.context, SC_MATCH_LIMIT_DEFAULT); |
691 | 250k | pcre2_set_recursion_limit(pd->parse_regex.context, SC_MATCH_LIMIT_RECURSION_DEFAULT); |
692 | 250k | } |
693 | | |
694 | 266k | pcre2_match_data_free(match); |
695 | 266k | return pd; |
696 | | |
697 | 51.2k | error: |
698 | 51.2k | pcre2_match_data_free(match); |
699 | 51.2k | DetectPcreFree(de_ctx, pd); |
700 | 51.2k | return NULL; |
701 | 266k | } |
702 | | |
703 | | /** \internal |
704 | | * \brief check if we need to extract capture settings and set them up if needed |
705 | | */ |
706 | | static int DetectPcreParseCapture(const char *regexstr, DetectEngineCtx *de_ctx, DetectPcreData *pd, |
707 | | char *capture_names) |
708 | 266k | { |
709 | 266k | int ret = 0, res = 0; |
710 | 266k | char type_str[16] = ""; |
711 | 266k | const char *orig_right_edge = regexstr + strlen(regexstr); |
712 | 266k | char *name_array[DETECT_PCRE_CAPTURE_MAX] = { NULL }; |
713 | 266k | int name_idx = 0; |
714 | 266k | int capture_cnt = 0; |
715 | 266k | int key = 0; |
716 | 266k | size_t copylen; |
717 | 266k | pcre2_match_data *match = NULL; |
718 | | |
719 | 266k | SCLogDebug("regexstr %s, pd %p", regexstr, pd); |
720 | | |
721 | 266k | ret = pcre2_pattern_info(pd->parse_regex.regex, PCRE2_INFO_CAPTURECOUNT, &capture_cnt); |
722 | 266k | SCLogDebug("ret %d capture_cnt %d", ret, capture_cnt); |
723 | 266k | if (ret == 0 && capture_cnt && strlen(capture_names) > 0) |
724 | 2.52k | { |
725 | 2.52k | char *ptr = NULL; |
726 | 7.09k | while ((name_array[name_idx] = strtok_r(name_idx == 0 ? capture_names : NULL, " ,", &ptr))){ |
727 | 5.46k | if (name_idx > (capture_cnt - 1)) { |
728 | 314 | SCLogError("more pkt/flow " |
729 | 314 | "var capture names than capturing substrings"); |
730 | 314 | return -1; |
731 | 314 | } |
732 | 5.14k | SCLogDebug("name '%s'", name_array[name_idx]); |
733 | | |
734 | 5.14k | if (strcmp(name_array[name_idx], "pkt:key") == 0) { |
735 | 300 | key = 1; |
736 | 300 | SCLogDebug("key-value/key"); |
737 | | |
738 | 300 | pd->captypes[pd->idx] = VAR_TYPE_PKT_VAR_KV; |
739 | 300 | SCLogDebug("id %u type %u", pd->capids[pd->idx], pd->captypes[pd->idx]); |
740 | 300 | pd->idx++; |
741 | | |
742 | 4.84k | } else if (key == 1 && strcmp(name_array[name_idx], "pkt:value") == 0) { |
743 | 0 | SCLogDebug("key-value/value"); |
744 | 0 | key = 0; |
745 | | |
746 | | /* kv error conditions */ |
747 | 4.84k | } else if (key == 0 && strcmp(name_array[name_idx], "pkt:value") == 0) { |
748 | 2 | return -1; |
749 | 4.84k | } else if (key == 1) { |
750 | 36 | return -1; |
751 | | |
752 | 4.81k | } else if (strncmp(name_array[name_idx], "flow:", 5) == 0) { |
753 | 2.80k | pd->capids[pd->idx] = |
754 | 2.80k | VarNameStoreRegister(name_array[name_idx] + 5, VAR_TYPE_FLOW_VAR); |
755 | 2.80k | pd->captypes[pd->idx] = VAR_TYPE_FLOW_VAR; |
756 | 2.80k | pd->idx++; |
757 | | |
758 | 2.80k | } else if (strncmp(name_array[name_idx], "pkt:", 4) == 0) { |
759 | 1.63k | pd->capids[pd->idx] = |
760 | 1.63k | VarNameStoreRegister(name_array[name_idx] + 4, VAR_TYPE_PKT_VAR); |
761 | 1.63k | pd->captypes[pd->idx] = VAR_TYPE_PKT_VAR; |
762 | 1.63k | SCLogDebug("id %u type %u", pd->capids[pd->idx], pd->captypes[pd->idx]); |
763 | 1.63k | pd->idx++; |
764 | | |
765 | 1.63k | } else { |
766 | 370 | SCLogError(" pkt/flow " |
767 | 370 | "var capture names must start with 'pkt:' or 'flow:'"); |
768 | 370 | return -1; |
769 | 370 | } |
770 | | |
771 | 4.74k | name_idx++; |
772 | 4.74k | if (name_idx >= DETECT_PCRE_CAPTURE_MAX) |
773 | 172 | break; |
774 | 4.74k | } |
775 | 2.52k | } |
776 | | |
777 | | /* take the size of the whole input as buffer size for the string we will |
778 | | * extract below. Add 1 to please Coverity's alloc_strlen test. */ |
779 | 265k | size_t cap_buffer_len = strlen(regexstr) + 1; |
780 | 265k | char capture_str[cap_buffer_len]; |
781 | 265k | memset(capture_str, 0x00, cap_buffer_len); |
782 | | |
783 | 265k | if (de_ctx == NULL) |
784 | 0 | goto error; |
785 | | |
786 | 267k | while (1) { |
787 | 267k | SCLogDebug("\'%s\'", regexstr); |
788 | | |
789 | 267k | ret = DetectParsePcreExec(parse_capture_regex, &match, regexstr, 0, 0); |
790 | 267k | if (ret < 3) { |
791 | 265k | pcre2_match_data_free(match); |
792 | 265k | return 0; |
793 | 265k | } |
794 | 1.67k | copylen = sizeof(type_str); |
795 | 1.67k | res = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)type_str, ©len); |
796 | 1.67k | if (res != 0) { |
797 | 35 | SCLogError("pcre2_substring_copy_bynumber failed"); |
798 | 35 | goto error; |
799 | 35 | } |
800 | 1.64k | cap_buffer_len = strlen(regexstr) + 1; |
801 | 1.64k | res = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)capture_str, &cap_buffer_len); |
802 | 1.64k | if (res != 0) { |
803 | 0 | SCLogError("pcre2_substring_copy_bynumber failed"); |
804 | 0 | goto error; |
805 | 0 | } |
806 | 1.64k | if (strlen(capture_str) == 0 || strlen(type_str) == 0) { |
807 | 0 | goto error; |
808 | 0 | } |
809 | | |
810 | 1.64k | SCLogDebug("type \'%s\'", type_str); |
811 | 1.64k | SCLogDebug("capture \'%s\'", capture_str); |
812 | | |
813 | 1.64k | if (pd->idx >= DETECT_PCRE_CAPTURE_MAX) { |
814 | 1 | SCLogError("rule can have maximally %d pkt/flow " |
815 | 1 | "var captures", |
816 | 1 | DETECT_PCRE_CAPTURE_MAX); |
817 | 1 | pcre2_match_data_free(match); |
818 | 1 | return -1; |
819 | 1 | } |
820 | | |
821 | 1.64k | if (strcmp(type_str, "pkt") == 0) { |
822 | 243 | pd->capids[pd->idx] = VarNameStoreRegister((char *)capture_str, VAR_TYPE_PKT_VAR); |
823 | 243 | pd->captypes[pd->idx] = VAR_TYPE_PKT_VAR; |
824 | 243 | SCLogDebug("id %u type %u", pd->capids[pd->idx], pd->captypes[pd->idx]); |
825 | 243 | pd->idx++; |
826 | 1.40k | } else if (strcmp(type_str, "flow") == 0) { |
827 | 260 | pd->capids[pd->idx] = VarNameStoreRegister((char *)capture_str, VAR_TYPE_FLOW_VAR); |
828 | 260 | pd->captypes[pd->idx] = VAR_TYPE_FLOW_VAR; |
829 | 260 | pd->idx++; |
830 | 260 | } |
831 | | |
832 | | //SCLogNotice("pd->capname %s", pd->capname); |
833 | 1.64k | PCRE2_SIZE *ov = pcre2_get_ovector_pointer(match); |
834 | 1.64k | regexstr += ov[1]; |
835 | | |
836 | 1.64k | pcre2_match_data_free(match); |
837 | 1.64k | match = NULL; |
838 | | |
839 | 1.64k | if (regexstr >= orig_right_edge) |
840 | 67 | break; |
841 | 1.64k | } |
842 | 67 | return 0; |
843 | | |
844 | 35 | error: |
845 | 35 | pcre2_match_data_free(match); |
846 | 35 | return -1; |
847 | 265k | } |
848 | | |
849 | | static void *DetectPcreThreadInit(void *data) |
850 | 73.3k | { |
851 | 73.3k | DetectPcreData *pd = (DetectPcreData *)data; |
852 | 73.3k | pcre2_match_data *match = pcre2_match_data_create_from_pattern(pd->parse_regex.regex, NULL); |
853 | 73.3k | return match; |
854 | 73.3k | } |
855 | | |
856 | | static void DetectPcreThreadFree(void *ctx) |
857 | 73.3k | { |
858 | 73.3k | if (ctx != NULL) { |
859 | 73.3k | pcre2_match_data *match = (pcre2_match_data *)ctx; |
860 | 73.3k | pcre2_match_data_free(match); |
861 | 73.3k | } |
862 | 73.3k | } |
863 | | |
864 | | static int DetectPcreSetup (DetectEngineCtx *de_ctx, Signature *s, const char *regexstr) |
865 | 758k | { |
866 | 758k | SCEnter(); |
867 | 758k | DetectPcreData *pd = NULL; |
868 | 758k | SigMatch *sm = NULL; |
869 | 758k | int parsed_sm_list = DETECT_SM_LIST_NOTSET; |
870 | 758k | char capture_names[1024] = ""; |
871 | 758k | AppProto alproto = ALPROTO_UNKNOWN; |
872 | | |
873 | 758k | pd = DetectPcreParse(de_ctx, regexstr, &parsed_sm_list, |
874 | 758k | capture_names, sizeof(capture_names), s->init_data->negated, |
875 | 758k | &alproto); |
876 | 758k | if (pd == NULL) |
877 | 122k | goto error; |
878 | 635k | if (DetectPcreParseCapture(regexstr, de_ctx, pd, capture_names) < 0) |
879 | 2.06k | goto error; |
880 | | |
881 | 633k | pd->thread_ctx_id = DetectRegisterThreadCtxFuncs( |
882 | 633k | de_ctx, "pcre", DetectPcreThreadInit, (void *)pd, DetectPcreThreadFree, 0); |
883 | 633k | if (pd->thread_ctx_id == -1) |
884 | 0 | goto error; |
885 | | |
886 | 633k | int sm_list = -1; |
887 | 633k | if (s->init_data->list != DETECT_SM_LIST_NOTSET) { |
888 | 62.7k | if (parsed_sm_list != DETECT_SM_LIST_NOTSET && parsed_sm_list != s->init_data->list) { |
889 | 922 | SCLogError("Expression seen with a sticky buffer still set; either (1) reset sticky " |
890 | 922 | "buffer with pkt_data or (2) use a sticky buffer providing \"%s\".", |
891 | 922 | DetectEngineBufferTypeGetDescriptionById(de_ctx, parsed_sm_list)); |
892 | 922 | goto error; |
893 | 922 | } |
894 | 61.8k | if (DetectBufferGetActiveList(de_ctx, s) == -1) |
895 | 6 | goto error; |
896 | | |
897 | 61.8k | sm_list = s->init_data->list; |
898 | 570k | } else { |
899 | 570k | switch (parsed_sm_list) { |
900 | 313k | case DETECT_SM_LIST_NOTSET: |
901 | 313k | sm_list = DETECT_SM_LIST_PMATCH; |
902 | 313k | break; |
903 | 256k | default: { |
904 | 256k | if (alproto != ALPROTO_UNKNOWN) { |
905 | | /* see if the proto doesn't conflict |
906 | | * with what we already have. */ |
907 | 256k | if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, alproto)) { |
908 | 555 | goto error; |
909 | 555 | } |
910 | 256k | if (DetectSignatureSetAppProto(s, alproto) < 0) |
911 | 510 | goto error; |
912 | 256k | } |
913 | 255k | sm_list = parsed_sm_list; |
914 | 255k | break; |
915 | 256k | } |
916 | 570k | } |
917 | 570k | } |
918 | 631k | if (sm_list == -1) |
919 | 0 | goto error; |
920 | | |
921 | 631k | sm = SigMatchAlloc(); |
922 | 631k | if (sm == NULL) |
923 | 1 | goto error; |
924 | 631k | sm->type = DETECT_PCRE; |
925 | 631k | sm->ctx = (void *)pd; |
926 | 631k | SigMatchAppendSMToList(s, sm, sm_list); |
927 | | |
928 | 643k | for (uint8_t x = 0; x < pd->idx; x++) { |
929 | 12.2k | if (DetectFlowvarPostMatchSetup(de_ctx, s, pd->capids[x]) < 0) |
930 | 0 | goto error_nofree; |
931 | 12.2k | } |
932 | | |
933 | 631k | if (!(pd->flags & DETECT_PCRE_RELATIVE)) |
934 | 605k | goto okay; |
935 | | |
936 | | /* errors below shouldn't free pd */ |
937 | | |
938 | 26.0k | SigMatch *prev_pm = DetectGetLastSMByListPtr(s, sm->prev, |
939 | 26.0k | DETECT_CONTENT, DETECT_PCRE, -1); |
940 | 26.0k | if (s->init_data->list == DETECT_SM_LIST_NOTSET && prev_pm == NULL) { |
941 | 949 | SCLogError("pcre with /R (relative) needs " |
942 | 949 | "preceding match in the same buffer"); |
943 | 949 | goto error_nofree; |
944 | | /* null is allowed when we use a sticky buffer */ |
945 | 25.1k | } else if (prev_pm == NULL) { |
946 | 1.51k | goto okay; |
947 | 1.51k | } |
948 | 23.6k | if (prev_pm->type == DETECT_CONTENT) { |
949 | 13.7k | DetectContentData *cd = (DetectContentData *)prev_pm->ctx; |
950 | 13.7k | cd->flags |= DETECT_CONTENT_RELATIVE_NEXT; |
951 | 13.7k | } else if (prev_pm->type == DETECT_PCRE) { |
952 | 9.89k | DetectPcreData *tmp = (DetectPcreData *)prev_pm->ctx; |
953 | 9.89k | tmp->flags |= DETECT_PCRE_RELATIVE_NEXT; |
954 | 9.89k | } |
955 | | |
956 | 630k | okay: |
957 | 630k | SCReturnInt(0); |
958 | 127k | error: |
959 | 127k | DetectPcreFree(de_ctx, pd); |
960 | 127k | error_nofree: |
961 | 127k | SCReturnInt(-1); |
962 | 127k | } |
963 | | |
964 | | static void DetectPcreFree(DetectEngineCtx *de_ctx, void *ptr) |
965 | 371k | { |
966 | 371k | if (ptr == NULL) |
967 | 59.3k | return; |
968 | | |
969 | 312k | DetectPcreData *pd = (DetectPcreData *)ptr; |
970 | 312k | DetectParseFreeRegex(&pd->parse_regex); |
971 | 312k | DetectUnregisterThreadCtxFuncs(de_ctx, pd, "pcre"); |
972 | | |
973 | 317k | for (uint8_t i = 0; i < pd->idx; i++) { |
974 | 5.24k | VarNameStoreUnregister(pd->capids[i], pd->captypes[i]); |
975 | 5.24k | } |
976 | 312k | SCFree(pd); |
977 | | |
978 | 312k | return; |
979 | 371k | } |
980 | | |
981 | | #ifdef UNITTESTS /* UNITTESTS */ |
982 | | #include "detect-engine-alert.h" |
983 | | static int g_file_data_buffer_id = 0; |
984 | | static int g_http_header_buffer_id = 0; |
985 | | static int g_dce_stub_data_buffer_id = 0; |
986 | | |
987 | | /** |
988 | | * \test DetectPcreParseTest01 make sure we don't allow invalid opts 7. |
989 | | */ |
990 | | static int DetectPcreParseTest01 (void) |
991 | | { |
992 | | int result = 1; |
993 | | DetectPcreData *pd = NULL; |
994 | | const char *teststring = "/blah/7"; |
995 | | int list = DETECT_SM_LIST_NOTSET; |
996 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
997 | | FAIL_IF_NULL(de_ctx); |
998 | | AppProto alproto = ALPROTO_UNKNOWN; |
999 | | |
1000 | | pd = DetectPcreParse(de_ctx, teststring, &list, NULL, 0, false, &alproto); |
1001 | | FAIL_IF_NOT_NULL(pd); |
1002 | | |
1003 | | DetectEngineCtxFree(de_ctx); |
1004 | | return result; |
1005 | | } |
1006 | | |
1007 | | /** |
1008 | | * \test DetectPcreParseTest02 make sure we don't allow invalid opts Ui$. |
1009 | | */ |
1010 | | static int DetectPcreParseTest02 (void) |
1011 | | { |
1012 | | int result = 1; |
1013 | | DetectPcreData *pd = NULL; |
1014 | | const char *teststring = "/blah/Ui$"; |
1015 | | int list = DETECT_SM_LIST_NOTSET; |
1016 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1017 | | FAIL_IF_NULL(de_ctx); |
1018 | | AppProto alproto = ALPROTO_UNKNOWN; |
1019 | | |
1020 | | pd = DetectPcreParse(de_ctx, teststring, &list, NULL, 0, false, &alproto); |
1021 | | FAIL_IF_NOT_NULL(pd); |
1022 | | FAIL_IF_NOT(alproto == ALPROTO_HTTP1); |
1023 | | |
1024 | | DetectEngineCtxFree(de_ctx); |
1025 | | return result; |
1026 | | } |
1027 | | |
1028 | | /** |
1029 | | * \test DetectPcreParseTest03 make sure we don't allow invalid opts UZi. |
1030 | | */ |
1031 | | static int DetectPcreParseTest03 (void) |
1032 | | { |
1033 | | int result = 1; |
1034 | | DetectPcreData *pd = NULL; |
1035 | | const char *teststring = "/blah/UNi"; |
1036 | | int list = DETECT_SM_LIST_NOTSET; |
1037 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1038 | | FAIL_IF_NULL(de_ctx); |
1039 | | AppProto alproto = ALPROTO_UNKNOWN; |
1040 | | |
1041 | | pd = DetectPcreParse(de_ctx, teststring, &list, NULL, 0, false, &alproto); |
1042 | | FAIL_IF_NOT_NULL(pd); |
1043 | | |
1044 | | DetectEngineCtxFree(de_ctx); |
1045 | | return result; |
1046 | | } |
1047 | | |
1048 | | /** |
1049 | | * \test DetectPcreParseTest04 make sure we allow escaped " |
1050 | | */ |
1051 | | static int DetectPcreParseTest04 (void) |
1052 | | { |
1053 | | int result = 1; |
1054 | | DetectPcreData *pd = NULL; |
1055 | | const char *teststring = "/b\\\"lah/i"; |
1056 | | int list = DETECT_SM_LIST_NOTSET; |
1057 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1058 | | FAIL_IF_NULL(de_ctx); |
1059 | | AppProto alproto = ALPROTO_UNKNOWN; |
1060 | | |
1061 | | pd = DetectPcreParse(de_ctx, teststring, &list, NULL, 0, false, &alproto); |
1062 | | FAIL_IF_NULL(pd); |
1063 | | FAIL_IF_NOT(alproto == ALPROTO_UNKNOWN); |
1064 | | |
1065 | | DetectPcreFree(de_ctx, pd); |
1066 | | DetectEngineCtxFree(de_ctx); |
1067 | | return result; |
1068 | | } |
1069 | | |
1070 | | /** |
1071 | | * \test DetectPcreParseTest05 make sure we parse pcre with no opts |
1072 | | */ |
1073 | | static int DetectPcreParseTest05 (void) |
1074 | | { |
1075 | | int result = 1; |
1076 | | DetectPcreData *pd = NULL; |
1077 | | const char *teststring = "/b(l|a)h/"; |
1078 | | int list = DETECT_SM_LIST_NOTSET; |
1079 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1080 | | FAIL_IF_NULL(de_ctx); |
1081 | | AppProto alproto = ALPROTO_UNKNOWN; |
1082 | | |
1083 | | pd = DetectPcreParse(de_ctx, teststring, &list, NULL, 0, false, &alproto); |
1084 | | FAIL_IF_NULL(pd); |
1085 | | FAIL_IF_NOT(alproto == ALPROTO_UNKNOWN); |
1086 | | |
1087 | | DetectPcreFree(de_ctx, pd); |
1088 | | DetectEngineCtxFree(de_ctx); |
1089 | | return result; |
1090 | | } |
1091 | | |
1092 | | /** |
1093 | | * \test DetectPcreParseTest06 make sure we parse pcre with smi opts |
1094 | | */ |
1095 | | static int DetectPcreParseTest06 (void) |
1096 | | { |
1097 | | int result = 1; |
1098 | | DetectPcreData *pd = NULL; |
1099 | | const char *teststring = "/b(l|a)h/smi"; |
1100 | | int list = DETECT_SM_LIST_NOTSET; |
1101 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1102 | | FAIL_IF_NULL(de_ctx); |
1103 | | AppProto alproto = ALPROTO_UNKNOWN; |
1104 | | |
1105 | | pd = DetectPcreParse(de_ctx, teststring, &list, NULL, 0, false, &alproto); |
1106 | | FAIL_IF_NULL(pd); |
1107 | | FAIL_IF_NOT(alproto == ALPROTO_UNKNOWN); |
1108 | | |
1109 | | DetectPcreFree(de_ctx, pd); |
1110 | | DetectEngineCtxFree(de_ctx); |
1111 | | return result; |
1112 | | } |
1113 | | |
1114 | | /** |
1115 | | * \test DetectPcreParseTest07 make sure we parse pcre with /Ui opts |
1116 | | */ |
1117 | | static int DetectPcreParseTest07 (void) |
1118 | | { |
1119 | | int result = 1; |
1120 | | DetectPcreData *pd = NULL; |
1121 | | const char *teststring = "/blah/Ui"; |
1122 | | int list = DETECT_SM_LIST_NOTSET; |
1123 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1124 | | FAIL_IF_NULL(de_ctx); |
1125 | | AppProto alproto = ALPROTO_UNKNOWN; |
1126 | | |
1127 | | pd = DetectPcreParse(de_ctx, teststring, &list, NULL, 0, false, &alproto); |
1128 | | FAIL_IF_NULL(pd); |
1129 | | FAIL_IF_NOT(alproto == ALPROTO_HTTP1); |
1130 | | |
1131 | | DetectPcreFree(de_ctx, pd); |
1132 | | DetectEngineCtxFree(de_ctx); |
1133 | | return result; |
1134 | | } |
1135 | | |
1136 | | /** |
1137 | | * \test DetectPcreParseTest08 make sure we parse pcre with O opts |
1138 | | */ |
1139 | | static int DetectPcreParseTest08 (void) |
1140 | | { |
1141 | | int result = 1; |
1142 | | DetectPcreData *pd = NULL; |
1143 | | const char *teststring = "/b(l|a)h/O"; |
1144 | | int list = DETECT_SM_LIST_NOTSET; |
1145 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1146 | | FAIL_IF_NULL(de_ctx); |
1147 | | AppProto alproto = ALPROTO_UNKNOWN; |
1148 | | |
1149 | | pd = DetectPcreParse(de_ctx, teststring, &list, NULL, 0, false, &alproto); |
1150 | | FAIL_IF_NULL(pd); |
1151 | | FAIL_IF_NOT(alproto == ALPROTO_UNKNOWN); |
1152 | | |
1153 | | DetectPcreFree(de_ctx, pd); |
1154 | | DetectEngineCtxFree(de_ctx); |
1155 | | return result; |
1156 | | } |
1157 | | |
1158 | | /** |
1159 | | * \test DetectPcreParseTest09 make sure we parse pcre with a content |
1160 | | * that has slashes |
1161 | | */ |
1162 | | static int DetectPcreParseTest09 (void) |
1163 | | { |
1164 | | DetectPcreData *pd = NULL; |
1165 | | const char *teststring = "/lala\\\\/"; |
1166 | | int list = DETECT_SM_LIST_NOTSET; |
1167 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1168 | | FAIL_IF_NULL(de_ctx); |
1169 | | AppProto alproto = ALPROTO_UNKNOWN; |
1170 | | |
1171 | | pd = DetectPcreParse(de_ctx, teststring, &list, NULL, 0, false, &alproto); |
1172 | | FAIL_IF_NULL(pd); |
1173 | | |
1174 | | DetectPcreFree(de_ctx, pd); |
1175 | | DetectEngineCtxFree(de_ctx); |
1176 | | PASS; |
1177 | | } |
1178 | | |
1179 | | /** |
1180 | | * \test Test pcre option for dce sig(yeah I'm bored of writing test titles). |
1181 | | */ |
1182 | | static int DetectPcreParseTest10(void) |
1183 | | { |
1184 | | Signature *s = SigAlloc(); |
1185 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1186 | | FAIL_IF_NULL(de_ctx); |
1187 | | |
1188 | | FAIL_IF(DetectSignatureSetAppProto(s, ALPROTO_DCERPC) < 0); |
1189 | | |
1190 | | FAIL_IF_NOT(DetectPcreSetup(de_ctx, s, "/bamboo/") == 0); |
1191 | | FAIL_IF_NOT(DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id) == NULL); |
1192 | | FAIL_IF_NOT(s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL); |
1193 | | |
1194 | | SigFree(de_ctx, s); |
1195 | | |
1196 | | s = SigAlloc(); |
1197 | | FAIL_IF_NULL(s); |
1198 | | |
1199 | | /* failure since we have no preceding content/pcre/bytejump */ |
1200 | | FAIL_IF_NOT(DetectPcreSetup(de_ctx, s, "/bamboo/") == 0); |
1201 | | FAIL_IF_NOT(DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id) == NULL); |
1202 | | FAIL_IF_NOT(s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL); |
1203 | | |
1204 | | SigFree(de_ctx, s); |
1205 | | DetectEngineCtxFree(de_ctx); |
1206 | | |
1207 | | PASS; |
1208 | | } |
1209 | | |
1210 | | /** \test Check a signature with pcre relative method */ |
1211 | | static int DetectPcreParseTest15(void) |
1212 | | { |
1213 | | DetectEngineCtx *de_ctx = NULL; |
1214 | | |
1215 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1216 | | |
1217 | | de_ctx->flags |= DE_QUIET; |
1218 | | de_ctx->sig_list = SigInit(de_ctx, |
1219 | | "alert tcp any any -> any any " |
1220 | | "(msg:\"Testing pcre relative http_method\"; " |
1221 | | "content:\"GET\"; " |
1222 | | "http_method; pcre:\"/abc/RM\"; sid:1;)"); |
1223 | | FAIL_IF_NULL(de_ctx->sig_list); |
1224 | | |
1225 | | if (de_ctx != NULL) |
1226 | | SigCleanSignatures(de_ctx); |
1227 | | if (de_ctx != NULL) |
1228 | | DetectEngineCtxFree(de_ctx); |
1229 | | PASS; |
1230 | | } |
1231 | | |
1232 | | |
1233 | | /** \test Check a signature with pcre relative cookie */ |
1234 | | static int DetectPcreParseTest16(void) |
1235 | | { |
1236 | | DetectEngineCtx *de_ctx = NULL; |
1237 | | |
1238 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1239 | | |
1240 | | de_ctx->flags |= DE_QUIET; |
1241 | | de_ctx->sig_list = SigInit(de_ctx, |
1242 | | "alert tcp any any -> any any " |
1243 | | "(msg:\"Testing pcre relative http_cookie\"; " |
1244 | | "content:\"test\"; " |
1245 | | "http_cookie; pcre:\"/abc/RC\"; sid:1;)"); |
1246 | | FAIL_IF_NULL(de_ctx->sig_list); |
1247 | | |
1248 | | if (de_ctx != NULL) |
1249 | | SigCleanSignatures(de_ctx); |
1250 | | if (de_ctx != NULL) |
1251 | | DetectEngineCtxFree(de_ctx); |
1252 | | PASS; |
1253 | | } |
1254 | | |
1255 | | /** \test Check a signature with pcre relative raw header */ |
1256 | | static int DetectPcreParseTest17(void) |
1257 | | { |
1258 | | DetectEngineCtx *de_ctx = NULL; |
1259 | | |
1260 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1261 | | |
1262 | | de_ctx->flags |= DE_QUIET; |
1263 | | de_ctx->sig_list = SigInit(de_ctx, |
1264 | | "alert tcp any any -> any any " |
1265 | | "(msg:\"Testing pcre relative http_raw_header\"; " |
1266 | | "flow:to_server; content:\"test\"; " |
1267 | | "http_raw_header; pcre:\"/abc/RD\"; sid:1;)"); |
1268 | | FAIL_IF_NULL(de_ctx->sig_list); |
1269 | | |
1270 | | if (de_ctx != NULL) |
1271 | | SigCleanSignatures(de_ctx); |
1272 | | if (de_ctx != NULL) |
1273 | | DetectEngineCtxFree(de_ctx); |
1274 | | PASS; |
1275 | | } |
1276 | | |
1277 | | /** \test Check a signature with pcre relative header */ |
1278 | | static int DetectPcreParseTest18(void) |
1279 | | { |
1280 | | DetectEngineCtx *de_ctx = NULL; |
1281 | | |
1282 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1283 | | |
1284 | | de_ctx->flags |= DE_QUIET; |
1285 | | de_ctx->sig_list = SigInit(de_ctx, |
1286 | | "alert tcp any any -> any any " |
1287 | | "(msg:\"Testing pcre relative http_header\"; " |
1288 | | "content:\"test\"; " |
1289 | | "http_header; pcre:\"/abc/RH\"; sid:1;)"); |
1290 | | FAIL_IF_NULL(de_ctx->sig_list); |
1291 | | |
1292 | | if (de_ctx != NULL) |
1293 | | SigCleanSignatures(de_ctx); |
1294 | | if (de_ctx != NULL) |
1295 | | DetectEngineCtxFree(de_ctx); |
1296 | | PASS; |
1297 | | } |
1298 | | |
1299 | | /** \test Check a signature with pcre relative client-body */ |
1300 | | static int DetectPcreParseTest19(void) |
1301 | | { |
1302 | | DetectEngineCtx *de_ctx = NULL; |
1303 | | |
1304 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1305 | | |
1306 | | de_ctx->flags |= DE_QUIET; |
1307 | | de_ctx->sig_list = SigInit(de_ctx, |
1308 | | "alert tcp any any -> any any " |
1309 | | "(msg:\"Testing pcre relative http_client_body\"; " |
1310 | | "content:\"test\"; " |
1311 | | "http_client_body; pcre:\"/abc/RP\"; sid:1;)"); |
1312 | | FAIL_IF_NULL(de_ctx->sig_list); |
1313 | | |
1314 | | if (de_ctx != NULL) |
1315 | | SigCleanSignatures(de_ctx); |
1316 | | if (de_ctx != NULL) |
1317 | | DetectEngineCtxFree(de_ctx); |
1318 | | PASS; |
1319 | | } |
1320 | | |
1321 | | /** \test Check a signature with pcre relative raw uri */ |
1322 | | static int DetectPcreParseTest20(void) |
1323 | | { |
1324 | | DetectEngineCtx *de_ctx = NULL; |
1325 | | |
1326 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1327 | | |
1328 | | de_ctx->flags |= DE_QUIET; |
1329 | | de_ctx->sig_list = SigInit(de_ctx, |
1330 | | "alert tcp any any -> any any " |
1331 | | "(msg:\"Testing http_raw_uri\"; " |
1332 | | "content:\"test\"; " |
1333 | | "http_raw_uri; pcre:\"/abc/RI\"; sid:1;)"); |
1334 | | FAIL_IF_NULL(de_ctx->sig_list); |
1335 | | |
1336 | | if (de_ctx != NULL) |
1337 | | SigCleanSignatures(de_ctx); |
1338 | | if (de_ctx != NULL) |
1339 | | DetectEngineCtxFree(de_ctx); |
1340 | | PASS; |
1341 | | } |
1342 | | |
1343 | | /** \test Check a signature with pcre relative uricontent */ |
1344 | | static int DetectPcreParseTest21(void) |
1345 | | { |
1346 | | DetectEngineCtx *de_ctx = NULL; |
1347 | | |
1348 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1349 | | |
1350 | | de_ctx->flags |= DE_QUIET; |
1351 | | de_ctx->sig_list = SigInit(de_ctx, |
1352 | | "alert tcp any any -> any any " |
1353 | | "(msg:\"Testing pcre relative uricontent\"; " |
1354 | | "uricontent:\"test\"; " |
1355 | | "pcre:\"/abc/RU\"; sid:1;)"); |
1356 | | FAIL_IF_NULL(de_ctx->sig_list); |
1357 | | |
1358 | | if (de_ctx != NULL) |
1359 | | SigCleanSignatures(de_ctx); |
1360 | | if (de_ctx != NULL) |
1361 | | DetectEngineCtxFree(de_ctx); |
1362 | | PASS; |
1363 | | } |
1364 | | |
1365 | | /** \test Check a signature with pcre relative http_uri */ |
1366 | | static int DetectPcreParseTest22(void) |
1367 | | { |
1368 | | DetectEngineCtx *de_ctx = NULL; |
1369 | | |
1370 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1371 | | |
1372 | | de_ctx->flags |= DE_QUIET; |
1373 | | de_ctx->sig_list = SigInit(de_ctx, |
1374 | | "alert tcp any any -> any any " |
1375 | | "(msg:\"Testing pcre relative http_uri\"; " |
1376 | | "content:\"test\"; " |
1377 | | "http_uri; pcre:\"/abc/RU\"; sid:1;)"); |
1378 | | FAIL_IF_NULL(de_ctx->sig_list); |
1379 | | |
1380 | | if (de_ctx != NULL) |
1381 | | SigCleanSignatures(de_ctx); |
1382 | | if (de_ctx != NULL) |
1383 | | DetectEngineCtxFree(de_ctx); |
1384 | | PASS; |
1385 | | } |
1386 | | |
1387 | | /** \test Check a signature with inconsistent pcre relative */ |
1388 | | static int DetectPcreParseTest23(void) |
1389 | | { |
1390 | | DetectEngineCtx *de_ctx = NULL; |
1391 | | |
1392 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1393 | | |
1394 | | de_ctx->flags |= DE_QUIET; |
1395 | | de_ctx->sig_list = SigInit(de_ctx, |
1396 | | "alert tcp any any -> any any " |
1397 | | "(msg:\"Testing inconsistent pcre relative\"; " |
1398 | | "content:\"GET\"; " |
1399 | | "http_cookie; pcre:\"/abc/RM\"; sid:1;)"); |
1400 | | FAIL_IF_NOT_NULL(de_ctx->sig_list); |
1401 | | |
1402 | | if (de_ctx != NULL) |
1403 | | SigCleanSignatures(de_ctx); |
1404 | | if (de_ctx != NULL) |
1405 | | DetectEngineCtxFree(de_ctx); |
1406 | | PASS; |
1407 | | } |
1408 | | |
1409 | | /** \test Check a signature with inconsistent pcre modifiers */ |
1410 | | static int DetectPcreParseTest24(void) |
1411 | | { |
1412 | | DetectEngineCtx *de_ctx = NULL; |
1413 | | |
1414 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1415 | | |
1416 | | de_ctx->flags |= DE_QUIET; |
1417 | | de_ctx->sig_list = SigInit(de_ctx, |
1418 | | "alert tcp any any -> any any " |
1419 | | "(msg:\"Testing inconsistent pcre modifiers\"; " |
1420 | | "pcre:\"/abc/UI\"; sid:1;)"); |
1421 | | FAIL_IF_NOT_NULL(de_ctx->sig_list); |
1422 | | |
1423 | | if (de_ctx != NULL) |
1424 | | SigCleanSignatures(de_ctx); |
1425 | | if (de_ctx != NULL) |
1426 | | DetectEngineCtxFree(de_ctx); |
1427 | | PASS; |
1428 | | } |
1429 | | |
1430 | | /** \test Check a signature with inconsistent pcre modifiers */ |
1431 | | static int DetectPcreParseTest25(void) |
1432 | | { |
1433 | | DetectEngineCtx *de_ctx = NULL; |
1434 | | |
1435 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1436 | | |
1437 | | de_ctx->flags |= DE_QUIET; |
1438 | | de_ctx->sig_list = SigInit(de_ctx, |
1439 | | "alert tcp any any -> any any " |
1440 | | "(msg:\"Testing inconsistent pcre modifiers\"; " |
1441 | | "pcre:\"/abc/DH\"; sid:1;)"); |
1442 | | FAIL_IF_NOT_NULL(de_ctx->sig_list); |
1443 | | |
1444 | | if (de_ctx != NULL) |
1445 | | SigCleanSignatures(de_ctx); |
1446 | | if (de_ctx != NULL) |
1447 | | DetectEngineCtxFree(de_ctx); |
1448 | | PASS; |
1449 | | } |
1450 | | |
1451 | | /** \test Check a signature with inconsistent pcre modifiers */ |
1452 | | static int DetectPcreParseTest26(void) |
1453 | | { |
1454 | | DetectEngineCtx *de_ctx = NULL; |
1455 | | |
1456 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1457 | | |
1458 | | de_ctx->flags |= DE_QUIET; |
1459 | | de_ctx->sig_list = SigInit(de_ctx, |
1460 | | "alert http any any -> any any " |
1461 | | "(msg:\"Testing inconsistent pcre modifiers\"; " |
1462 | | "pcre:\"/abc/F\"; sid:1;)"); |
1463 | | FAIL_IF_NOT_NULL(de_ctx->sig_list); |
1464 | | |
1465 | | if (de_ctx != NULL) |
1466 | | SigCleanSignatures(de_ctx); |
1467 | | if (de_ctx != NULL) |
1468 | | DetectEngineCtxFree(de_ctx); |
1469 | | PASS; |
1470 | | } |
1471 | | |
1472 | | /** \test Bug 1098 */ |
1473 | | static int DetectPcreParseTest27(void) |
1474 | | { |
1475 | | DetectEngineCtx *de_ctx = NULL; |
1476 | | |
1477 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1478 | | |
1479 | | de_ctx->flags |= DE_QUIET; |
1480 | | de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any 80 " |
1481 | | "(content:\"baduricontent\"; http_raw_uri; " |
1482 | | "pcre:\"/^[a-z]{5}\\.html/R\"; sid:2; rev:2;)"); |
1483 | | FAIL_IF_NOT(de_ctx->sig_list == NULL); |
1484 | | |
1485 | | if (de_ctx != NULL) |
1486 | | SigCleanSignatures(de_ctx); |
1487 | | if (de_ctx != NULL) |
1488 | | DetectEngineCtxFree(de_ctx); |
1489 | | PASS; |
1490 | | } |
1491 | | |
1492 | | /** \test Bug 1957 */ |
1493 | | static int DetectPcreParseTest28(void) |
1494 | | { |
1495 | | DetectEngineCtx *de_ctx = NULL; |
1496 | | |
1497 | | FAIL_IF( (de_ctx = DetectEngineCtxInit()) == NULL); |
1498 | | |
1499 | | de_ctx->flags |= DE_QUIET; |
1500 | | de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any 80 " |
1501 | | "(content:\"|2E|suricata\"; http_host; pcre:\"/\\x2Esuricata$/W\"; " |
1502 | | "sid:2; rev:2;)"); |
1503 | | FAIL_IF_NULL(de_ctx->sig_list); |
1504 | | |
1505 | | DetectEngineCtxFree(de_ctx); |
1506 | | PASS; |
1507 | | } |
1508 | | |
1509 | | static int DetectPcreTestSig01(void) |
1510 | | { |
1511 | | uint8_t *buf = (uint8_t *)"lalala lalala\\ lala\n"; |
1512 | | uint16_t buflen = strlen((char *)buf); |
1513 | | Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP); |
1514 | | int result = 0; |
1515 | | |
1516 | | char sig[] = "alert tcp any any -> any any (msg:\"pcre with an ending slash\"; pcre:\"/ " |
1517 | | "lalala\\\\/\"; sid:1;)"; |
1518 | | if (UTHPacketMatchSig(p, sig) == 0) { |
1519 | | result = 0; |
1520 | | goto end; |
1521 | | } |
1522 | | result = 1; |
1523 | | end: |
1524 | | if (p != NULL) |
1525 | | UTHFreePacket(p); |
1526 | | return result; |
1527 | | } |
1528 | | |
1529 | | /** \test anchored pcre */ |
1530 | | static int DetectPcreTestSig02(void) |
1531 | | { |
1532 | | uint8_t *buf = (uint8_t *)"lalala\n"; |
1533 | | uint16_t buflen = strlen((char *)buf); |
1534 | | Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP); |
1535 | | |
1536 | | char sig[] = "alert tcp any any -> any any (msg:\"pcre with an ending slash\"; " |
1537 | | "pcre:\"/^(la)+$/\"; sid:1;)"; |
1538 | | FAIL_IF(UTHPacketMatchSig(p, sig) == 0); |
1539 | | |
1540 | | if (p != NULL) |
1541 | | UTHFreePacket(p); |
1542 | | PASS; |
1543 | | } |
1544 | | |
1545 | | /** \test anchored pcre */ |
1546 | | static int DetectPcreTestSig03(void) |
1547 | | { |
1548 | | /* test it also without ending in a newline "\n" */ |
1549 | | uint8_t *buf = (uint8_t *)"lalala"; |
1550 | | uint16_t buflen = strlen((char *)buf); |
1551 | | Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP); |
1552 | | |
1553 | | char sig[] = "alert tcp any any -> any any (msg:\"pcre with an ending slash\"; " |
1554 | | "pcre:\"/^(la)+$/\"; sid:1;)"; |
1555 | | FAIL_IF(UTHPacketMatchSig(p, sig) == 0); |
1556 | | |
1557 | | if (p != NULL) |
1558 | | UTHFreePacket(p); |
1559 | | PASS; |
1560 | | } |
1561 | | |
1562 | | /** \test Test tracking of body chunks per transactions (on requests) |
1563 | | */ |
1564 | | static int DetectPcreTxBodyChunksTest01(void) |
1565 | | { |
1566 | | Flow f; |
1567 | | TcpSession ssn; |
1568 | | Packet *p = NULL; |
1569 | | uint8_t httpbuf1[] = "GET / HTTP/1.1\r\n"; |
1570 | | uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n"; |
1571 | | uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n"; |
1572 | | uint8_t httpbuf4[] = "Body one!!"; |
1573 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
1574 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
1575 | | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ |
1576 | | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ |
1577 | | uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n"; |
1578 | | uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n"; |
1579 | | uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!"; |
1580 | | uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ |
1581 | | uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ |
1582 | | uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */ |
1583 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
1584 | | |
1585 | | memset(&f, 0, sizeof(f)); |
1586 | | memset(&ssn, 0, sizeof(ssn)); |
1587 | | |
1588 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
1589 | | |
1590 | | FLOW_INITIALIZE(&f); |
1591 | | f.protoctx = (void *)&ssn; |
1592 | | f.proto = IPPROTO_TCP; |
1593 | | f.flags |= FLOW_IPV4; |
1594 | | |
1595 | | p->flow = &f; |
1596 | | p->flowflags |= FLOW_PKT_TOSERVER; |
1597 | | p->flowflags |= FLOW_PKT_ESTABLISHED; |
1598 | | p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; |
1599 | | f.alproto = ALPROTO_HTTP1; |
1600 | | |
1601 | | StreamTcpInitConfig(true); |
1602 | | |
1603 | | AppLayerHtpEnableRequestBodyCallback(); |
1604 | | |
1605 | | int r = AppLayerParserParse( |
1606 | | NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1); |
1607 | | FAIL_IF(r != 0); |
1608 | | |
1609 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2); |
1610 | | FAIL_IF(r != 0); |
1611 | | |
1612 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf3, httplen3); |
1613 | | FAIL_IF(r != 0); |
1614 | | |
1615 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf4, httplen4); |
1616 | | FAIL_IF(r != 0); |
1617 | | |
1618 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf5, httplen5); |
1619 | | FAIL_IF(r != 0); |
1620 | | |
1621 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf6, httplen6); |
1622 | | FAIL_IF(r != 0); |
1623 | | |
1624 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf7, httplen7); |
1625 | | FAIL_IF(r != 0); |
1626 | | |
1627 | | /* Now we should have 2 transactions, each with it's own list |
1628 | | * of request body chunks (let's test it) */ |
1629 | | |
1630 | | HtpState *htp_state = f.alstate; |
1631 | | FAIL_IF(htp_state == NULL); |
1632 | | |
1633 | | /* hardcoded check of the transactions and it's client body chunks */ |
1634 | | FAIL_IF(AppLayerParserGetTxCnt(&f, htp_state) != 2); |
1635 | | |
1636 | | htp_tx_t *t1 = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, htp_state, 0); |
1637 | | htp_tx_t *t2 = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, htp_state, 1); |
1638 | | |
1639 | | HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(t1); |
1640 | | FAIL_IF(htud == NULL); |
1641 | | |
1642 | | HtpBodyChunk *cur = htud->request_body.first; |
1643 | | FAIL_IF(htud->request_body.first == NULL); |
1644 | | |
1645 | | FAIL_IF(StreamingBufferSegmentCompareRawData(htud->request_body.sb, &cur->sbseg, (uint8_t *)"Body one!!", 10) != 1); |
1646 | | |
1647 | | htud = (HtpTxUserData *) htp_tx_get_user_data(t2); |
1648 | | |
1649 | | cur = htud->request_body.first; |
1650 | | FAIL_IF(htud->request_body.first == NULL); |
1651 | | |
1652 | | FAIL_IF(StreamingBufferSegmentCompareRawData(htud->request_body.sb, &cur->sbseg, (uint8_t *)"Body two!!", 10) != 1); |
1653 | | |
1654 | | if (alp_tctx != NULL) |
1655 | | AppLayerParserThreadCtxFree(alp_tctx); |
1656 | | StreamTcpFreeConfig(true); |
1657 | | FLOW_DESTROY(&f); |
1658 | | UTHFreePacket(p); |
1659 | | PASS; |
1660 | | } |
1661 | | |
1662 | | /** \test test pcre P modifier with multiple pipelined http transactions */ |
1663 | | static int DetectPcreTxBodyChunksTest02(void) |
1664 | | { |
1665 | | Signature *s = NULL; |
1666 | | DetectEngineThreadCtx *det_ctx = NULL; |
1667 | | ThreadVars th_v; |
1668 | | Flow f; |
1669 | | TcpSession ssn; |
1670 | | Packet *p = NULL; |
1671 | | uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n"; |
1672 | | uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n"; |
1673 | | uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n"; |
1674 | | uint8_t httpbuf4[] = "Body one!!"; |
1675 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
1676 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
1677 | | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ |
1678 | | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ |
1679 | | uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n"; |
1680 | | uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n"; |
1681 | | uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!"; |
1682 | | uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ |
1683 | | uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ |
1684 | | uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */ |
1685 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
1686 | | |
1687 | | memset(&th_v, 0, sizeof(th_v)); |
1688 | | memset(&f, 0, sizeof(f)); |
1689 | | memset(&ssn, 0, sizeof(ssn)); |
1690 | | |
1691 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
1692 | | |
1693 | | FLOW_INITIALIZE(&f); |
1694 | | f.protoctx = (void *)&ssn; |
1695 | | f.proto = IPPROTO_TCP; |
1696 | | f.flags |= FLOW_IPV4; |
1697 | | |
1698 | | p->flow = &f; |
1699 | | p->flowflags |= FLOW_PKT_TOSERVER; |
1700 | | p->flowflags |= FLOW_PKT_ESTABLISHED; |
1701 | | p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; |
1702 | | f.alproto = ALPROTO_HTTP1; |
1703 | | |
1704 | | StreamTcpInitConfig(true); |
1705 | | |
1706 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1707 | | FAIL_IF(de_ctx == NULL); |
1708 | | |
1709 | | de_ctx->flags |= DE_QUIET; |
1710 | | |
1711 | | s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; pcre:\"/one/P\"; sid:1; rev:1;)"); |
1712 | | FAIL_IF(s == NULL); |
1713 | | s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; pcre:\"/two/P\"; sid:2; rev:1;)"); |
1714 | | FAIL_IF(s == NULL); |
1715 | | |
1716 | | SigGroupBuild(de_ctx); |
1717 | | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); |
1718 | | |
1719 | | int r = AppLayerParserParse( |
1720 | | NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf1, httplen1); |
1721 | | FAIL_IF(r != 0); |
1722 | | |
1723 | | /* do detect */ |
1724 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1725 | | FAIL_IF(PacketAlertCheck(p, 1)); |
1726 | | p->alerts.cnt = 0; |
1727 | | |
1728 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2); |
1729 | | FAIL_IF(r != 0); |
1730 | | |
1731 | | /* do detect */ |
1732 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1733 | | FAIL_IF(PacketAlertCheck(p, 1)); |
1734 | | p->alerts.cnt = 0; |
1735 | | |
1736 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf3, httplen3); |
1737 | | FAIL_IF(r != 0); |
1738 | | |
1739 | | /* do detect */ |
1740 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1741 | | FAIL_IF(PacketAlertCheck(p, 1)); |
1742 | | p->alerts.cnt = 0; |
1743 | | |
1744 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf4, httplen4); |
1745 | | FAIL_IF(r != 0); |
1746 | | |
1747 | | /* do detect */ |
1748 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1749 | | FAIL_IF(!(PacketAlertCheck(p, 1))); |
1750 | | p->alerts.cnt = 0; |
1751 | | |
1752 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf5, httplen5); |
1753 | | FAIL_IF(r != 0); |
1754 | | |
1755 | | /* do detect */ |
1756 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1757 | | FAIL_IF(PacketAlertCheck(p, 1)); |
1758 | | p->alerts.cnt = 0; |
1759 | | |
1760 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf6, httplen6); |
1761 | | FAIL_IF(r != 0); |
1762 | | |
1763 | | /* do detect */ |
1764 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1765 | | FAIL_IF((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))); |
1766 | | p->alerts.cnt = 0; |
1767 | | |
1768 | | SCLogDebug("sending data chunk 7"); |
1769 | | |
1770 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf7, httplen7); |
1771 | | FAIL_IF(r != 0); |
1772 | | |
1773 | | /* do detect */ |
1774 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1775 | | FAIL_IF(!(PacketAlertCheck(p, 2))); |
1776 | | p->alerts.cnt = 0; |
1777 | | |
1778 | | HtpState *htp_state = f.alstate; |
1779 | | FAIL_IF(htp_state == NULL); |
1780 | | |
1781 | | /* hardcoded check of the transactions and it's client body chunks */ |
1782 | | FAIL_IF(AppLayerParserGetTxCnt(&f, htp_state) != 2); |
1783 | | |
1784 | | htp_tx_t *t1 = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, htp_state, 0); |
1785 | | htp_tx_t *t2 = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, htp_state, 1); |
1786 | | |
1787 | | HtpTxUserData *htud = (HtpTxUserData *) htp_tx_get_user_data(t1); |
1788 | | |
1789 | | HtpBodyChunk *cur = htud->request_body.first; |
1790 | | FAIL_IF(htud->request_body.first == NULL); |
1791 | | |
1792 | | FAIL_IF(StreamingBufferSegmentCompareRawData(htud->request_body.sb, &cur->sbseg, (uint8_t *)"Body one!!", 10) != 1); |
1793 | | |
1794 | | htud = (HtpTxUserData *) htp_tx_get_user_data(t2); |
1795 | | |
1796 | | cur = htud->request_body.first; |
1797 | | FAIL_IF(htud->request_body.first == NULL); |
1798 | | |
1799 | | FAIL_IF(StreamingBufferSegmentCompareRawData(htud->request_body.sb, &cur->sbseg, (uint8_t *)"Body two!!", 10) != 1); |
1800 | | |
1801 | | if (alp_tctx != NULL) |
1802 | | AppLayerParserThreadCtxFree(alp_tctx); |
1803 | | if (det_ctx != NULL) { |
1804 | | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); |
1805 | | } |
1806 | | if (de_ctx != NULL) { |
1807 | | SigGroupCleanup(de_ctx); |
1808 | | DetectEngineCtxFree(de_ctx); |
1809 | | } |
1810 | | |
1811 | | StreamTcpFreeConfig(true); |
1812 | | FLOW_DESTROY(&f); |
1813 | | UTHFreePacket(p); |
1814 | | PASS; |
1815 | | } |
1816 | | |
1817 | | /** \test multiple http transactions and body chunks of request handling */ |
1818 | | static int DetectPcreTxBodyChunksTest03(void) |
1819 | | { |
1820 | | Signature *s = NULL; |
1821 | | DetectEngineThreadCtx *det_ctx = NULL; |
1822 | | ThreadVars th_v; |
1823 | | Flow f; |
1824 | | TcpSession ssn; |
1825 | | Packet *p = NULL; |
1826 | | uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n"; |
1827 | | uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n"; |
1828 | | uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n"; |
1829 | | uint8_t httpbuf4[] = "Body one!!"; |
1830 | | uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ |
1831 | | uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ |
1832 | | uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ |
1833 | | uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ |
1834 | | uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n"; |
1835 | | uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n"; |
1836 | | uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!"; |
1837 | | uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ |
1838 | | uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ |
1839 | | uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */ |
1840 | | AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); |
1841 | | |
1842 | | memset(&th_v, 0, sizeof(th_v)); |
1843 | | memset(&f, 0, sizeof(f)); |
1844 | | memset(&ssn, 0, sizeof(ssn)); |
1845 | | |
1846 | | p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); |
1847 | | |
1848 | | FLOW_INITIALIZE(&f); |
1849 | | f.protoctx = (void *)&ssn; |
1850 | | f.proto = IPPROTO_TCP; |
1851 | | f.flags |= FLOW_IPV4; |
1852 | | |
1853 | | p->flow = &f; |
1854 | | p->flowflags |= FLOW_PKT_TOSERVER; |
1855 | | p->flowflags |= FLOW_PKT_ESTABLISHED; |
1856 | | p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; |
1857 | | f.alproto = ALPROTO_HTTP1; |
1858 | | |
1859 | | StreamTcpInitConfig(true); |
1860 | | |
1861 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1862 | | FAIL_IF(de_ctx == NULL); |
1863 | | |
1864 | | de_ctx->flags |= DE_QUIET; |
1865 | | |
1866 | | s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; pcre:\"/one/P\"; sid:1; rev:1;)"); |
1867 | | FAIL_IF(s == NULL); |
1868 | | s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; pcre:\"/two/P\"; sid:2; rev:1;)"); |
1869 | | FAIL_IF(s == NULL); |
1870 | | |
1871 | | SigGroupBuild(de_ctx); |
1872 | | DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); |
1873 | | |
1874 | | int r = AppLayerParserParse( |
1875 | | NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf1, httplen1); |
1876 | | FAIL_IF(r != 0); |
1877 | | |
1878 | | /* do detect */ |
1879 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1880 | | FAIL_IF(PacketAlertCheck(p, 1)); |
1881 | | p->alerts.cnt = 0; |
1882 | | |
1883 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2); |
1884 | | FAIL_IF(r != 0); |
1885 | | |
1886 | | /* do detect */ |
1887 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1888 | | FAIL_IF(PacketAlertCheck(p, 1)); |
1889 | | p->alerts.cnt = 0; |
1890 | | |
1891 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf3, httplen3); |
1892 | | FAIL_IF(r != 0); |
1893 | | |
1894 | | /* do detect */ |
1895 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1896 | | FAIL_IF(PacketAlertCheck(p, 1)); |
1897 | | p->alerts.cnt = 0; |
1898 | | |
1899 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf4, httplen4); |
1900 | | FAIL_IF(r != 0); |
1901 | | |
1902 | | /* do detect */ |
1903 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1904 | | FAIL_IF(!(PacketAlertCheck(p, 1))); |
1905 | | p->alerts.cnt = 0; |
1906 | | |
1907 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf5, httplen5); |
1908 | | FAIL_IF(r != 0); |
1909 | | |
1910 | | /* do detect */ |
1911 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1912 | | FAIL_IF(PacketAlertCheck(p, 1)); |
1913 | | p->alerts.cnt = 0; |
1914 | | |
1915 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf6, httplen6); |
1916 | | FAIL_IF(r != 0); |
1917 | | |
1918 | | /* do detect */ |
1919 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1920 | | FAIL_IF((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))); |
1921 | | p->alerts.cnt = 0; |
1922 | | |
1923 | | SCLogDebug("sending data chunk 7"); |
1924 | | |
1925 | | r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf7, httplen7); |
1926 | | FAIL_IF(r != 0); |
1927 | | |
1928 | | /* do detect */ |
1929 | | SigMatchSignatures(&th_v, de_ctx, det_ctx, p); |
1930 | | FAIL_IF(!(PacketAlertCheck(p, 2))); |
1931 | | p->alerts.cnt = 0; |
1932 | | |
1933 | | HtpState *htp_state = f.alstate; |
1934 | | FAIL_IF(htp_state == NULL); |
1935 | | |
1936 | | FAIL_IF(AppLayerParserGetTxCnt(&f, htp_state) != 2); |
1937 | | |
1938 | | if (alp_tctx != NULL) |
1939 | | AppLayerParserThreadCtxFree(alp_tctx); |
1940 | | if (det_ctx != NULL) { |
1941 | | DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); |
1942 | | } |
1943 | | if (de_ctx != NULL) { |
1944 | | SigGroupCleanup(de_ctx); |
1945 | | DetectEngineCtxFree(de_ctx); |
1946 | | } |
1947 | | |
1948 | | StreamTcpFreeConfig(true); |
1949 | | FLOW_DESTROY(&f); |
1950 | | UTHFreePacket(p); |
1951 | | PASS; |
1952 | | } |
1953 | | |
1954 | | /** |
1955 | | * \brief Test parsing of pcre's with the W modifier set. |
1956 | | */ |
1957 | | static int DetectPcreParseHttpHost(void) |
1958 | | { |
1959 | | AppProto alproto = ALPROTO_UNKNOWN; |
1960 | | int list = DETECT_SM_LIST_NOTSET; |
1961 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1962 | | |
1963 | | FAIL_IF(de_ctx == NULL); |
1964 | | |
1965 | | DetectPcreData *pd = DetectPcreParse(de_ctx, "/domain\\.com/W", &list, NULL, 0, false, &alproto); |
1966 | | FAIL_IF(pd == NULL); |
1967 | | DetectPcreFree(de_ctx, pd); |
1968 | | |
1969 | | list = DETECT_SM_LIST_NOTSET; |
1970 | | pd = DetectPcreParse(de_ctx, "/dOmain\\.com/W", &list, NULL, 0, false, &alproto); |
1971 | | FAIL_IF(pd != NULL); |
1972 | | |
1973 | | /* Uppercase meta characters are valid. */ |
1974 | | list = DETECT_SM_LIST_NOTSET; |
1975 | | pd = DetectPcreParse(de_ctx, "/domain\\D+\\.com/W", &list, NULL, 0, false, &alproto); |
1976 | | FAIL_IF(pd == NULL); |
1977 | | DetectPcreFree(de_ctx, pd); |
1978 | | |
1979 | | /* This should not parse as the first \ escapes the second \, then |
1980 | | * we have a D. */ |
1981 | | list = DETECT_SM_LIST_NOTSET; |
1982 | | pd = DetectPcreParse(de_ctx, "/\\\\Ddomain\\.com/W", &list, NULL, 0, false, &alproto); |
1983 | | FAIL_IF(pd != NULL); |
1984 | | |
1985 | | DetectEngineCtxFree(de_ctx); |
1986 | | PASS; |
1987 | | } |
1988 | | |
1989 | | /** |
1990 | | * \brief Test parsing of capture extension |
1991 | | */ |
1992 | | static int DetectPcreParseCaptureTest(void) |
1993 | | { |
1994 | | DetectEngineCtx *de_ctx = DetectEngineCtxInit(); |
1995 | | FAIL_IF(de_ctx == NULL); |
1996 | | |
1997 | | Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any " |
1998 | | "(content:\"Server: \"; http_header; pcre:\"/(.*)\\r\\n/HR, flow:somecapture\"; content:\"xyz\"; http_header; sid:1;)"); |
1999 | | FAIL_IF(s == NULL); |
2000 | | s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any " |
2001 | | "(content:\"Server: \"; http_header; pcre:\"/(flow:.*)\\r\\n/HR\"; content:\"xyz\"; http_header; sid:2;)"); |
2002 | | FAIL_IF(s == NULL); |
2003 | | s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any " |
2004 | | "(content:\"Server: \"; http_header; pcre:\"/([a-z]+)([0-9]+)\\r\\n/HR, flow:somecapture, pkt:anothercap\"; content:\"xyz\"; http_header; sid:3;)"); |
2005 | | FAIL_IF(s == NULL); |
2006 | | s = DetectEngineAppendSig(de_ctx, |
2007 | | "alert http any any -> any any " |
2008 | | "(content:\"Server: \"; http_header; pcre:\"/([a-z]+)\\r\\n/HR, flow:somecapture, " |
2009 | | "pkt:anothercap\"; content:\"xyz\"; http_header; sid:3;)"); |
2010 | | FAIL_IF_NOT_NULL(s); |
2011 | | |
2012 | | SigGroupBuild(de_ctx); |
2013 | | |
2014 | | uint32_t capid1 = VarNameStoreLookupByName("somecapture", VAR_TYPE_FLOW_VAR); |
2015 | | FAIL_IF(capid1 == 0); |
2016 | | uint32_t capid2 = VarNameStoreLookupByName("anothercap", VAR_TYPE_PKT_VAR); |
2017 | | FAIL_IF(capid2 == 0); |
2018 | | FAIL_IF(capid1 == capid2); |
2019 | | |
2020 | | DetectEngineCtxFree(de_ctx); |
2021 | | PASS; |
2022 | | } |
2023 | | |
2024 | | /** |
2025 | | * \brief this function registers unit tests for DetectPcre |
2026 | | */ |
2027 | | static void DetectPcreRegisterTests(void) |
2028 | | { |
2029 | | g_file_data_buffer_id = DetectBufferTypeGetByName("file_data"); |
2030 | | g_http_header_buffer_id = DetectBufferTypeGetByName("http_header"); |
2031 | | g_dce_stub_data_buffer_id = DetectBufferTypeGetByName("dce_stub_data"); |
2032 | | |
2033 | | UtRegisterTest("DetectPcreParseTest01", DetectPcreParseTest01); |
2034 | | UtRegisterTest("DetectPcreParseTest02", DetectPcreParseTest02); |
2035 | | UtRegisterTest("DetectPcreParseTest03", DetectPcreParseTest03); |
2036 | | UtRegisterTest("DetectPcreParseTest04", DetectPcreParseTest04); |
2037 | | UtRegisterTest("DetectPcreParseTest05", DetectPcreParseTest05); |
2038 | | UtRegisterTest("DetectPcreParseTest06", DetectPcreParseTest06); |
2039 | | UtRegisterTest("DetectPcreParseTest07", DetectPcreParseTest07); |
2040 | | UtRegisterTest("DetectPcreParseTest08", DetectPcreParseTest08); |
2041 | | UtRegisterTest("DetectPcreParseTest09", DetectPcreParseTest09); |
2042 | | UtRegisterTest("DetectPcreParseTest10", DetectPcreParseTest10); |
2043 | | UtRegisterTest("DetectPcreParseTest15", DetectPcreParseTest15); |
2044 | | UtRegisterTest("DetectPcreParseTest16", DetectPcreParseTest16); |
2045 | | UtRegisterTest("DetectPcreParseTest17", DetectPcreParseTest17); |
2046 | | UtRegisterTest("DetectPcreParseTest18", DetectPcreParseTest18); |
2047 | | UtRegisterTest("DetectPcreParseTest19", DetectPcreParseTest19); |
2048 | | UtRegisterTest("DetectPcreParseTest20", DetectPcreParseTest20); |
2049 | | UtRegisterTest("DetectPcreParseTest21", DetectPcreParseTest21); |
2050 | | UtRegisterTest("DetectPcreParseTest22", DetectPcreParseTest22); |
2051 | | UtRegisterTest("DetectPcreParseTest23", DetectPcreParseTest23); |
2052 | | UtRegisterTest("DetectPcreParseTest24", DetectPcreParseTest24); |
2053 | | UtRegisterTest("DetectPcreParseTest25", DetectPcreParseTest25); |
2054 | | UtRegisterTest("DetectPcreParseTest26", DetectPcreParseTest26); |
2055 | | UtRegisterTest("DetectPcreParseTest27", DetectPcreParseTest27); |
2056 | | UtRegisterTest("DetectPcreParseTest28", DetectPcreParseTest28); |
2057 | | |
2058 | | UtRegisterTest("DetectPcreTestSig01", DetectPcreTestSig01); |
2059 | | UtRegisterTest("DetectPcreTestSig02 -- anchored pcre", DetectPcreTestSig02); |
2060 | | UtRegisterTest("DetectPcreTestSig03 -- anchored pcre", DetectPcreTestSig03); |
2061 | | |
2062 | | UtRegisterTest("DetectPcreTxBodyChunksTest01", |
2063 | | DetectPcreTxBodyChunksTest01); |
2064 | | UtRegisterTest("DetectPcreTxBodyChunksTest02 -- modifier P, body chunks per tx", |
2065 | | DetectPcreTxBodyChunksTest02); |
2066 | | UtRegisterTest("DetectPcreTxBodyChunksTest03 -- modifier P, body chunks per tx", |
2067 | | DetectPcreTxBodyChunksTest03); |
2068 | | |
2069 | | UtRegisterTest("DetectPcreParseHttpHost", DetectPcreParseHttpHost); |
2070 | | UtRegisterTest("DetectPcreParseCaptureTest", DetectPcreParseCaptureTest); |
2071 | | |
2072 | | } |
2073 | | #endif /* UNITTESTS */ |