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