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-helper.h"
44 #include "util-debug.h"
45 
46 struct {
47  const char *name;
48  uint8_t proto;
49  uint8_t proto2;
50  uint8_t flags;
51 } proto_table[] = {
52  // clang-format off
53  { "tcp", IPPROTO_TCP, 0, 0, },
54  { "tcp-pkt", IPPROTO_TCP, 0, DETECT_PROTO_ONLY_PKT, },
55  { "tcp-stream", IPPROTO_TCP, 0, DETECT_PROTO_ONLY_STREAM, },
56  { "udp", IPPROTO_UDP, 0, 0, },
57  { "icmpv4", IPPROTO_ICMP, 0, 0, },
58  { "icmpv6", IPPROTO_ICMPV6, 0, 0, },
59  { "icmp", IPPROTO_ICMP, IPPROTO_ICMPV6, 0, },
60  { "igmp", IPPROTO_IGMP, 0, 0, },
61  { "sctp", IPPROTO_SCTP, 0, 0, },
62  { "ipv4", 0, 0, DETECT_PROTO_IPV4 | DETECT_PROTO_ANY, },
63  { "ip4", 0, 0, DETECT_PROTO_IPV4 | DETECT_PROTO_ANY, },
64  { "ipv6", 0, 0, DETECT_PROTO_IPV6 | DETECT_PROTO_ANY, },
65  { "ip6", 0, 0, DETECT_PROTO_IPV6 | DETECT_PROTO_ANY, },
66  { "ip", 0, 0, DETECT_PROTO_ANY, },
67  { "pkthdr", 0, 0, DETECT_PROTO_L2_ANY, },
68  { "ether", 0, 0, DETECT_PROTO_ETHERNET, },
69  { "arp", 0, 0, DETECT_PROTO_ARP | DETECT_PROTO_ETHERNET, },
70  // clang-format on
71 };
72 
74 {
75  for (size_t i = 0; i < ARRAY_SIZE(proto_table); i++) {
76  printf("%s\n", proto_table[i].name);
77  }
78 }
79 
80 /**
81  * \brief Parses a protocol sent as a string.
82  *
83  * \param dp Pointer to the DetectProto instance which will be updated with the
84  * incoming protocol information.
85  * \param str Pointer to the string containing the protocol name.
86  *
87  * \retval >=0 If proto is detected, -1 otherwise.
88  */
89 int DetectProtoParse(DetectProto *dp, const char *str)
90 {
91  int found = -1;
92  for (size_t i = 0; i < ARRAY_SIZE(proto_table); i++) {
93  if (strcasecmp(str, proto_table[i].name) == 0) {
94  if (proto_table[i].proto != 0)
95  dp->proto[proto_table[i].proto / 8] |= 1 << (proto_table[i].proto % 8);
96  if (proto_table[i].proto2 != 0)
97  dp->proto[proto_table[i].proto2 / 8] |= 1 << (proto_table[i].proto2 % 8);
98  dp->flags |= proto_table[i].flags;
100  memset(dp->proto, 0xff, sizeof(dp->proto));
101  found = 0;
102  SCLogDebug("protocol %s: flags %02x", str, dp->flags);
103  break;
104  }
105  }
106  return found;
107 }
108 
109 /** \brief see if a DetectProto contains a certain proto
110  * \param dp detect proto to inspect
111  * \param proto protocol (such as IPPROTO_TCP) to look for
112  * \retval 0 protocol not in the set
113  * \retval 1 protocol is in the set */
115 {
116  if (dp == NULL || dp->flags & (DETECT_PROTO_ANY | DETECT_PROTO_L2_ANY))
117  return 1;
118 
119  if (dp->proto[proto / 8] & (1<<(proto % 8)))
120  return 1;
121 
122  return 0;
123 }
124 
125 /** \brief see if a DetectProto explicitly a certain proto
126  * Explicit means the protocol was explicitly set, so "any"
127  * doesn't qualify.
128  * \param dp detect proto to inspect
129  * \param proto protocol (such as IPPROTO_TCP) to look for
130  * \retval false protocol not in the set
131  * \retval true protocol is in the set */
132 bool DetectProtoHasExplicitProto(const DetectProto *dp, const uint8_t proto)
133 {
134  if (dp == NULL || dp->flags & (DETECT_PROTO_ANY | DETECT_PROTO_L2_ANY))
135  return false;
136 
137  return ((dp->proto[proto / 8] & (1 << (proto % 8))));
138 }
139 
140 /* return true if protocols enabled are only TCP and/or UDP */
141 static int DetectProtoIsOnlyTCPUDP(const DetectProto *dp)
142 {
143  uint8_t protos[256 / 8];
144  memset(protos, 0x00, sizeof(protos));
145  protos[IPPROTO_TCP / 8] |= (1 << (IPPROTO_TCP % 8));
146  protos[IPPROTO_UDP / 8] |= (1 << (IPPROTO_UDP % 8));
147 
148  int cnt = 0;
149  for (size_t i = 0; i < sizeof(protos); i++) {
150  if ((dp->proto[i] & protos[i]) != 0)
151  cnt++;
152  }
153  return cnt != 0;
154 }
155 
157 {
158  BUG_ON(s->proto);
159  /* IP-only sigs are not per SGH, so need full proto */
161  goto full;
162  /* Frames like the dns.pdu are registered for UDP and TCP, and share a MPM. So
163  * a UDP rule can become a match candidate for a TCP sgh, meaning we need to
164  * evaluate the rule's proto. */
165  if ((s->init_data->init_flags & SIG_FLAG_INIT_FRAME) != 0 &&
167  goto full;
168 
169  /* for now, we use the full protocol logic for DETECT_PROTO_IPV4/DETECT_PROTO_IPV6,
170  * but we should address that as well. */
172  SCLogDebug("sid %u has IPV4 or IPV6 flag set, so need full protocol", s->id);
173  goto full;
174  }
175 
176  /* no need to set up Signature::proto if sig needs any protocol,
177  * or only TCP and/or UDP, as for those the SGH is per TCP/UDP */
178  if ((s->init_data->proto.flags & DETECT_PROTO_ANY) ||
179  DetectProtoIsOnlyTCPUDP(&s->init_data->proto)) {
180  s->proto = NULL;
181  return 0;
182  }
183 
184 full:
185  s->proto = SCCalloc(1, sizeof(*s->proto));
186  if (s->proto == NULL)
187  return -1;
188 
189  memcpy(s->proto, &s->init_data->proto, sizeof(*s->proto));
190  return 0;
191 }
192 
193 /* TESTS */
194 
195 #ifdef UNITTESTS
196 #include "detect-engine.h"
197 #include "detect-parse.h"
198 #include "detect-engine-mpm.h"
199 
200 /**
201  * \brief this function is used to initialize the detection engine context and
202  * setup the signature with passed values.
203  */
204 static int DetectProtoInitTest(DetectEngineCtx **de_ctx, Signature **sig,
205  DetectProto *dp, const char *str)
206 {
207  char fullstr[1024];
208  int result = 0;
209  static uint32_t test_sid = 1;
210 
211  *sig = NULL;
212 
213  if (snprintf(fullstr, 1024,
214  "alert %s any any -> any any (msg:\"DetectProto"
215  " test\"; sid:%u;)",
216  str, test_sid++) >= 1024) {
217  goto end;
218  }
219 
220  if (*de_ctx == NULL) {
222  if (*de_ctx == NULL) {
223  goto end;
224  }
225 
226  (*de_ctx)->flags |= DE_QUIET;
227  }
228 
229  Signature *s = DetectEngineAppendSig(*de_ctx, fullstr);
230  if (s == NULL) {
231  goto end;
232  }
233  *sig = s;
234 
235  result = 1;
236 
237 end:
238  return result;
239 }
240 
241 /**
242  * \test ProtoTestParse01 is a test to make sure that we parse the
243  * protocol correctly, when given valid proto option.
244  */
245 static int ProtoTestParse01 (void)
246 {
247  DetectProto dp;
248  memset(&dp,0,sizeof(DetectProto));
249 
250  int r = DetectProtoParse(&dp, "6");
251 
252  FAIL_IF_NOT(r < 0);
253 
254  PASS;
255 }
256 /**
257  * \test ProtoTestParse02 is a test to make sure that we parse the
258  * protocol correctly, when given "tcp" as proto option.
259  */
260 static int ProtoTestParse02 (void)
261 {
262  DetectProto dp;
263  memset(&dp,0,sizeof(DetectProto));
264 
265  int r = DetectProtoParse(&dp, "tcp");
266 
267  FAIL_IF_NOT(r >= 0);
268  FAIL_IF_NOT(dp.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8)));
269 
270  PASS;
271 }
272 /**
273  * \test ProtoTestParse03 is a test to make sure that we parse the
274  * protocol correctly, when given "ip" as proto option.
275  */
276 static int ProtoTestParse03 (void)
277 {
278  DetectProto dp;
279  memset(&dp,0,sizeof(DetectProto));
280 
281  int r = DetectProtoParse(&dp, "ip");
282 
283  FAIL_IF_NOT(r >= 0);
285 
286  PASS;
287 }
288 
289 /**
290  * \test ProtoTestParse04 is a test to make sure that we do not parse the
291  * protocol, when given an invalid proto option.
292  */
293 static int ProtoTestParse04 (void)
294 {
295  DetectProto dp;
296  memset(&dp,0,sizeof(DetectProto));
297 
298  /* Check for a bad number */
299  int r = DetectProtoParse(&dp, "4242");
300 
301  FAIL_IF_NOT(r < 0);
302 
303  PASS;
304 }
305 
306 /**
307  * \test ProtoTestParse05 is a test to make sure that we do not parse the
308  * protocol, when given an invalid proto option.
309  */
310 static int ProtoTestParse05 (void)
311 {
312  DetectProto dp;
313  memset(&dp,0,sizeof(DetectProto));
314 
315  /* Check for a bad string */
316  int r = DetectProtoParse(&dp, "tcp/udp");
317 
318  FAIL_IF_NOT(r < 0);
319 
320  PASS;
321 }
322 
323 /**
324  * \test make sure that we properly parse tcp-pkt
325  */
326 static int ProtoTestParse06 (void)
327 {
328  DetectProto dp;
329  memset(&dp,0,sizeof(DetectProto));
330 
331  /* Check for a bad string */
332  int r = DetectProtoParse(&dp, "tcp-pkt");
333 
334  FAIL_IF(r < 0);
336 
337  PASS;
338 }
339 
340 /**
341  * \test make sure that we properly parse tcp-stream
342  */
343 static int ProtoTestParse07 (void)
344 {
345  DetectProto dp;
346  memset(&dp,0,sizeof(DetectProto));
347 
348  /* Check for a bad string */
349  int r = DetectProtoParse(&dp, "tcp-stream");
350 
351  FAIL_IF(r < 0);
353 
354  PASS;
355 }
356 
357 /**
358  * \test DetectIPProtoTestSetup01 is a test for a protocol setting up in
359  * signature.
360  */
361 static int DetectProtoTestSetup01(void)
362 {
363  DetectProto dp;
364  Signature *sig = NULL;
365  DetectEngineCtx *de_ctx = NULL;
366  int i;
367 
368  memset(&dp, 0, sizeof(dp));
369 
370  FAIL_IF_NOT(DetectProtoInitTest(&de_ctx, &sig, &dp, "tcp"));
371 
372  /* The signature proto should be TCP */
373  FAIL_IF_NOT(sig->init_data->proto.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8)));
374 
375  for (i = 2; i < 256 / 8; i++) {
376  FAIL_IF(sig->init_data->proto.proto[i] != 0);
377  }
378 
380 
381  PASS;
382 }
383 
384 /**
385  * \test DetectProtoTestSetup02 is a test for a icmpv4 and icmpv6
386  * protocol setting up in signature.
387  */
388 static int DetectProtoTestSetup02(void)
389 {
390  DetectProto dp;
391  Signature *sig_icmpv4 = NULL;
392  Signature *sig_icmpv6 = NULL;
393  Signature *sig_icmp = NULL;
394  DetectEngineCtx *de_ctx = NULL;
395 
396  memset(&dp, 0, sizeof(dp));
397 
398  FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv4, &dp, "icmpv4") == 0);
399  FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv6, &dp, "icmpv6") == 0);
400  FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmp, &dp, "icmp") == 0);
401 
402  FAIL_IF_NOT(sig_icmpv4->init_data->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8)));
403  FAIL_IF_NOT(
404  sig_icmpv6->init_data->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8)));
405 
406  FAIL_IF_NOT(sig_icmp->init_data->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8)));
407  FAIL_IF_NOT(sig_icmp->init_data->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8)));
408 
410 
411  PASS;
412 }
413 
414 /**
415  * \test signature parsing with tcp-pkt and tcp-stream
416  */
417 
418 static int DetectProtoTestSig01(void)
419 {
422 
423  de_ctx->flags |= DE_QUIET;
424 
426  de_ctx, "alert tcp-pkt any any -> any any (msg:\"tcp-pkt\"; content:\"blah\"; sid:1;)");
427  FAIL_IF_NULL(s);
428 
430  "alert tcp-stream any any -> any any (msg:\"tcp-stream\"; content:\"blah\"; sid:2;)");
431  FAIL_IF_NULL(s);
432 
434 
435  PASS;
436 }
437 #endif /* UNITTESTS */
438 
439 /**
440  * \brief this function registers unit tests for DetectProto
441  */
443 {
444 #ifdef UNITTESTS
445  UtRegisterTest("ProtoTestParse01", ProtoTestParse01);
446  UtRegisterTest("ProtoTestParse02", ProtoTestParse02);
447  UtRegisterTest("ProtoTestParse03", ProtoTestParse03);
448  UtRegisterTest("ProtoTestParse04", ProtoTestParse04);
449  UtRegisterTest("ProtoTestParse05", ProtoTestParse05);
450  UtRegisterTest("ProtoTestParse06", ProtoTestParse06);
451  UtRegisterTest("ProtoTestParse07", ProtoTestParse07);
452 
453  UtRegisterTest("DetectProtoTestSetup01", DetectProtoTestSetup01);
454  UtRegisterTest("DetectProtoTestSetup02", DetectProtoTestSetup02);
455 
456  UtRegisterTest("DetectProtoTestSig01", DetectProtoTestSig01);
457 #endif /* UNITTESTS */
458 }
459 
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
DetectEngineProtoList
void DetectEngineProtoList(void)
Definition: detect-engine-proto.c:73
flow-util.h
DETECT_PROTO_IPV6
#define DETECT_PROTO_IPV6
Definition: detect-engine-proto.h:32
detect-engine-siggroup.h
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:101
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
name
const char * name
Definition: detect-engine-proto.c:47
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:995
DETECT_PROTO_ANY
#define DETECT_PROTO_ANY
Definition: detect-engine-proto.h:28
DETECT_PROTO_ETHERNET
#define DETECT_PROTO_ETHERNET
Definition: detect-engine-proto.h:33
proto
uint8_t proto
Definition: detect-engine-proto.c:48
proto_table
struct @60 proto_table[]
DetectEngineCtxFree
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Definition: detect-engine.c:2912
DE_QUIET
#define DE_QUIET
Definition: detect.h:334
SignatureInitData_::init_flags
uint32_t init_flags
Definition: detect.h:625
DetectEngineAppendSig
Signature * DetectEngineAppendSig(DetectEngineCtx *, const char *)
Parse and append a Signature into the Detection Engine Context signature list.
Definition: detect-parse.c:3971
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
DETECT_PROTO_ARP
#define DETECT_PROTO_ARP
Definition: detect-engine-proto.h:34
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:22
DetectProtoParse
int DetectProtoParse(DetectProto *dp, const char *str)
Parses a protocol sent as a string.
Definition: detect-engine-proto.c:89
SIG_TYPE_IPONLY
@ SIG_TYPE_IPONLY
Definition: detect.h:67
proto2
uint8_t proto2
Definition: detect-engine-proto.c:49
detect-engine-mpm.h
detect.h
SignatureInitData_::proto
DetectProto proto
Definition: detect.h:655
app-layer-parser.h
DetectProto_::proto
uint8_t proto[256/8]
Definition: detect-engine-proto.h:39
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:322
Signature_::init_data
SignatureInitData * init_data
Definition: detect.h:775
detect-engine-state.h
Data structures and function prototypes for keeping state for the detection engine.
ARRAY_SIZE
#define ARRAY_SIZE(arr)
Definition: suricata-common.h:563
DetectProto_::flags
uint8_t flags
Definition: detect-engine-proto.h:40
DETECT_PROTO_IPV4
#define DETECT_PROTO_IPV4
Definition: detect-engine-proto.h:31
cnt
uint32_t cnt
Definition: tmqh-packetpool.h:7
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
suricata-common.h
SIG_FLAG_INIT_FRAME
#define SIG_FLAG_INIT_FRAME
Definition: detect.h:299
DETECT_PROTO_ONLY_STREAM
#define DETECT_PROTO_ONLY_STREAM
Definition: detect-engine-proto.h:30
Signature_::proto
DetectProto * proto
Definition: detect.h:711
DetectProto_
Definition: detect-engine-proto.h:38
str
#define str(s)
Definition: suricata-common.h:313
Signature_::id
uint32_t id
Definition: detect.h:741
detect-parse.h
Signature_
Signature container.
Definition: detect.h:692
DetectProtoHasExplicitProto
bool DetectProtoHasExplicitProto(const DetectProto *dp, const uint8_t proto)
see if a DetectProto explicitly a certain proto Explicit means the protocol was explicitly set,...
Definition: detect-engine-proto.c:132
DETECT_PROTO_ONLY_PKT
#define DETECT_PROTO_ONLY_PKT
Definition: detect-engine-proto.h:29
flags
uint8_t flags
Definition: detect-engine-proto.c:50
DetectEngineCtxInit
DetectEngineCtx * DetectEngineCtxInit(void)
Definition: detect-engine.c:2873
DETECT_PROTO_L2_ANY
#define DETECT_PROTO_L2_ANY
Definition: detect-engine-proto.h:35
IPPROTO_SCTP
#define IPPROTO_SCTP
Definition: decode.h:1273
DetectEngineCtx_::flags
uint8_t flags
Definition: detect.h:997
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
Signature_::type
enum SignatureType type
Definition: detect.h:695
flow-var.h
DetectProtoContainsProto
int DetectProtoContainsProto(const DetectProto *dp, int proto)
see if a DetectProto contains a certain proto
Definition: detect-engine-proto.c:114
DetectProtoFinalizeSignature
int DetectProtoFinalizeSignature(Signature *s)
Definition: detect-engine-proto.c:156
DetectProtoTests
void DetectProtoTests(void)
this function registers unit tests for DetectProto
Definition: detect-engine-proto.c:442