1 /* Copyright (C) 2011-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 Eric Leblond <eric@regit.org>
22 *
23 * Implements the mark keyword. Based on detect-gid
24 * by Breno Silva <breno.silva@gmail.com>
25 */
26
27 #include "suricata-common.h"
28 #include "suricata.h"
29 #include "decode.h"
30 #include "detect.h"
31 #include "flow-var.h"
32 #include "decode-events.h"
33
34 #include "detect-mark.h"
35 #include "detect-parse.h"
36
37 #include "util-unittest.h"
38 #include "util-debug.h"
39
40 #define PARSE_REGEX "([0x]*[0-9a-f]+)/([0x]*[0-9a-f]+)"
41
42 static DetectParseRegex parse_regex;
43
44 static int DetectMarkSetup (DetectEngineCtx *, Signature *, const char *);
45 static int DetectMarkPacket(DetectEngineThreadCtx *det_ctx, Packet *p,
46 const Signature *s, const SigMatchCtx *ctx);
47 void DetectMarkDataFree(DetectEngineCtx *, void *ptr);
48 #if defined UNITTESTS && defined NFQ
49 static void MarkRegisterTests(void);
50 #endif
51
52 /**
53 * \brief Registration function for nfq_set_mark: keyword
54 */
55
DetectMarkRegister(void)56 void DetectMarkRegister (void)
57 {
58 sigmatch_table[DETECT_MARK].name = "nfq_set_mark";
59 sigmatch_table[DETECT_MARK].Match = DetectMarkPacket;
60 sigmatch_table[DETECT_MARK].Setup = DetectMarkSetup;
61 sigmatch_table[DETECT_MARK].Free = DetectMarkDataFree;
62 #if defined UNITTESTS && defined NFQ
63 sigmatch_table[DETECT_MARK].RegisterTests = MarkRegisterTests;
64 #endif
65 DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
66 }
67
68 #ifdef NFQ
69 /**
70 * \internal
71 * \brief This function is used to parse mark options passed via mark: keyword
72 *
73 * \param rawstr Pointer to the user provided mark options
74 *
75 * \retval 0 on success
76 * \retval < 0 on failure
77 */
DetectMarkParse(const char * rawstr)78 static void * DetectMarkParse (const char *rawstr)
79 {
80 int ret = 0, res = 0;
81 int ov[MAX_SUBSTRINGS];
82 const char *str_ptr = NULL;
83 char *ptr = NULL;
84 char *endptr = NULL;
85 uint32_t mark;
86 uint32_t mask;
87 DetectMarkData *data;
88
89 ret = DetectParsePcreExec(&parse_regex, rawstr, 0, 0, ov, MAX_SUBSTRINGS);
90 if (ret < 1) {
91 SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr);
92 return NULL;
93 }
94
95 res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, &str_ptr);
96 if (res < 0) {
97 SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
98 return NULL;
99 }
100
101 ptr = (char *)str_ptr;
102
103 if (ptr == NULL)
104 return NULL;
105
106 errno = 0;
107 mark = strtoul(ptr, &endptr, 0);
108 if (errno == ERANGE) {
109 SCLogError(SC_ERR_NUMERIC_VALUE_ERANGE, "Numeric value out of range");
110 SCFree(ptr);
111 return NULL;
112 } /* If there is no numeric value in the given string then strtoull(), makes
113 endptr equals to ptr and return 0 as result */
114 else if (endptr == ptr && mark == 0) {
115 SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "No numeric value");
116 SCFree(ptr);
117 return NULL;
118 } else if (endptr == ptr) {
119 SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "Invalid numeric value");
120 SCFree(ptr);
121 return NULL;
122 }
123
124 res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, &str_ptr);
125 if (res < 0) {
126 SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
127 return NULL;
128 }
129
130 SCFree(ptr);
131 ptr = (char *)str_ptr;
132
133 if (ptr == NULL) {
134 data = SCMalloc(sizeof(DetectMarkData));
135 if (unlikely(data == NULL)) {
136 return NULL;
137 }
138 data->mark = mark;
139 data->mask = 0xffff;
140 return data;
141 }
142
143 errno = 0;
144 mask = strtoul(ptr, &endptr, 0);
145 if (errno == ERANGE) {
146 SCLogError(SC_ERR_NUMERIC_VALUE_ERANGE, "Numeric value out of range");
147 SCFree(ptr);
148 return NULL;
149 } /* If there is no numeric value in the given string then strtoull(), makes
150 endptr equals to ptr and return 0 as result */
151 else if (endptr == ptr && mask == 0) {
152 SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "No numeric value");
153 SCFree(ptr);
154 return NULL;
155 }
156 else if (endptr == ptr) {
157 SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "Invalid numeric value");
158 SCFree(ptr);
159 return NULL;
160 }
161
162 SCLogDebug("Rule will set mark 0x%x with mask 0x%x", mark, mask);
163 SCFree(ptr);
164
165 data = SCMalloc(sizeof(DetectMarkData));
166 if (unlikely(data == NULL)) {
167 return NULL;
168 }
169 data->mark = mark;
170 data->mask = mask;
171 return data;
172 }
173
174 #endif /* NFQ */
175
176 /**
177 * \internal
178 * \brief this function is used to add the parsed mark into the current signature
179 *
180 * \param de_ctx pointer to the Detection Engine Context
181 * \param s pointer to the Current Signature
182 * \param rawstr pointer to the user provided mark options
183 *
184 * \retval 0 on Success
185 * \retval -1 on Failure
186 */
DetectMarkSetup(DetectEngineCtx * de_ctx,Signature * s,const char * rawstr)187 static int DetectMarkSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
188 {
189 #ifndef NFQ
190 return 0;
191 #else
192 DetectMarkData *data = DetectMarkParse(rawstr);
193 if (data == NULL) {
194 return -1;
195 }
196 SigMatch *sm = SigMatchAlloc();
197 if (sm == NULL) {
198 DetectMarkDataFree(de_ctx, data);
199 return -1;
200 }
201
202 sm->type = DETECT_MARK;
203 sm->ctx = (SigMatchCtx *)data;
204
205 /* Append it to the list of post match, so the mark is set if the
206 * full signature matches. */
207 SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_POSTMATCH);
208 return 0;
209 #endif
210 }
211
DetectMarkDataFree(DetectEngineCtx * de_ctx,void * ptr)212 void DetectMarkDataFree(DetectEngineCtx *de_ctx, void *ptr)
213 {
214 DetectMarkData *data = (DetectMarkData *)ptr;
215 SCFree(data);
216 }
217
218
DetectMarkPacket(DetectEngineThreadCtx * det_ctx,Packet * p,const Signature * s,const SigMatchCtx * ctx)219 static int DetectMarkPacket(DetectEngineThreadCtx *det_ctx, Packet *p,
220 const Signature *s, const SigMatchCtx *ctx)
221 {
222 #ifdef NFQ
223 const DetectMarkData *nf_data = (const DetectMarkData *)ctx;
224 if (nf_data->mask) {
225 if (!(IS_TUNNEL_PKT(p))) {
226 /* coverity[missing_lock] */
227 p->nfq_v.mark = (nf_data->mark & nf_data->mask)
228 | (p->nfq_v.mark & ~(nf_data->mask));
229 p->flags |= PKT_MARK_MODIFIED;
230 } else {
231 /* real tunnels may have multiple flows inside them, so marking
232 * might 'mark' too much. Rebuilt packets from IP fragments
233 * are fine. */
234 if (p->flags & PKT_REBUILT_FRAGMENT) {
235 Packet *tp = p->root ? p->root : p;
236 SCMutexLock(&tp->tunnel_mutex);
237 tp->nfq_v.mark = (nf_data->mark & nf_data->mask)
238 | (tp->nfq_v.mark & ~(nf_data->mask));
239 tp->flags |= PKT_MARK_MODIFIED;
240 SCMutexUnlock(&tp->tunnel_mutex);
241 }
242 }
243 }
244 #endif
245 return 1;
246 }
247
248 /*
249 * ONLY TESTS BELOW THIS COMMENT
250 */
251
252 #if defined UNITTESTS && defined NFQ
253 /**
254 * \test MarkTestParse01 is a test for a valid mark value
255 *
256 * \retval 1 on succces
257 * \retval 0 on failure
258 */
MarkTestParse01(void)259 static int MarkTestParse01 (void)
260 {
261 DetectMarkData *data;
262
263 data = DetectMarkParse("1/1");
264
265 if (data == NULL) {
266 return 0;
267 }
268
269 DetectMarkDataFree(NULL, data);
270 return 1;
271 }
272
273 /**
274 * \test MarkTestParse02 is a test for an invalid mark value
275 *
276 * \retval 1 on succces
277 * \retval 0 on failure
278 */
MarkTestParse02(void)279 static int MarkTestParse02 (void)
280 {
281 DetectMarkData *data;
282
283 data = DetectMarkParse("4");
284
285 if (data == NULL) {
286 return 1;
287 }
288
289 DetectMarkDataFree(NULL, data);
290 return 0;
291 }
292
293 /**
294 * \test MarkTestParse03 is a test for a valid mark value
295 *
296 * \retval 1 on succces
297 * \retval 0 on failure
298 */
MarkTestParse03(void)299 static int MarkTestParse03 (void)
300 {
301 DetectMarkData *data;
302
303 data = DetectMarkParse("0x10/0xff");
304
305 if (data == NULL) {
306 return 0;
307 }
308
309 DetectMarkDataFree(NULL, data);
310 return 1;
311 }
312
313 /**
314 * \test MarkTestParse04 is a test for a invalid mark value
315 *
316 * \retval 1 on succces
317 * \retval 0 on failure
318 */
MarkTestParse04(void)319 static int MarkTestParse04 (void)
320 {
321 DetectMarkData *data;
322
323 data = DetectMarkParse("0x1g/0xff");
324
325 if (data == NULL) {
326 return 1;
327 }
328
329 DetectMarkDataFree(NULL, data);
330 return 0;
331 }
332
333 /**
334 * \brief this function registers unit tests for Mark
335 */
MarkRegisterTests(void)336 static void MarkRegisterTests(void)
337 {
338 UtRegisterTest("MarkTestParse01", MarkTestParse01);
339 UtRegisterTest("MarkTestParse02", MarkTestParse02);
340 UtRegisterTest("MarkTestParse03", MarkTestParse03);
341 UtRegisterTest("MarkTestParse04", MarkTestParse04);
342 }
343 #endif /* UNITTESTS */