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