suricata
detect-engine-proto.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2022 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  * Proto part of the detection engine.
24  *
25  * \todo move this out of the detection plugin structure
26  */
27 
28 #include "suricata-common.h"
29 
30 #include "decode.h"
31 #include "detect.h"
32 
33 #include "app-layer-parser.h"
34 
35 #include "flow-util.h"
36 #include "flow-var.h"
37 
38 #include "detect-engine-siggroup.h"
39 #include "detect-engine-state.h"
40 
41 #include "util-cidr.h"
42 #include "util-byte.h"
43 #include "util-unittest.h"
44 #include "util-unittest-helper.h"
45 #include "util-debug.h"
46 
47 /**
48  * \brief Parses a protocol sent as a string.
49  *
50  * \param dp Pointer to the DetectProto instance which will be updated with the
51  * incoming protocol information.
52  * \param str Pointer to the string containing the protocol name.
53  *
54  * \retval >=0 If proto is detected, -1 otherwise.
55  */
56 int DetectProtoParse(DetectProto *dp, const char *str)
57 {
58  if (strcasecmp(str, "tcp") == 0) {
59  dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
60  SCLogDebug("TCP protocol detected");
61  } else if (strcasecmp(str, "tcp-pkt") == 0) {
62  dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
63  SCLogDebug("TCP protocol detected, packets only");
65  } else if (strcasecmp(str, "tcp-stream") == 0) {
66  dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
67  SCLogDebug("TCP protocol detected, stream only");
69  } else if (strcasecmp(str, "udp") == 0) {
70  dp->proto[IPPROTO_UDP / 8] |= 1 << (IPPROTO_UDP % 8);
71  SCLogDebug("UDP protocol detected");
72  } else if (strcasecmp(str, "icmpv4") == 0) {
73  dp->proto[IPPROTO_ICMP / 8] |= 1 << (IPPROTO_ICMP % 8);
74  SCLogDebug("ICMPv4 protocol detected");
75  } else if (strcasecmp(str, "icmpv6") == 0) {
76  dp->proto[IPPROTO_ICMPV6 / 8] |= 1 << (IPPROTO_ICMPV6 % 8);
77  SCLogDebug("ICMPv6 protocol detected");
78  } else if (strcasecmp(str, "icmp") == 0) {
79  dp->proto[IPPROTO_ICMP / 8] |= 1 << (IPPROTO_ICMP % 8);
80  dp->proto[IPPROTO_ICMPV6 / 8] |= 1 << (IPPROTO_ICMPV6 % 8);
81  SCLogDebug("ICMP protocol detected, sig applies both to ICMPv4 and ICMPv6");
82  } else if (strcasecmp(str, "sctp") == 0) {
83  dp->proto[IPPROTO_SCTP / 8] |= 1 << (IPPROTO_SCTP % 8);
84  SCLogDebug("SCTP protocol detected");
85  } else if (strcasecmp(str,"ipv4") == 0 ||
86  strcasecmp(str,"ip4") == 0 ) {
88  memset(dp->proto, 0xff, sizeof(dp->proto));
89  SCLogDebug("IPv4 protocol detected");
90  } else if (strcasecmp(str,"ipv6") == 0 ||
91  strcasecmp(str,"ip6") == 0 ) {
93  memset(dp->proto, 0xff, sizeof(dp->proto));
94  SCLogDebug("IPv6 protocol detected");
95  } else if (strcasecmp(str,"ip") == 0 ||
96  strcasecmp(str,"pkthdr") == 0) {
97  /* Proto "ip" is treated as an "any" */
98  dp->flags |= DETECT_PROTO_ANY;
99  memset(dp->proto, 0xff, sizeof(dp->proto));
100  SCLogDebug("IP protocol detected");
101  } else {
102  goto error;
103 
104  /** \todo are numeric protocols even valid? */
105 #if 0
106  uint8_t proto_u8; /* Used to avoid sign extension */
107 
108  /* Extract out a 0-256 value with validation checks */
109  if (ByteExtractStringUint8(&proto_u8, 10, 0, str) == -1) {
110  // XXX
111  SCLogDebug("DetectProtoParse: Error in extracting byte string");
112  goto error;
113  }
114  proto = (int)proto_u8;
115 
116  /* Proto 0 is the same as "ip" above */
117  if (proto == IPPROTO_IP) {
118  dp->flags |= DETECT_PROTO_ANY;
119  } else {
120  dp->proto[proto / 8] |= 1<<(proto % 8);
121  }
122 #endif
123  }
124 
125  return 0;
126 error:
127  return -1;
128 }
129 
130 /** \brief see if a DetectProto contains a certain proto
131  * \param dp detect proto to inspect
132  * \param proto protocol (such as IPPROTO_TCP) to look for
133  * \retval 0 protocol not in the set
134  * \retval 1 protocol is in the set */
136 {
137  if (dp->flags & DETECT_PROTO_ANY)
138  return 1;
139 
140  if (dp->proto[proto / 8] & (1<<(proto % 8)))
141  return 1;
142 
143  return 0;
144 }
145 
146 /* TESTS */
147 
148 #ifdef UNITTESTS
149 #include "detect-engine.h"
150 #include "detect-parse.h"
151 #include "detect-engine-mpm.h"
152 /**
153  * \brief this function is used to initialize the detection engine context and
154  * setup the signature with passed values.
155  */
156 static int DetectProtoInitTest(DetectEngineCtx **de_ctx, Signature **sig,
157  DetectProto *dp, const char *str)
158 {
159  char fullstr[1024];
160  int result = 0;
161 
162  *de_ctx = NULL;
163  *sig = NULL;
164 
165  if (snprintf(fullstr, 1024, "alert %s any any -> any any (msg:\"DetectProto"
166  " test\"; sid:1;)", str) >= 1024)
167  {
168  goto end;
169  }
170 
172  if (*de_ctx == NULL) {
173  goto end;
174  }
175 
176  (*de_ctx)->flags |= DE_QUIET;
177 
178  (*de_ctx)->sig_list = SigInit(*de_ctx, fullstr);
179  if ((*de_ctx)->sig_list == NULL) {
180  goto end;
181  }
182 
183  *sig = (*de_ctx)->sig_list;
184 
185  if (DetectProtoParse(dp, str) < 0)
186  goto end;
187 
188  result = 1;
189 
190 end:
191  return result;
192 }
193 
194 /**
195  * \test ProtoTestParse01 is a test to make sure that we parse the
196  * protocol correctly, when given valid proto option.
197  */
198 static int ProtoTestParse01 (void)
199 {
200  DetectProto dp;
201  memset(&dp,0,sizeof(DetectProto));
202 
203  int r = DetectProtoParse(&dp, "6");
204 
205  FAIL_IF_NOT(r < 0);
206 
207  PASS;
208 }
209 /**
210  * \test ProtoTestParse02 is a test to make sure that we parse the
211  * protocol correctly, when given "tcp" as proto option.
212  */
213 static int ProtoTestParse02 (void)
214 {
215  DetectProto dp;
216  memset(&dp,0,sizeof(DetectProto));
217 
218  int r = DetectProtoParse(&dp, "tcp");
219 
220  FAIL_IF_NOT(r >= 0);
221  FAIL_IF_NOT(dp.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8)));
222 
223  PASS;
224 }
225 /**
226  * \test ProtoTestParse03 is a test to make sure that we parse the
227  * protocol correctly, when given "ip" as proto option.
228  */
229 static int ProtoTestParse03 (void)
230 {
231  DetectProto dp;
232  memset(&dp,0,sizeof(DetectProto));
233 
234  int r = DetectProtoParse(&dp, "ip");
235 
236  FAIL_IF_NOT(r >= 0);
238 
239  PASS;
240 }
241 
242 /**
243  * \test ProtoTestParse04 is a test to make sure that we do not parse the
244  * protocol, when given an invalid proto option.
245  */
246 static int ProtoTestParse04 (void)
247 {
248  DetectProto dp;
249  memset(&dp,0,sizeof(DetectProto));
250 
251  /* Check for a bad number */
252  int r = DetectProtoParse(&dp, "4242");
253 
254  FAIL_IF_NOT(r < 0);
255 
256  PASS;
257 }
258 
259 /**
260  * \test ProtoTestParse05 is a test to make sure that we do not parse the
261  * protocol, when given an invalid proto option.
262  */
263 static int ProtoTestParse05 (void)
264 {
265  DetectProto dp;
266  memset(&dp,0,sizeof(DetectProto));
267 
268  /* Check for a bad string */
269  int r = DetectProtoParse(&dp, "tcp/udp");
270 
271  FAIL_IF_NOT(r < 0);
272 
273  PASS;
274 }
275 
276 /**
277  * \test make sure that we properly parse tcp-pkt
278  */
279 static int ProtoTestParse06 (void)
280 {
281  DetectProto dp;
282  memset(&dp,0,sizeof(DetectProto));
283 
284  /* Check for a bad string */
285  int r = DetectProtoParse(&dp, "tcp-pkt");
286 
287  FAIL_IF(r < 0);
289 
290  PASS;
291 }
292 
293 /**
294  * \test make sure that we properly parse tcp-stream
295  */
296 static int ProtoTestParse07 (void)
297 {
298  DetectProto dp;
299  memset(&dp,0,sizeof(DetectProto));
300 
301  /* Check for a bad string */
302  int r = DetectProtoParse(&dp, "tcp-stream");
303 
304  FAIL_IF(r < 0);
306 
307  PASS;
308 }
309 
310 /**
311  * \test DetectIPProtoTestSetup01 is a test for a protocol setting up in
312  * signature.
313  */
314 static int DetectProtoTestSetup01(void)
315 {
316  DetectProto dp;
317  Signature *sig = NULL;
318  DetectEngineCtx *de_ctx = NULL;
319  int i;
320 
321  memset(&dp, 0, sizeof(dp));
322 
323  FAIL_IF_NOT(DetectProtoInitTest(&de_ctx, &sig, &dp, "tcp"));
324 
325  /* The signature proto should be TCP */
326  FAIL_IF_NOT(sig->proto.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8)));
327 
328  for (i = 2; i < 256 / 8; i++) {
329  FAIL_IF(sig->proto.proto[i] != 0);
330  }
331 
333 
334  PASS;
335 }
336 
337 /**
338  * \test DetectrotoTestSetup02 is a test for a icmpv4 and icmpv6
339  * protocol setting up in signature.
340  */
341 static int DetectProtoTestSetup02(void)
342 {
343  DetectProto dp;
344  Signature *sig_icmpv4 = NULL;
345  Signature *sig_icmpv6 = NULL;
346  Signature *sig_icmp = NULL;
347  DetectEngineCtx *de_ctx = NULL;
348 
349  memset(&dp, 0, sizeof(dp));
350 
351  FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv4, &dp, "icmpv4") == 0);
352  FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv6, &dp, "icmpv6") == 0);
353  FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmp, &dp, "icmp") == 0);
354 
355  FAIL_IF_NOT(sig_icmpv4->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8)));
356  FAIL_IF_NOT(sig_icmpv6->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8)));
357 
358  FAIL_IF_NOT(sig_icmp->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8)));
359  FAIL_IF_NOT(sig_icmp->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8)));
360 
362 
363  PASS;
364 }
365 
366 /**
367  * \test signature parsing with tcp-pkt and tcp-stream
368  */
369 
370 static int DetectProtoTestSig01(void)
371 {
374 
375  de_ctx->flags |= DE_QUIET;
376 
378  de_ctx, "alert tcp-pkt any any -> any any (msg:\"tcp-pkt\"; content:\"blah\"; sid:1;)");
379  FAIL_IF_NULL(s);
380 
382  "alert tcp-stream any any -> any any (msg:\"tcp-stream\"; content:\"blah\"; sid:2;)");
383  FAIL_IF_NULL(s);
384 
386 
387  PASS;
388 }
389 #endif /* UNITTESTS */
390 
391 /**
392  * \brief this function registers unit tests for DetectProto
393  */
395 {
396 #ifdef UNITTESTS
397  UtRegisterTest("ProtoTestParse01", ProtoTestParse01);
398  UtRegisterTest("ProtoTestParse02", ProtoTestParse02);
399  UtRegisterTest("ProtoTestParse03", ProtoTestParse03);
400  UtRegisterTest("ProtoTestParse04", ProtoTestParse04);
401  UtRegisterTest("ProtoTestParse05", ProtoTestParse05);
402  UtRegisterTest("ProtoTestParse06", ProtoTestParse06);
403  UtRegisterTest("ProtoTestParse07", ProtoTestParse07);
404 
405  UtRegisterTest("DetectProtoTestSetup01", DetectProtoTestSetup01);
406  UtRegisterTest("DetectProtoTestSetup02", DetectProtoTestSetup02);
407 
408  UtRegisterTest("DetectProtoTestSig01", DetectProtoTestSig01);
409 #endif /* UNITTESTS */
410 }
411 
util-byte.h
detect-engine.h
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
flow-util.h
DETECT_PROTO_IPV6
#define DETECT_PROTO_IPV6
Definition: detect-engine-proto.h:34
detect-engine-siggroup.h
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:298
SigInit
Signature * SigInit(DetectEngineCtx *, const char *)
Parses a signature and adds it to the Detection Engine Context.
Definition: detect-parse.c:2115
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:811
DETECT_PROTO_ANY
#define DETECT_PROTO_ANY
Definition: detect-engine-proto.h:27
DetectEngineCtxFree
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Definition: detect-engine.c:2433
DE_QUIET
#define DE_QUIET
Definition: detect.h:295
proto
uint8_t proto
Definition: decode-template.h:0
util-unittest.h
util-unittest-helper.h
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
util-cidr.h
decode.h
util-debug.h
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:17
DetectProtoParse
int DetectProtoParse(DetectProto *dp, const char *str)
Parses a protocol sent as a string.
Definition: detect-engine-proto.c:56
detect-engine-mpm.h
detect.h
app-layer-parser.h
DetectProto_::proto
uint8_t proto[256/8]
Definition: detect-engine-proto.h:37
detect-engine-state.h
Data structures and function prototypes for keeping state for the detection engine.
DetectProto_::flags
uint8_t flags
Definition: detect-engine-proto.h:38
ByteExtractStringUint8
int ByteExtractStringUint8(uint8_t *res, int base, size_t len, const char *str)
Definition: util-byte.c:285
DetectEngineAppendSig
Signature * DetectEngineAppendSig(DetectEngineCtx *de_ctx, const char *sigstr)
Parse and append a Signature into the Detection Engine Context signature list.
Definition: detect-parse.c:2420
DETECT_PROTO_IPV4
#define DETECT_PROTO_IPV4
Definition: detect-engine-proto.h:33
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
Signature_::proto
DetectProto proto
Definition: detect.h:566
suricata-common.h
DETECT_PROTO_ONLY_STREAM
#define DETECT_PROTO_ONLY_STREAM
Definition: detect-engine-proto.h:31
DetectProto_
Definition: detect-engine-proto.h:36
str
#define str(s)
Definition: suricata-common.h:272
detect-parse.h
Signature_
Signature container.
Definition: detect.h:548
DETECT_PROTO_ONLY_PKT
#define DETECT_PROTO_ONLY_PKT
Definition: detect-engine-proto.h:29
DetectEngineCtxInit
DetectEngineCtx * DetectEngineCtxInit(void)
Definition: detect-engine.c:2394
DetectEngineCtx_::flags
uint8_t flags
Definition: detect.h:812
flow-var.h
DetectProtoContainsProto
int DetectProtoContainsProto(const DetectProto *dp, int proto)
see if a DetectProto contains a certain proto
Definition: detect-engine-proto.c:135
DetectProtoTests
void DetectProtoTests(void)
this function registers unit tests for DetectProto
Definition: detect-engine-proto.c:394