suricata
detect-engine-proto.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2010 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 
171  *de_ctx = DetectEngineCtxInit();
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  if (r < 0) {
205  return 1;
206  }
207 
208  SCLogDebug("DetectProtoParse should have rejected the \"6\" string");
209  return 0;
210 }
211 /**
212  * \test ProtoTestParse02 is a test to make sure that we parse the
213  * protocol correctly, when given "tcp" as proto option.
214  */
215 static int ProtoTestParse02 (void)
216 {
217  DetectProto dp;
218  memset(&dp,0,sizeof(DetectProto));
219 
220  int r = DetectProtoParse(&dp, "tcp");
221  if (r >= 0 && dp.proto[(IPPROTO_TCP/8)] & (1<<(IPPROTO_TCP%8))) {
222  return 1;
223  }
224 
225  SCLogDebug("ProtoTestParse02: Error in parsing the \"tcp\" string");
226  return 0;
227 }
228 /**
229  * \test ProtoTestParse03 is a test to make sure that we parse the
230  * protocol correctly, when given "ip" as proto option.
231  */
232 static int ProtoTestParse03 (void)
233 {
234  DetectProto dp;
235  memset(&dp,0,sizeof(DetectProto));
236 
237  int r = DetectProtoParse(&dp, "ip");
238  if (r >= 0 && dp.flags & DETECT_PROTO_ANY) {
239  return 1;
240  }
241 
242  SCLogDebug("ProtoTestParse03: Error in parsing the \"ip\" string");
243  return 0;
244 }
245 
246 /**
247  * \test ProtoTestParse04 is a test to make sure that we do not parse the
248  * protocol, when given an invalid proto option.
249  */
250 static int ProtoTestParse04 (void)
251 {
252  DetectProto dp;
253  memset(&dp,0,sizeof(DetectProto));
254 
255  /* Check for a bad number */
256  int r = DetectProtoParse(&dp, "4242");
257  if (r < 0) {
258  return 1;
259  }
260 
261  SCLogDebug("ProtoTestParse04: it should not parsing the \"4242\" string");
262  return 0;
263 }
264 
265 /**
266  * \test ProtoTestParse05 is a test to make sure that we do not parse the
267  * protocol, when given an invalid proto option.
268  */
269 static int ProtoTestParse05 (void)
270 {
271  DetectProto dp;
272  memset(&dp,0,sizeof(DetectProto));
273 
274  /* Check for a bad string */
275  int r = DetectProtoParse(&dp, "tcp/udp");
276  if (r < 0) {
277  return 1;
278  }
279 
280  SCLogDebug("ProtoTestParse05: it should not parsing the \"tcp/udp\" string");
281  return 0;
282 }
283 
284 /**
285  * \test make sure that we properly parse tcp-pkt
286  */
287 static int ProtoTestParse06 (void)
288 {
289  DetectProto dp;
290  memset(&dp,0,sizeof(DetectProto));
291 
292  /* Check for a bad string */
293  int r = DetectProtoParse(&dp, "tcp-pkt");
294  if (r < 0) {
295  printf("parsing tcp-pkt failed: ");
296  return 0;
297  }
298 
299  if (!(dp.flags & DETECT_PROTO_ONLY_PKT)) {
300  printf("DETECT_PROTO_ONLY_PKT flag not set: ");
301  return 0;
302  }
303 
304  return 1;
305 }
306 
307 /**
308  * \test make sure that we properly parse tcp-stream
309  */
310 static int ProtoTestParse07 (void)
311 {
312  DetectProto dp;
313  memset(&dp,0,sizeof(DetectProto));
314 
315  /* Check for a bad string */
316  int r = DetectProtoParse(&dp, "tcp-stream");
317  if (r < 0) {
318  printf("parsing tcp-stream failed: ");
319  return 0;
320  }
321 
322  if (!(dp.flags & DETECT_PROTO_ONLY_STREAM)) {
323  printf("DETECT_PROTO_ONLY_STREAM flag not set: ");
324  return 0;
325  }
326 
327  return 1;
328 }
329 
330 /**
331  * \test DetectIPProtoTestSetup01 is a test for a protocol setting up in
332  * signature.
333  */
334 static int DetectProtoTestSetup01(void)
335 {
336  DetectProto dp;
337  Signature *sig = NULL;
338  DetectEngineCtx *de_ctx = NULL;
339  int result = 0;
340  int i;
341 
342  memset(&dp, 0, sizeof(dp));
343 
344  result = DetectProtoInitTest(&de_ctx, &sig, &dp, "tcp");
345  if (result == 0) {
346  goto end;
347  }
348 
349  result = 0;
350 
351  /* The signature proto should be TCP */
352  if (!(sig->proto.proto[(IPPROTO_TCP/8)] & (1<<(IPPROTO_TCP%8)))) {
353  printf("failed in sig matching\n");
354  goto cleanup;
355  }
356  for (i = 2; i < 256/8; i++) {
357  if (sig->proto.proto[i] != 0) {
358  printf("failed in sig clear\n");
359  goto cleanup;
360  }
361  }
362 
363  result = 1;
364 
365 cleanup:
366  SigGroupCleanup(de_ctx);
367  SigCleanSignatures(de_ctx);
368  DetectEngineCtxFree(de_ctx);
369 end:
370  return result;
371 }
372 
373 /**
374  * \test DetectrotoTestSetup02 is a test for a icmpv4 and icmpv6
375  * protocol setting up in signature.
376  */
377 static int DetectProtoTestSetup02(void)
378 {
379  DetectProto dp;
380  Signature *sig_icmpv4 = NULL;
381  Signature *sig_icmpv6 = NULL;
382  Signature *sig_icmp = NULL;
383  DetectEngineCtx *de_ctx = NULL;
384  int result = 0;
385  int i;
386 
387  memset(&dp, 0, sizeof(dp));
388 
389  if (DetectProtoInitTest(&de_ctx, &sig_icmpv4, &dp, "icmpv4") == 0) {
390  printf("failure - imcpv4.\n");
391  goto end;
392  }
393 
394  if (DetectProtoInitTest(&de_ctx, &sig_icmpv6, &dp, "icmpv6") == 0) {
395  printf("failure - imcpv6.\n");
396  goto end;
397  }
398 
399  if (DetectProtoInitTest(&de_ctx, &sig_icmp, &dp, "icmp") == 0) {
400  printf("failure - imcp.\n");
401  goto end;
402  }
403 
404  for (i = 0; i < 256 / 8; i++) {
405  if (i == IPPROTO_ICMP) {
406  if (!(sig_icmpv4->proto.proto[i / 8] & (1 << (i % 8)))) {
407  printf("failed in sig matching - icmpv4 - icmpv4.\n");
408  goto end;
409  }
410  continue;
411  }
412  if (sig_icmpv4->proto.proto[i / 8] & (1 << (i % 8))) {
413  printf("failed in sig matching - icmpv4 - others.\n");
414  goto end;
415  }
416  }
417 
418  for (i = 0; i < 256 / 8; i++) {
419  if (i == IPPROTO_ICMPV6) {
420  if (!(sig_icmpv6->proto.proto[i / 8] & (1 << (i % 8)))) {
421  printf("failed in sig matching - icmpv6 - icmpv6.\n");
422  goto end;
423  }
424  continue;
425  }
426  if (sig_icmpv6->proto.proto[i / 8] & (1 << (i % 8))) {
427  printf("failed in sig matching - icmpv6 - others.\n");
428  goto end;
429  }
430  }
431 
432  for (i = 0; i < 256 / 8; i++) {
433  if (i == IPPROTO_ICMP || i == IPPROTO_ICMPV6) {
434  if (!(sig_icmp->proto.proto[i / 8] & (1 << (i % 8)))) {
435  printf("failed in sig matching - icmp - icmp.\n");
436  goto end;
437  }
438  continue;
439  }
440  if (sig_icmpv6->proto.proto[i / 8] & (1 << (i % 8))) {
441  printf("failed in sig matching - icmp - others.\n");
442  goto end;
443  }
444  }
445 
446  result = 1;
447 
448  end:
449  SigGroupCleanup(de_ctx);
450  SigCleanSignatures(de_ctx);
451  DetectEngineCtxFree(de_ctx);
452  return result;
453 }
454 
455 /**
456  * \test DetectProtoTestSig01 is a test for checking the working of protocol
457  * detection by setting up the signature and later testing its working
458  * by matching the received packet against the sig.
459  */
460 
461 static int DetectProtoTestSig01(void)
462 {
463  Packet *p = NULL;
464  Signature *s = NULL;
465  ThreadVars th_v;
466  DetectEngineThreadCtx *det_ctx;
467  int result = 0;
468  Flow f;
469 
470  memset(&f, 0, sizeof(Flow));
471  memset(&th_v, 0, sizeof(th_v));
472 
473  FLOW_INITIALIZE(&f);
474 
475  p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
476 
477  p->flow = &f;
479  p->flags |= PKT_HAS_FLOW;
480 
482  if (de_ctx == NULL) {
483  goto end;
484  }
485 
486  de_ctx->flags |= DE_QUIET;
487 
488  s = de_ctx->sig_list = SigInit(de_ctx,"alert udp any any -> any any "
489  "(msg:\"Not tcp\"; flow:to_server; sid:1;)");
490 
491  if (s == NULL)
492  goto end;
493 
494  s = s->next = SigInit(de_ctx,"alert ip any any -> any any "
495  "(msg:\"IP\"; flow:to_server; sid:2;)");
496 
497  if (s == NULL)
498  goto end;
499 
500  s = s->next = SigInit(de_ctx,"alert tcp any any -> any any "
501  "(msg:\"TCP\"; flow:to_server; sid:3;)");
502 
503  if (s == NULL)
504  goto end;
505 
506  SigGroupBuild(de_ctx);
507  DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
508 
509  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
510  if (PacketAlertCheck(p, 1)) {
511  printf("sid 1 alerted, but should not have: ");
512  goto cleanup;
513  } else if (PacketAlertCheck(p, 2) == 0) {
514  printf("sid 2 did not alert, but should have: ");
515  goto cleanup;
516  } else if (PacketAlertCheck(p, 3) == 0) {
517  printf("sid 3 did not alert, but should have: ");
518  goto cleanup;
519  }
520 
521  result = 1;
522 
523 cleanup:
524  FLOW_DESTROY(&f);
525 
526  SigGroupCleanup(de_ctx);
527  SigCleanSignatures(de_ctx);
528 
529  DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
530  DetectEngineCtxFree(de_ctx);
531 
532  UTHFreePackets(&p, 1);
533 end:
534  return result;
535 }
536 
537 /**
538  * \test signature parsing with tcp-pkt and tcp-stream
539  */
540 
541 static int DetectProtoTestSig02(void)
542 {
543  Signature *s = NULL;
544  int result = 0;
545 
547  if (de_ctx == NULL) {
548  goto end;
549  }
550 
551  de_ctx->flags |= DE_QUIET;
552 
553  s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp-pkt any any -> any any "
554  "(msg:\"tcp-pkt\"; content:\"blah\"; sid:1;)");
555  if (s == NULL) {
556  printf("tcp-pkt sig parsing failed: ");
557  goto end;
558  }
559 
560  s = s->next = SigInit(de_ctx,"alert tcp-stream any any -> any any "
561  "(msg:\"tcp-stream\"; content:\"blah\"; sid:2;)");
562  if (s == NULL) {
563  printf("tcp-pkt sig parsing failed: ");
564  goto end;
565  }
566 
567  result = 1;
568 
569 end:
570  if (de_ctx != NULL)
571  DetectEngineCtxFree(de_ctx);
572  return result;
573 }
574 #endif /* UNITTESTS */
575 
576 /**
577  * \brief this function registers unit tests for DetectProto
578  */
580 {
581 #ifdef UNITTESTS
582  UtRegisterTest("ProtoTestParse01", ProtoTestParse01);
583  UtRegisterTest("ProtoTestParse02", ProtoTestParse02);
584  UtRegisterTest("ProtoTestParse03", ProtoTestParse03);
585  UtRegisterTest("ProtoTestParse04", ProtoTestParse04);
586  UtRegisterTest("ProtoTestParse05", ProtoTestParse05);
587  UtRegisterTest("ProtoTestParse06", ProtoTestParse06);
588  UtRegisterTest("ProtoTestParse07", ProtoTestParse07);
589 
590  UtRegisterTest("DetectProtoTestSetup01", DetectProtoTestSetup01);
591  UtRegisterTest("DetectProtoTestSetup02", DetectProtoTestSetup02);
592 
593  UtRegisterTest("DetectProtoTestSig01", DetectProtoTestSig01);
594  UtRegisterTest("DetectProtoTestSig02", DetectProtoTestSig02);
595 #endif /* UNITTESTS */
596 }
597 
DetectProto proto
Definition: detect.h:539
#define DETECT_PROTO_IPV6
#define SCLogDebug(...)
Definition: util-debug.h:335
struct Flow_ * flow
Definition: decode.h:445
void DetectProtoTests(void)
this function registers unit tests for DetectProto
int PacketAlertCheck(Packet *p, uint32_t sid)
Check if a certain sid alerted, this is used in the test functions.
#define DETECT_PROTO_ANY
Signature * SigInit(DetectEngineCtx *, const char *)
Parses a signature and adds it to the Detection Engine Context.
Signature * sig_list
Definition: detect.h:767
uint8_t proto[256/8]
void SigCleanSignatures(DetectEngineCtx *de_ctx)
int ByteExtractStringUint8(uint8_t *res, int base, uint16_t len, const char *str)
Definition: util-byte.c:284
TmEcode DetectEngineThreadCtxInit(ThreadVars *, void *, void **)
initialize thread specific detection engine context
Signature container.
Definition: detect.h:522
main detection engine ctx
Definition: detect.h:761
TmEcode DetectEngineThreadCtxDeinit(ThreadVars *, void *)
int DetectProtoParse(DetectProto *dp, const char *str)
Parses a protocol sent as a string.
#define DE_QUIET
Definition: detect.h:292
#define str(s)
uint8_t flags
Definition: detect.h:762
Data structures and function prototypes for keeping state for the detection engine.
#define FLOW_DESTROY(f)
Definition: flow-util.h:121
int SigGroupBuild(DetectEngineCtx *de_ctx)
Convert the signature list into the runtime match structure.
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
void SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
wrapper for old tests
Definition: detect.c:1669
uint8_t flowflags
Definition: decode.h:439
Packet * UTHBuildPacket(uint8_t *payload, uint16_t payload_len, uint8_t ipproto)
UTHBuildPacket is a wrapper that build packets with default ip and port fields.
#define FLOW_PKT_TOSERVER
Definition: flow.h:201
int SigGroupCleanup(DetectEngineCtx *de_ctx)
struct Signature_ * next
Definition: detect.h:594
uint8_t proto
#define DETECT_PROTO_IPV4
#define DETECT_PROTO_ONLY_STREAM
#define FLOW_INITIALIZE(f)
Definition: flow-util.h:39
#define PKT_HAS_FLOW
Definition: decode.h:1093
#define DETECT_PROTO_ONLY_PKT
Per thread variable structure.
Definition: threadvars.h:57
uint32_t flags
Definition: decode.h:443
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
void UTHFreePackets(Packet **p, int numpkts)
UTHFreePackets: function to release the allocated data from UTHBuildPacket and the packet itself...
Flow data structure.
Definition: flow.h:325
int DetectProtoContainsProto(const DetectProto *dp, int proto)
see if a DetectProto contains a certain proto
DetectEngineCtx * DetectEngineCtxInit(void)