/src/suricata/src/detect-transform-pcrexform.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2020 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 Jeff Lucovsky <jeff@lucovsky.org> |
22 | | * |
23 | | * Implements the pcrexform transform keyword with option support |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | |
28 | | #include "detect.h" |
29 | | #include "detect-engine.h" |
30 | | #include "detect-engine-buffer.h" |
31 | | #include "detect-transform-pcrexform.h" |
32 | | #include "detect-pcre.h" |
33 | | |
34 | | typedef struct DetectTransformPcrexformData { |
35 | | pcre2_code *regex; |
36 | | pcre2_match_context *context; |
37 | | uint8_t *id_data; |
38 | | uint32_t id_data_len; |
39 | | } DetectTransformPcrexformData; |
40 | | |
41 | | static int DetectTransformPcrexformSetup (DetectEngineCtx *, Signature *, const char *); |
42 | | static void DetectTransformPcrexformFree(DetectEngineCtx *, void *); |
43 | | static void DetectTransformPcrexform( |
44 | | DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, void *options); |
45 | | #ifdef UNITTESTS |
46 | | void DetectTransformPcrexformRegisterTests (void); |
47 | | #endif |
48 | | |
49 | | static void DetectTransformPcrexformId(const uint8_t **data, uint32_t *length, void *context) |
50 | 62.2k | { |
51 | 62.2k | if (context) { |
52 | 62.2k | DetectTransformPcrexformData *pxd = (DetectTransformPcrexformData *)context; |
53 | 62.2k | *data = (const uint8_t *)pxd->id_data; |
54 | 62.2k | *length = pxd->id_data_len; |
55 | 62.2k | } |
56 | 62.2k | } |
57 | | |
58 | | void DetectTransformPcrexformRegister(void) |
59 | 73 | { |
60 | 73 | sigmatch_table[DETECT_TRANSFORM_PCREXFORM].name = "pcrexform"; |
61 | 73 | sigmatch_table[DETECT_TRANSFORM_PCREXFORM].desc = |
62 | 73 | "modify buffer via PCRE before inspection"; |
63 | 73 | sigmatch_table[DETECT_TRANSFORM_PCREXFORM].url = "/rules/transforms.html#pcre-xform"; |
64 | 73 | sigmatch_table[DETECT_TRANSFORM_PCREXFORM].Transform = |
65 | 73 | DetectTransformPcrexform; |
66 | 73 | sigmatch_table[DETECT_TRANSFORM_PCREXFORM].TransformId = DetectTransformPcrexformId; |
67 | 73 | sigmatch_table[DETECT_TRANSFORM_PCREXFORM].Free = |
68 | 73 | DetectTransformPcrexformFree; |
69 | 73 | sigmatch_table[DETECT_TRANSFORM_PCREXFORM].Setup = |
70 | 73 | DetectTransformPcrexformSetup; |
71 | | #ifdef UNITTESTS |
72 | | sigmatch_table[DETECT_TRANSFORM_PCREXFORM].RegisterTests = DetectTransformPcrexformRegisterTests; |
73 | | #endif |
74 | 73 | sigmatch_table[DETECT_TRANSFORM_PCREXFORM].flags |= SIGMATCH_QUOTES_MANDATORY; |
75 | 73 | } |
76 | | |
77 | | static void DetectTransformPcrexformFree(DetectEngineCtx *de_ctx, void *ptr) |
78 | 76.7k | { |
79 | 76.7k | if (ptr != NULL) { |
80 | 76.7k | DetectTransformPcrexformData *pxd = (DetectTransformPcrexformData *) ptr; |
81 | | |
82 | 76.7k | pcre2_match_context_free(pxd->context); |
83 | 76.7k | pcre2_code_free(pxd->regex); |
84 | | |
85 | 76.7k | SCFree(pxd->id_data); |
86 | 76.7k | SCFree(pxd); |
87 | 76.7k | } |
88 | 76.7k | } |
89 | | |
90 | | /** |
91 | | * \internal |
92 | | * \brief Apply the pcrexform keyword to the last pattern match |
93 | | * \param det_ctx detection engine ctx |
94 | | * \param s signature |
95 | | * \param regexstr options string |
96 | | * \retval 0 ok |
97 | | * \retval -1 failure |
98 | | */ |
99 | | static int DetectTransformPcrexformSetup (DetectEngineCtx *de_ctx, Signature *s, const char *regexstr) |
100 | 80.9k | { |
101 | 80.9k | SCEnter(); |
102 | | |
103 | | // Create pxd from regexstr |
104 | 80.9k | DetectTransformPcrexformData *pxd = SCCalloc(1, sizeof(*pxd)); |
105 | 80.9k | if (pxd == NULL) { |
106 | 0 | SCLogDebug("pxd allocation failed"); |
107 | 0 | SCReturnInt(-1); |
108 | 0 | } |
109 | | |
110 | 80.9k | pxd->context = pcre2_match_context_create(NULL); |
111 | 80.9k | if (pxd->context == NULL) { |
112 | 0 | SCFree(pxd); |
113 | 0 | SCReturnInt(-1); |
114 | 0 | } |
115 | 80.9k | pcre2_set_match_limit(pxd->context, SC_MATCH_LIMIT_DEFAULT); |
116 | 80.9k | pcre2_set_recursion_limit(pxd->context, SC_MATCH_LIMIT_RECURSION_DEFAULT); |
117 | 80.9k | int en; |
118 | 80.9k | PCRE2_SIZE eo; |
119 | 80.9k | pxd->regex = pcre2_compile((PCRE2_SPTR8)regexstr, PCRE2_ZERO_TERMINATED, 0, &en, &eo, NULL); |
120 | 80.9k | if (pxd->regex == NULL) { |
121 | 5.56k | PCRE2_UCHAR buffer[256]; |
122 | 5.56k | pcre2_get_error_message(en, buffer, sizeof(buffer)); |
123 | 5.56k | SCLogError("pcre2 compile of \"%s\" failed at " |
124 | 5.56k | "offset %d: %s", |
125 | 5.56k | regexstr, (int)eo, buffer); |
126 | 5.56k | pcre2_match_context_free(pxd->context); |
127 | 5.56k | SCFree(pxd); |
128 | 5.56k | SCReturnInt(-1); |
129 | 5.56k | } |
130 | | |
131 | | // check pcd->regex has exactly one capture expression |
132 | 75.3k | uint32_t nb; |
133 | 75.3k | if (pcre2_pattern_info(pxd->regex, PCRE2_INFO_CAPTURECOUNT, &nb) < 0) { |
134 | 0 | SCLogError("pcrexform failed getting info about capturecount"); |
135 | 0 | DetectTransformPcrexformFree(de_ctx, pxd); |
136 | 0 | SCReturnInt(-1); |
137 | 0 | } |
138 | 75.3k | if (nb != 1) { |
139 | 1.13k | SCLogError("pcrexform needs exactly one substring capture, found %" PRIu32, nb); |
140 | 1.13k | DetectTransformPcrexformFree(de_ctx, pxd); |
141 | 1.13k | SCReturnInt(-1); |
142 | 1.13k | } |
143 | | |
144 | 74.2k | pxd->id_data = (uint8_t *)SCStrdup(regexstr); |
145 | 74.2k | if (pxd->id_data == NULL) { |
146 | 0 | DetectTransformPcrexformFree(de_ctx, pxd); |
147 | 0 | SCReturnInt(-1); |
148 | 0 | } |
149 | 74.2k | pxd->id_data_len = (uint32_t)strlen(regexstr); |
150 | | |
151 | 74.2k | int r = SCDetectSignatureAddTransform(s, DETECT_TRANSFORM_PCREXFORM, pxd); |
152 | 74.2k | if (r != 0) { |
153 | 466 | DetectTransformPcrexformFree(de_ctx, pxd); |
154 | 466 | } |
155 | | |
156 | 74.2k | SCReturnInt(r); |
157 | 74.2k | } |
158 | | |
159 | | static void DetectTransformPcrexform( |
160 | | DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, void *options) |
161 | 3.85k | { |
162 | 3.85k | const char *input = (const char *)buffer->inspect; |
163 | 3.85k | const uint32_t input_len = buffer->inspect_len; |
164 | 3.85k | DetectTransformPcrexformData *pxd = options; |
165 | | |
166 | 3.85k | pcre2_match_data *match = pcre2_match_data_create_from_pattern(pxd->regex, NULL); |
167 | 3.85k | int ret = pcre2_match(pxd->regex, (PCRE2_SPTR8)input, input_len, 0, 0, match, pxd->context); |
168 | | |
169 | 3.85k | if (ret > 0) { |
170 | 36 | const char *str; |
171 | 36 | PCRE2_SIZE caplen; |
172 | 36 | ret = pcre2_substring_get_bynumber(match, 1, (PCRE2_UCHAR8 **)&str, &caplen); |
173 | | |
174 | 36 | if (ret >= 0) { |
175 | 36 | InspectionBufferCopy(buffer, (uint8_t *)str, (uint32_t)caplen); |
176 | 36 | pcre2_substring_free((PCRE2_UCHAR8 *)str); |
177 | 36 | } |
178 | 36 | } |
179 | 3.85k | pcre2_match_data_free(match); |
180 | 3.85k | } |
181 | | |
182 | | #ifdef UNITTESTS |
183 | | #include "tests/detect-transform-pcrexform.c" |
184 | | #endif |