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 */