suricata
detect-mark.c
Go to the documentation of this file.
1 /* Copyright (C) 2011-2021 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 
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;
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  */
78 static void * DetectMarkParse (const char *rawstr)
79 {
80  int res = 0;
81  size_t pcre2_len;
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  pcre2_match_data *match = NULL;
90  int ret = DetectParsePcreExec(&parse_regex, &match, rawstr, 0, 0);
91  if (ret < 1) {
92  SCLogError("pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr);
93  pcre2_match_data_free(match);
94  return NULL;
95  }
96 
97  res = pcre2_substring_get_bynumber(match, 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
98  if (res < 0) {
99  SCLogError("pcre2_substring_get_bynumber failed");
100  goto error;
101  }
102 
103  ptr = (char *)str_ptr;
104 
105  if (ptr == NULL)
106  goto error;
107 
108  errno = 0;
109  mark = strtoul(ptr, &endptr, 0);
110  if (errno == ERANGE) {
111  SCLogError("Numeric value out of range");
112  pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
113  goto error;
114  } /* If there is no numeric value in the given string then strtoull(), makes
115  endptr equals to ptr and return 0 as result */
116  else if (endptr == ptr && mark == 0) {
117  SCLogError("No numeric value");
118  pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
119  goto error;
120  } else if (endptr == ptr) {
121  SCLogError("Invalid numeric value");
122  pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
123  goto error;
124  }
125 
126  res = pcre2_substring_get_bynumber(match, 2, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
127  if (res < 0) {
128  SCLogError("pcre2_substring_get_bynumber failed");
129  goto error;
130  }
131 
132  pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
133  ptr = (char *)str_ptr;
134 
135  if (ptr == NULL) {
136  data = SCMalloc(sizeof(DetectMarkData));
137  if (unlikely(data == NULL)) {
138  goto error;
139  }
140  data->mark = mark;
141  data->mask = 0xffff;
142  pcre2_match_data_free(match);
143  return data;
144  }
145 
146  errno = 0;
147  mask = strtoul(ptr, &endptr, 0);
148  if (errno == ERANGE) {
149  SCLogError("Numeric value out of range");
150  pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
151  goto error;
152  } /* If there is no numeric value in the given string then strtoull(), makes
153  endptr equals to ptr and return 0 as result */
154  else if (endptr == ptr && mask == 0) {
155  SCLogError("No numeric value");
156  pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
157  goto error;
158  }
159  else if (endptr == ptr) {
160  SCLogError("Invalid numeric value");
161  pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
162  goto error;
163  }
164 
165  SCLogDebug("Rule will set mark 0x%x with mask 0x%x", mark, mask);
166  pcre2_substring_free((PCRE2_UCHAR8 *)ptr);
167 
168  data = SCMalloc(sizeof(DetectMarkData));
169  if (unlikely(data == NULL)) {
170  goto error;
171  }
172  data->mark = mark;
173  data->mask = mask;
174  pcre2_match_data_free(match);
175  return data;
176 
177 error:
178  if (match) {
179  pcre2_match_data_free(match);
180  }
181  return NULL;
182 }
183 
184 #endif /* NFQ */
185 
186 /**
187  * \internal
188  * \brief this function is used to add the parsed mark into the current signature
189  *
190  * \param de_ctx pointer to the Detection Engine Context
191  * \param s pointer to the Current Signature
192  * \param rawstr pointer to the user provided mark options
193  *
194  * \retval 0 on Success
195  * \retval -1 on Failure
196  */
197 static int DetectMarkSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
198 {
199 #ifndef NFQ
200  return 0;
201 #else
202  DetectMarkData *data = DetectMarkParse(rawstr);
203  if (data == NULL) {
204  return -1;
205  }
206  SigMatch *sm = SigMatchAlloc();
207  if (sm == NULL) {
208  DetectMarkDataFree(de_ctx, data);
209  return -1;
210  }
211 
212  sm->type = DETECT_MARK;
213  sm->ctx = (SigMatchCtx *)data;
214 
215  /* Append it to the list of post match, so the mark is set if the
216  * full signature matches. */
218  return 0;
219 #endif
220 }
221 
223 {
224  DetectMarkData *data = (DetectMarkData *)ptr;
225  SCFree(data);
226 }
227 
228 
229 static int DetectMarkPacket(DetectEngineThreadCtx *det_ctx, Packet *p,
230  const Signature *s, const SigMatchCtx *ctx)
231 {
232 #ifdef NFQ
233  const DetectMarkData *nf_data = (const DetectMarkData *)ctx;
234  if (nf_data->mask) {
235  if (!(IS_TUNNEL_PKT(p))) {
236  /* coverity[missing_lock] */
237  p->nfq_v.mark = (nf_data->mark & nf_data->mask)
238  | (p->nfq_v.mark & ~(nf_data->mask));
239  p->flags |= PKT_MARK_MODIFIED;
240  } else {
241  /* real tunnels may have multiple flows inside them, so marking
242  * might 'mark' too much. Rebuilt packets from IP fragments
243  * are fine. */
244  if (p->flags & PKT_REBUILT_FRAGMENT) {
245  Packet *tp = p->root ? p->root : p;
247  tp->nfq_v.mark = (nf_data->mark & nf_data->mask)
248  | (tp->nfq_v.mark & ~(nf_data->mask));
249  tp->flags |= PKT_MARK_MODIFIED;
251  }
252  }
253  }
254 #endif
255  return 1;
256 }
257 
258 /*
259  * ONLY TESTS BELOW THIS COMMENT
260  */
261 
262 #if defined UNITTESTS && defined NFQ
263 /**
264  * \test MarkTestParse01 is a test for a valid mark value
265  *
266  */
267 static int MarkTestParse01 (void)
268 {
269  DetectMarkData *data;
270 
271  data = DetectMarkParse("1/1");
272 
273  FAIL_IF_NULL(data);
274 
275  DetectMarkDataFree(NULL, data);
276  PASS;
277 }
278 
279 /**
280  * \test MarkTestParse02 is a test for an invalid mark value
281  *
282  */
283 static int MarkTestParse02 (void)
284 {
285  DetectMarkData *data;
286 
287  data = DetectMarkParse("4");
288 
289  FAIL_IF_NOT_NULL(data);
290 
291  DetectMarkDataFree(NULL, data);
292  PASS;
293 }
294 
295 /**
296  * \test MarkTestParse03 is a test for a valid mark value
297  *
298  */
299 static int MarkTestParse03 (void)
300 {
301  DetectMarkData *data;
302 
303  data = DetectMarkParse("0x10/0xff");
304 
305  FAIL_IF_NULL(data);
306 
307  DetectMarkDataFree(NULL, data);
308  PASS;
309 }
310 
311 /**
312  * \test MarkTestParse04 is a test for a invalid mark value
313  *
314  */
315 static int MarkTestParse04 (void)
316 {
317  DetectMarkData *data;
318 
319  data = DetectMarkParse("0x1g/0xff");
320 
321  FAIL_IF_NOT_NULL(data);
322 
323  DetectMarkDataFree(NULL, data);
324  PASS;
325 }
326 
327 /**
328  * \brief this function registers unit tests for Mark
329  */
330 static void MarkRegisterTests(void)
331 {
332  UtRegisterTest("MarkTestParse01", MarkTestParse01);
333  UtRegisterTest("MarkTestParse02", MarkTestParse02);
334  UtRegisterTest("MarkTestParse03", MarkTestParse03);
335  UtRegisterTest("MarkTestParse04", MarkTestParse04);
336 }
337 #endif /* UNITTESTS */
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
SigMatchAppendSMToList
void SigMatchAppendSMToList(Signature *s, SigMatch *new, const int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:437
SigTableElmt_::Free
void(* Free)(DetectEngineCtx *, void *)
Definition: detect.h:1275
DetectParseRegex
Definition: detect-parse.h:62
SigTableElmt_::name
const char * name
Definition: detect.h:1285
Packet_::persistent
struct Packet_::@40 persistent
detect-mark.h
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
PARSE_REGEX
#define PARSE_REGEX
Definition: detect-mark.c:40
Packet_::flags
uint32_t flags
Definition: decode.h:467
DETECT_MARK
@ DETECT_MARK
Definition: detect-engine-register.h:106
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:827
DetectMarkData_
Definition: detect-mark.h:42
SCSpinLock
#define SCSpinLock
Definition: threads-debug.h:235
DetectParsePcreExec
int DetectParsePcreExec(DetectParseRegex *parse_regex, pcre2_match_data **match, const char *str, int start_offset, int options)
Definition: detect-parse.c:2629
NFQPacketVars_::mark
uint32_t mark
Definition: source-nfq.h:45
SigTableElmt_::Setup
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1270
util-unittest.h
DETECT_SM_LIST_POSTMATCH
@ DETECT_SM_LIST_POSTMATCH
Definition: detect.h:118
decode.h
FAIL_IF_NOT_NULL
#define FAIL_IF_NOT_NULL(expr)
Fail a test if expression evaluates to non-NULL.
Definition: util-unittest.h:96
util-debug.h
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:17
DetectEngineThreadCtx_
Definition: detect.h:1075
Packet_::tunnel_lock
SCSpinlock tunnel_lock
Definition: decode.h:652
DetectSetupParseRegexes
void DetectSetupParseRegexes(const char *parse_str, DetectParseRegex *detect_parse)
Definition: detect-parse.c:2753
detect.h
SCSpinUnlock
#define SCSpinUnlock
Definition: threads-debug.h:237
SigMatch_::ctx
SigMatchCtx * ctx
Definition: detect.h:344
Packet_
Definition: decode.h:430
SigTableElmt_::Match
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition: detect.h:1253
IS_TUNNEL_PKT
#define IS_TUNNEL_PKT(p)
Definition: decode.h:794
DetectMarkRegister
void DetectMarkRegister(void)
Registration function for nfq_set_mark: keyword.
Definition: detect-mark.c:56
SigMatchAlloc
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:322
decode-events.h
SigMatchCtx_
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition: detect.h:336
Packet_::nfq_v
NFQPacketVars nfq_v
Definition: decode.h:483
DetectMarkDataFree
void DetectMarkDataFree(DetectEngineCtx *, void *ptr)
Definition: detect-mark.c:222
suricata-common.h
SigMatch_::type
uint16_t type
Definition: detect.h:342
sigmatch_table
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect-parse.c:129
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
Packet_::root
struct Packet_ * root
Definition: decode.h:622
PKT_REBUILT_FRAGMENT
#define PKT_REBUILT_FRAGMENT
Definition: decode.h:1036
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:261
SCFree
#define SCFree(p)
Definition: util-mem.h:61
detect-parse.h
Signature_
Signature container.
Definition: detect.h:582
SigMatch_
a single match condition for a signature
Definition: detect.h:341
suricata.h
PKT_MARK_MODIFIED
#define PKT_MARK_MODIFIED
Definition: decode.h:1007
DetectMarkData_::mark
uint32_t mark
Definition: detect-mark.h:43
DetectMarkData_::mask
uint32_t mask
Definition: detect-mark.h:44
flow-var.h
SigTableElmt_::RegisterTests
void(* RegisterTests)(void)
Definition: detect.h:1277