From 12386689619964e9bf9a1fdf3b37c13b33cc0626 Mon Sep 17 00:00:00 2001 From: Pablo Rincon Date: Wed, 5 May 2010 20:15:57 +0200 Subject: [PATCH] Adding actions order and suport for rule action "pass" --- src/Makefile.am | 1 + src/decode.h | 2 + src/detect-engine-iponly.c | 11 +- src/detect-engine-sigorder.c | 512 +++++++++++++++++- src/detect.c | 102 +++- src/flow.h | 2 + src/suricata.c | 4 + src/util-action.c | 995 +++++++++++++++++++++++++++++++++++ src/util-action.h | 12 + src/util-error.c | 1 + src/util-error.h | 1 + suricata.yaml | 9 + 12 files changed, 1628 insertions(+), 24 deletions(-) create mode 100644 src/util-action.c create mode 100644 src/util-action.h diff --git a/src/Makefile.am b/src/Makefile.am index 9e6eaa32c1..bb37b63c8a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -192,6 +192,7 @@ app-layer-ftp.c app-layer-ftp.h \ defrag.c defrag.h \ output.c output.h \ win32-misc.c win32-misc.h \ +util-action.c util-action.h \ win32-syslog.h # set the include path found by configure diff --git a/src/decode.h b/src/decode.h index 5f29a8c307..53cae2ca61 100644 --- a/src/decode.h +++ b/src/decode.h @@ -186,6 +186,8 @@ typedef uint16_t Port; /* structure to store the sids/gids/etc the detection engine * found in this packet */ typedef struct PacketAlert_ { + uint16_t num; /* Internal num, used for sorting */ + uint8_t action; /* Internal num, used for sorting */ uint32_t gid; uint32_t sid; uint8_t rev; diff --git a/src/detect-engine-iponly.c b/src/detect-engine-iponly.c index 9b44e66b61..84a19ffd81 100644 --- a/src/detect-engine-iponly.c +++ b/src/detect-engine-iponly.c @@ -944,6 +944,9 @@ void IPOnlyMatchPacket(DetectEngineCtx *de_ctx, /* The final results will be at io_tctx */ io_tctx->sig_match_array[u] = dst->array[u] & src->array[u]; + /* We have to move the logic of the signature checking + * to the main detect loop, in order to apply the + * priority of actions (pass, drop, reject, alert) */ if (io_tctx->sig_match_array[u] != 0) { /* We have a match :) Let's see from which signum's */ uint8_t bitarray = io_tctx->sig_match_array[u]; @@ -961,10 +964,6 @@ void IPOnlyMatchPacket(DetectEngineCtx *de_ctx, u * 8 + i, s->id, s->msg); if (!(s->flags & SIG_FLAG_NOALERT)) { - PacketAlertHandle(de_ctx, det_ctx, s, p); - /* set verdict on packet */ - p->action |= s->action; - if (p->flow != NULL) { if (s->action & ACTION_DROP) p->flow->flags |= FLOW_ACTION_DROP; @@ -974,7 +973,11 @@ void IPOnlyMatchPacket(DetectEngineCtx *de_ctx, p->flow->flags |= FLOW_ACTION_DROP; if (s->action & ACTION_REJECT_BOTH) p->flow->flags |= FLOW_ACTION_DROP; + if (s->action & ACTION_PASS) + p->flow->flags |= FLOW_ACTION_PASS; } + p->action |= ACTION_PASS; + PacketAlertHandle(de_ctx, det_ctx, s, p); } } } diff --git a/src/detect-engine-sigorder.c b/src/detect-engine-sigorder.c index 3fdb3438df..87f55bd1ee 100644 --- a/src/detect-engine-sigorder.c +++ b/src/detect-engine-sigorder.c @@ -36,6 +36,8 @@ #include "util-unittest.h" #include "util-debug.h" +#include "util-action.h" +#include "action-globals.h" #define DETECT_FLOWVAR_NOT_USED 1 #define DETECT_FLOWVAR_TYPE_READ 2 @@ -270,7 +272,7 @@ static void SCSigOrderByAction(DetectEngineCtx *de_ctx, while (min != max) { prev = min; /* the sorting logic */ - if (sw->sig->action <= min->sig->action) { + if (ActionOrderVal(sw->sig->action) >= ActionOrderVal(min->sig->action)) { min = min->next; continue; } @@ -696,7 +698,7 @@ static void SCSigOrderByPriority(DetectEngineCtx *de_ctx, while (min != max) { prev = min; /* the sorting logic */ - if (sw->sig->prio >= min->sig->prio) { + if (sw->sig->prio <= min->sig->prio) { min = min->next; continue; } @@ -1550,6 +1552,508 @@ end: return result; } +static int SCSigTestSignatureOrdering07(void) +{ + int result = 1; + Signature *prevsig = NULL, *sig = NULL; + SCSigSignatureWrapper *sw = NULL; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; sid:1; rev:4;)"); + if (sig == NULL) { + goto end; + } + prevsig = sig; + de_ctx->sig_list = sig; + + sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; sid:2; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:10; depth:4; sid:3; rev:4; priority:3;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; depth:4; sid:4; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:5; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "drop tcp any !21:902 -> any any (msg:\"Testing sigordering drop\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:6; rev:4; priority:1;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "reject tcp any !21:902 -> any any (msg:\"Testing sigordering reject\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:7; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; sid:8; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByAction); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbits); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvar); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvar); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriority); + SCSigOrderSignatures(de_ctx); + + sw = de_ctx->sc_sig_sig_wrapper; + uint8_t pos = 0; + while (sw != NULL) { + switch (pos) { + case 0: + result &=(sw->sig->id == 2)? 1 : 0; + break; + case 1: + result &=(sw->sig->id == 4)? 1 : 0; + break; + case 2: + result &=(sw->sig->id == 5)? 1 : 0; + break; + case 3: + result &=(sw->sig->id == 6)? 1 : 0; + break; + case 4: + result &=(sw->sig->id == 7)? 1 : 0; + break; + case 5: + result &=(sw->sig->id == 1)? 1 : 0; + break; + case 6: + result &=(sw->sig->id == 3)? 1 : 0; + break; + case 7: + result &=(sw->sig->id == 8)? 1 : 0; + break; + } + sw = sw->next; + pos++; + } + +end: + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test Order with a different Action priority + * (as specified from config) + */ +static int SCSigTestSignatureOrdering08(void) +{ + int result = 1; + Signature *prevsig = NULL, *sig = NULL; + SCSigSignatureWrapper *sw = NULL; + extern uint8_t action_order_sigs[4]; + + /* Let's change the order. Default is pass, drop, reject, alert (pass has highest prio) */ + action_order_sigs[0] = ACTION_REJECT; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_ALERT; + action_order_sigs[3] = ACTION_PASS; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; sid:1; rev:4;)"); + if (sig == NULL) { + goto end; + } + prevsig = sig; + de_ctx->sig_list = sig; + + sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; sid:2; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:10; depth:4; sid:3; rev:4; priority:3;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; depth:4; sid:4; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:5; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "drop tcp any !21:902 -> any any (msg:\"Testing sigordering drop\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:6; rev:4; priority:1;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "reject tcp any !21:902 -> any any (msg:\"Testing sigordering reject\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:7; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; sid:8; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByAction); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbits); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvar); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvar); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriority); + SCSigOrderSignatures(de_ctx); + + sw = de_ctx->sc_sig_sig_wrapper; + + uint8_t pos = 0; + while (sw != NULL) { + + switch (pos) { + case 0: + result &=(sw->sig->id == 7)? 1 : 0; + break; + case 1: + result &=(sw->sig->id == 6)? 1 : 0; + break; + case 2: + result &=(sw->sig->id == 1)? 1 : 0; + break; + case 3: + result &=(sw->sig->id == 3)? 1 : 0; + break; + case 4: + result &=(sw->sig->id == 8)? 1 : 0; + break; + case 5: + result &=(sw->sig->id == 2)? 1 : 0; + break; + case 6: + result &=(sw->sig->id == 4)? 1 : 0; + break; + case 7: + result &=(sw->sig->id == 5)? 1 : 0; + break; + } + sw = sw->next; + pos++; + } + +end: + /* Restore the default pre-order definition */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test Order with a different Action priority + * (as specified from config) + */ +static int SCSigTestSignatureOrdering09(void) +{ + int result = 1; + Signature *prevsig = NULL, *sig = NULL; + SCSigSignatureWrapper *sw = NULL; + extern uint8_t action_order_sigs[4]; + + /* Let's change the order. Default is pass, drop, reject, alert (pass has highest prio) */ + action_order_sigs[0] = ACTION_DROP; + action_order_sigs[1] = ACTION_REJECT; + action_order_sigs[2] = ACTION_ALERT; + action_order_sigs[3] = ACTION_PASS; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; sid:1; rev:4;)"); + if (sig == NULL) { + goto end; + } + prevsig = sig; + de_ctx->sig_list = sig; + + sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; sid:2; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:10; depth:4; sid:3; rev:4; priority:3;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; depth:4; sid:4; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:5; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "drop tcp any !21:902 -> any any (msg:\"Testing sigordering drop\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:6; rev:4; priority:1;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "reject tcp any !21:902 -> any any (msg:\"Testing sigordering reject\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:7; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; sid:8; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByAction); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbits); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvar); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvar); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriority); + SCSigOrderSignatures(de_ctx); + + sw = de_ctx->sc_sig_sig_wrapper; + + uint8_t pos = 0; + while (sw != NULL) { + + switch (pos) { + case 0: + result &=(sw->sig->id == 6)? 1 : 0; + break; + case 1: + result &=(sw->sig->id == 7)? 1 : 0; + break; + case 2: + result &=(sw->sig->id == 1)? 1 : 0; + break; + case 3: + result &=(sw->sig->id == 3)? 1 : 0; + break; + case 4: + result &=(sw->sig->id == 8)? 1 : 0; + break; + case 5: + result &=(sw->sig->id == 2)? 1 : 0; + break; + case 6: + result &=(sw->sig->id == 4)? 1 : 0; + break; + case 7: + result &=(sw->sig->id == 5)? 1 : 0; + break; + } + sw = sw->next; + pos++; + } + +end: + /* Restore the default pre-order definition */ + action_order_sigs[0] = ACTION_DROP; + action_order_sigs[1] = ACTION_REJECT; + action_order_sigs[2] = ACTION_PASS; + action_order_sigs[3] = ACTION_ALERT; + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test Order with a different Action priority + * (as specified from config) + */ +static int SCSigTestSignatureOrdering10(void) +{ + int result = 1; + Signature *prevsig = NULL, *sig = NULL; + SCSigSignatureWrapper *sw = NULL; + extern uint8_t action_order_sigs[4]; + + /* Let's change the order. Default is pass, drop, reject, alert (pass has highest prio) */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_ALERT; + action_order_sigs[2] = ACTION_DROP; + action_order_sigs[3] = ACTION_REJECT; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:0; depth:4; pcre:\"/220[- ]/\"; sid:1; rev:4;)"); + if (sig == NULL) { + goto end; + } + prevsig = sig; + de_ctx->sig_list = sig; + + sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; sid:2; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:10; depth:4; sid:3; rev:4; priority:3;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:10; depth:4; sid:4; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "pass tcp any !21:902 -> any any (msg:\"Testing sigordering pass\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:5; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "drop tcp any !21:902 -> any any (msg:\"Testing sigordering drop\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:6; rev:4; priority:1;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "reject tcp any !21:902 -> any any (msg:\"Testing sigordering reject\"; content:\"220\"; offset:11; depth:4; pcre:\"/220[- ]/\"; sid:7; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + sig = SigInit(de_ctx, "alert tcp any !21:902 -> any any (msg:\"Testing sigordering alert\"; content:\"220\"; offset:12; depth:4; pcre:\"/220[- ]/\"; sid:8; rev:4; priority:2;)"); + if (sig == NULL) { + goto end; + } + prevsig->next = sig; + prevsig = sig; + + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByAction); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbits); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvar); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvar); + SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriority); + SCSigOrderSignatures(de_ctx); + + sw = de_ctx->sc_sig_sig_wrapper; + + uint8_t pos = 0; + while (sw != NULL) { + + switch (pos) { + case 0: + result &=(sw->sig->id == 2)? 1 : 0; + break; + case 1: + result &=(sw->sig->id == 4)? 1 : 0; + break; + case 2: + result &=(sw->sig->id == 5)? 1 : 0; + break; + case 3: + result &=(sw->sig->id == 1)? 1 : 0; + break; + case 4: + result &=(sw->sig->id == 3)? 1 : 0; + break; + case 5: + result &=(sw->sig->id == 8)? 1 : 0; + break; + case 6: + result &=(sw->sig->id == 6)? 1 : 0; + break; + case 7: + result &=(sw->sig->id == 7)? 1 : 0; + break; + } + sw = sw->next; + pos++; + } + +end: + /* Restore the default pre-order definition */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + #endif void SCSigRegisterSignatureOrderingTests(void) @@ -1563,6 +2067,10 @@ void SCSigRegisterSignatureOrderingTests(void) UtRegisterTest("SCSigTestSignatureOrdering04", SCSigTestSignatureOrdering04, 1); UtRegisterTest("SCSigTestSignatureOrdering05", SCSigTestSignatureOrdering05, 1); UtRegisterTest("SCSigTestSignatureOrdering06", SCSigTestSignatureOrdering06, 1); + UtRegisterTest("SCSigTestSignatureOrdering07", SCSigTestSignatureOrdering07, 1); + UtRegisterTest("SCSigTestSignatureOrdering08", SCSigTestSignatureOrdering08, 1); + UtRegisterTest("SCSigTestSignatureOrdering09", SCSigTestSignatureOrdering09, 1); + UtRegisterTest("SCSigTestSignatureOrdering10", SCSigTestSignatureOrdering10, 1); #endif diff --git a/src/detect.c b/src/detect.c index 5f2e2aa8b0..8b94e0656b 100644 --- a/src/detect.c +++ b/src/detect.c @@ -388,6 +388,17 @@ int SigLoadSignatures (DetectEngineCtx *de_ctx, char *sig_file) SCSigOrderSignatures(de_ctx); SCSigSignatureOrderingModuleCleanup(de_ctx); + Signature *s = de_ctx->sig_list; + + /* Assign the unique id of signatures after sorting, + * so the IP Only engine process them in order too */ + uint16_t sig_id = 0; + while (s != NULL) { + s->num = sig_id++; + s = s->next; + } + de_ctx->signum = sig_id; + /* Setup the signature group lookup structure and pattern matchers */ SigGroupBuild(de_ctx); SCReturnInt(0); @@ -416,22 +427,55 @@ int PacketAlertCheck(Packet *p, uint32_t sid) int PacketAlertAppend(DetectEngineThreadCtx *det_ctx, Signature *s, Packet *p) { + int i = 0; + if (p->alerts.cnt == PACKET_ALERT_MAX) return 0; SCLogDebug("sid %"PRIu32"", s->id); - if (s->gid > 1) - p->alerts.alerts[p->alerts.cnt].gid = s->gid; - else - p->alerts.alerts[p->alerts.cnt].gid = 1; - - p->alerts.alerts[p->alerts.cnt].sid = s->id; - p->alerts.alerts[p->alerts.cnt].rev = s->rev; - p->alerts.alerts[p->alerts.cnt].prio = s->prio; - p->alerts.alerts[p->alerts.cnt].msg = s->msg; - p->alerts.alerts[p->alerts.cnt].class_msg = s->class_msg; - p->alerts.alerts[p->alerts.cnt].references = s->references; + /* It should be usually the last, so check it before iterating */ + if (p->alerts.cnt == 0 || (p->alerts.cnt > 0 && + p->alerts.alerts[p->alerts.cnt - 1].num < s->num)) { + /* We just add it */ + if (s->gid > 1) + p->alerts.alerts[p->alerts.cnt].gid = s->gid; + else + p->alerts.alerts[p->alerts.cnt].gid = 1; + + p->alerts.alerts[p->alerts.cnt].num= s->num; + p->alerts.alerts[p->alerts.cnt].action = s->action; + p->alerts.alerts[p->alerts.cnt].sid = s->id; + p->alerts.alerts[p->alerts.cnt].rev = s->rev; + p->alerts.alerts[p->alerts.cnt].prio = s->prio; + p->alerts.alerts[p->alerts.cnt].msg = s->msg; + p->alerts.alerts[p->alerts.cnt].class_msg = s->class_msg; + p->alerts.alerts[p->alerts.cnt].references = s->references; + } else { + /* We need to make room for this s->num + (a bit ugly with mamcpy but we are planning changes here)*/ + for (i = p->alerts.cnt - 1; i >= 0 && p->alerts.alerts[i].num > s->num; i--) { + memcpy(&p->alerts.alerts[i + 1], &p->alerts.alerts[i], sizeof(PacketAlert)); + } + + i++; /* The right place to store the alert */ + + if (s->gid > 1) + p->alerts.alerts[i].gid = s->gid; + else + p->alerts.alerts[i].gid = 1; + + p->alerts.alerts[p->alerts.cnt].num= s->num; + p->alerts.alerts[p->alerts.cnt].action = s->action; + p->alerts.alerts[i].sid = s->id; + p->alerts.alerts[i].rev = s->rev; + p->alerts.alerts[i].prio = s->prio; + p->alerts.alerts[i].msg = s->msg; + p->alerts.alerts[i].class_msg = s->class_msg; + p->alerts.alerts[i].references = s->references; + } + + /* Update the count */ p->alerts.cnt++; SCPerfCounterIncr(det_ctx->counter_alerts, det_ctx->tv->sc_perf_pca); @@ -508,6 +552,7 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh void *alstate = NULL; uint8_t flags = 0; uint32_t cnt = 0; + uint16_t i = 0; SCEnter(); @@ -545,6 +590,10 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh (p->flowflags & FLOW_PKT_TOCLIENT && (p->flow->flags & FLOW_TOCLIENT_IPONLY_SET)))) { /* Get the result of the first IPOnlyMatch() */ + if (p->flow->flags & FLOW_ACTION_PASS) { + /* if it matched a "pass" rule, we have to let it go */ + p->action |= ACTION_PASS; + } if (p->flow->flags & FLOW_ACTION_DROP) p->action |= ACTION_DROP; } else { /* Even without flow we should match the packet src/dst */ @@ -708,9 +757,9 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh fmatch = 1; if (!(s->flags & SIG_FLAG_NOALERT)) { - PacketAlertHandle(de_ctx, det_ctx, s, p); /* set verdict on packet */ p->action |= s->action; + PacketAlertHandle(de_ctx, det_ctx, s, p); } } else { /* reset offset */ @@ -746,9 +795,10 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh if (!(s->flags & SIG_FLAG_NOALERT)) { /* only add once */ if (rmatch == 0) { - PacketAlertHandle(de_ctx, det_ctx, s, p); /* set verdict on packet */ p->action |= s->action; + + PacketAlertHandle(de_ctx, det_ctx, s, p); } } rmatch = fmatch = 1; @@ -760,11 +810,13 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh rmatch = 0; } } + /* Limit the number of times we do this recursive thing. * XXX is this a sane limit? Should it be configurable? */ if (det_ctx->pkt_cnt == 10) break; } while (rmatch); + } else { sm = s->match; @@ -790,12 +842,10 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh if (sm == NULL) { fmatch = 1; if (!(s->flags & SIG_FLAG_NOALERT)) { - - /* set flowbit for this match */ - PacketAlertHandle(de_ctx, det_ctx, s, p); - /* set verdict on packet */ p->action |= s->action; + + PacketAlertHandle(de_ctx, det_ctx, s, p); } } } else { @@ -803,15 +853,31 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh sm = NULL; } } + SCLogDebug("match functions done, sm %p", sm); } } } + /* so now let's iterate the alerts and remove the ones after a pass rule + * matched (if any) */ +end: + SCLogDebug("(p->action & ation pass)) = %"PRIu8, (p->action & ACTION_PASS)); + if (p->action & ACTION_PASS) { + for (; i < p->alerts.cnt; i++) { + SCLogDebug("Sig->num: %"PRIu16, p->alerts.alerts[i].num); + if (p->alerts.alerts[i].action & ACTION_PASS) { + /* Ok, reset the alert cnt to end in the previous of pass + * so we ignore the rest with less prio */ + p->alerts.cnt = i; + break; + } + } + } + /* cleanup pkt specific part of the patternmatcher */ PacketPatternCleanup(th_v, det_ctx); -end: if (p->flow != NULL) { SCMutexLock(&p->flow->m); p->flow->use_cnt--; diff --git a/src/flow.h b/src/flow.h index 1760d93c96..abc6794532 100644 --- a/src/flow.h +++ b/src/flow.h @@ -56,6 +56,8 @@ /** All packets in this flow should be dropped */ #define FLOW_ACTION_DROP 0x0200 +/** All packets in this flow should be accepted */ +#define FLOW_ACTION_PASS 0x0400 /* pkt flow flags */ #define FLOW_PKT_TOSERVER 0x01 diff --git a/src/suricata.c b/src/suricata.c index ba588bd435..11efd072b5 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -42,6 +42,7 @@ #include "util-pool.h" #include "util-byte.h" #include "util-cpu.h" +#include "util-action.h" #include "util-pidfile.h" #include "detect-parse.h" @@ -831,6 +832,7 @@ int main(int argc, char **argv) AppLayerParserRegisterTests(); ThreadMacrosRegisterTests(); UtilSpmSearchRegistertests(); + UtilActionRegisterTests(); SCClassConfRegisterTests(); SCThresholdConfRegisterTests(); #ifdef __SC_CUDA_SUPPORT__ @@ -931,6 +933,8 @@ int main(int argc, char **argv) SCClassConfLoadClassficationConfigFile(de_ctx); + ActionInitConfig(); + if (SigLoadSignatures(de_ctx, sig_file) < 0) { if (sig_file == NULL) { SCLogError(SC_ERR_OPENING_FILE, "Signature file has not been provided"); diff --git a/src/util-action.c b/src/util-action.c new file mode 100644 index 0000000000..c6ed468de5 --- /dev/null +++ b/src/util-action.c @@ -0,0 +1,995 @@ +/** + * Copyright(c) 2009 Open Information Security Foundation. + * \author Pablo Rincon + */ +#include "suricata-common.h" + +#include "action-globals.h" +#include "conf.h" +#include "conf-yaml-loader.h" + +#include "detect.h" +#include "detect-engine.h" +#include "detect-engine-sigorder.h" + +#include "util-unittest.h" +#include "util-action.h" +#include "util-unittest-helper.h" +#include "util-debug.h" + +/* Default order: */ +uint8_t action_order_sigs[4] = {ACTION_PASS, ACTION_DROP, ACTION_REJECT, ACTION_ALERT}; +/* This order can be changed from config */ + +/** + * \brief Return the priority associated to an action (to order sigs + * as specified at config) + * action_order_sigs has this priority by index val + * so action_order_sigs[0] has to be inspected first. + * This function is called from detect-engine-sigorder + * \param action can be one of ACTION_PASS, ACTION_DROP, + * ACTION_REJECT or ACTION_ALERT + * \retval uint8_t the priority (order of this actions) + */ +uint8_t ActionOrderVal(uint8_t action) { + /* reject_both and reject_dst have the same prio as reject */ + if( action & ACTION_REJECT || + action & ACTION_REJECT_BOTH || + action & ACTION_REJECT_BOTH) { + action = ACTION_REJECT; + } + uint8_t i = 0; + for (; i < 4; i++) { + if (action_order_sigs[i] == action) + return i; + } + /* Unknown action, set just a low prio (high val) */ + return 10; +} + +/** + * \brief Return the ACTION_* bit from their ascii value + * \param action can be one of "pass", "drop", + * "reject" or "alert" + * \retval uint8_t can be one of ACTION_PASS, ACTION_DROP, + * ACTION_REJECT or ACTION_ALERT + */ +uint8_t ActionAsciiToFlag(char *action) { + if (strcmp(action,"pass") == 0) + return ACTION_PASS; + if (strcmp(action,"drop") == 0) + return ACTION_DROP; + if (strcmp(action,"reject") == 0) + return ACTION_REJECT; + if (strcmp(action,"alert") == 0) + return ACTION_ALERT; + + return 0; +} + +/** + * \brief Load the action order from config. If none is provided, + * it will be default to ACTION_PASS, ACTION_DROP, + * ACTION_REJECT, ACTION_ALERT (pass has the highest prio) + * \retval none + */ +void ActionInitConfig() { + uint8_t actions_used = 0; + uint8_t action_flag = 0; + uint8_t actions_config[4] = {0, 0, 0, 0}; + int order = 0; + + ConfNode *action_order; + ConfNode *action = NULL; + + /* Let's load the order of actions from the general config */ + action_order = ConfGetNode("action-order"); + if (action_order != NULL) { + TAILQ_FOREACH(action, &action_order->head, next) { + SCLogDebug("Loading action order : %s", action->val); + action_flag = ActionAsciiToFlag(action->val); + if (action_flag == 0) { + SCLogError(SC_ERR_ACTION_ORDER, "action-order, invalid action: \"%s\". Please, use" + " \"pass\",\"drop\",\"alert\",\"reject\". You have" + " to specify all of them, without quotes and without" + " capital letters", action->val); + return; + } + + if (actions_used & action_flag) { + SCLogError(SC_ERR_ACTION_ORDER, "action-order, action already set: \"%s\". Please," + " use \"pass\",\"drop\",\"alert\",\"reject\". You" + " have to specify all of them, without quotes and" + " without capital letters", action->val); + return; + } + + if (order >= 4) { + SCLogError(SC_ERR_ACTION_ORDER, "action-order, you have already specified all the " + "possible actions plus \"%s\". Please, use \"pass\"," + "\"drop\",\"alert\",\"reject\". You have to specify" + " all of them, without quotes and without capital" + " letters", action->val); + return; + } + actions_used |= action_flag; + actions_config[order++] = action_flag; + } + } + if (order < 4) { + SCLogError(SC_ERR_ACTION_ORDER, "action-order, the config didn't specify all of the " + "actions. Please, use \"pass\",\"drop\",\"alert\"," + "\"reject\". You have to specify all of them, without" + " quotes and without capital letters"); + return; + } + + /* Now, it's a valid config. Override the default preset */ + for (order = 0; order < 4; order++) { + action_order_sigs[order] = actions_config[order]; + } +} + +#ifdef UNITTESTS +#include "util-unittest.h" + +/** + * \test Check that we invalidate duplicated actions + * (It should default to pass, drop, reject, alert) + */ +int UtilActionTest01(void) +{ + int res = 1; + char config[] = "\ +%YAML 1.1\n\ +---\n\ +action-order:\n\ + - alert\n\ + - drop\n\ + - reject\n\ + - alert\n"; + + ConfCreateContextBackup(); + ConfInit(); + ConfYamlLoadString(config, strlen(config)); + + ActionInitConfig(); + if (action_order_sigs[0] != ACTION_PASS || + action_order_sigs[1] != ACTION_DROP || + action_order_sigs[2] != ACTION_REJECT || + action_order_sigs[3] != ACTION_ALERT) + { + res = 0; + } + ConfRestoreContextBackup(); + + /* Restore default values */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + return res; +} + +/** + * \test Check that we invalidate with unknown keywords + * (It should default to pass, drop, reject, alert) + */ +int UtilActionTest02(void) +{ + int res = 1; + char config[] = "\ +%YAML 1.1\n\ +---\n\ +action-order:\n\ + - alert\n\ + - drop\n\ + - reject\n\ + - ftw\n"; + + ConfCreateContextBackup(); + ConfInit(); + ConfYamlLoadString(config, strlen(config)); + + ActionInitConfig(); + if (action_order_sigs[0] != ACTION_PASS || + action_order_sigs[1] != ACTION_DROP || + action_order_sigs[2] != ACTION_REJECT || + action_order_sigs[3] != ACTION_ALERT) + { + res = 0; + } + ConfRestoreContextBackup(); + + /* Restore default values */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + return res; +} + +/** + * \test Check that we invalidate if any action is missing + * (It should default to pass, drop, reject, alert) + */ +int UtilActionTest03(void) +{ + int res = 1; + char config[] = "\ +%YAML 1.1\n\ +---\n\ +action-order:\n\ + - alert\n\ + - drop\n\ + - reject\n"; + + ConfCreateContextBackup(); + ConfInit(); + ConfYamlLoadString(config, strlen(config)); + + ActionInitConfig(); + if (action_order_sigs[0] != ACTION_PASS || + action_order_sigs[1] != ACTION_DROP || + action_order_sigs[2] != ACTION_REJECT || + action_order_sigs[3] != ACTION_ALERT) + { + res = 0; + } + ConfRestoreContextBackup(); + + /* Restore default values */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + return res; +} + +/** + * \test Check that we invalidate if any action is missing + * (It should default to pass, drop, reject, alert) + */ +int UtilActionTest04(void) +{ + int res = 1; + char config[] = "\ +%YAML 1.1\n\ +---\n\ +action-order:\n"; + + ConfCreateContextBackup(); + ConfInit(); + ConfYamlLoadString(config, strlen(config)); + + ActionInitConfig(); + if (action_order_sigs[0] != ACTION_PASS || + action_order_sigs[1] != ACTION_DROP || + action_order_sigs[2] != ACTION_REJECT || + action_order_sigs[3] != ACTION_ALERT) + { + res = 0; + } + ConfRestoreContextBackup(); + + /* Restore default values */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + return res; +} + +/** + * \test Check that we invalidate with unknown keywords + * and/or more than the expected + * (It should default to pass, drop, reject, alert) + */ +int UtilActionTest05(void) +{ + int res = 1; + char config[] = "\ +%YAML 1.1\n\ +---\n\ +action-order:\n\ + - alert\n\ + - drop\n\ + - reject\n\ + - pass\n\ + - whatever\n"; + + ConfCreateContextBackup(); + ConfInit(); + ConfYamlLoadString(config, strlen(config)); + + ActionInitConfig(); + if (action_order_sigs[0] != ACTION_PASS || + action_order_sigs[1] != ACTION_DROP || + action_order_sigs[2] != ACTION_REJECT || + action_order_sigs[3] != ACTION_ALERT) + { + res = 0; + } + ConfRestoreContextBackup(); + + /* Restore default values */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + return res; +} + +/** + * \test Check that we load a valid config + */ +int UtilActionTest06(void) +{ + int res = 1; + char config[] = "\ +%YAML 1.1\n\ +---\n\ +action-order:\n\ + - alert\n\ + - drop\n\ + - reject\n\ + - pass\n"; + + ConfCreateContextBackup(); + ConfInit(); + ConfYamlLoadString(config, strlen(config)); + + ActionInitConfig(); + if (action_order_sigs[0] != ACTION_ALERT || + action_order_sigs[1] != ACTION_DROP || + action_order_sigs[2] != ACTION_REJECT || + action_order_sigs[3] != ACTION_PASS) + { + res = 0; + } + ConfRestoreContextBackup(); + + /* Restore default values */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + return res; +} + +/** + * \test Check that we load a valid config + */ +int UtilActionTest07(void) +{ + int res = 1; + char config[] = "\ +%YAML 1.1\n\ +---\n\ +action-order:\n\ + - pass\n\ + - alert\n\ + - drop\n\ + - reject\n"; + + ConfCreateContextBackup(); + ConfInit(); + ConfYamlLoadString(config, strlen(config)); + + ActionInitConfig(); + if (action_order_sigs[0] != ACTION_PASS || + action_order_sigs[1] != ACTION_ALERT || + action_order_sigs[2] != ACTION_DROP || + action_order_sigs[3] != ACTION_REJECT) + { + res = 0; + } + ConfRestoreContextBackup(); + + /* Restore default values */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + return res; +} + +/** + * \test Check that we handle the "pass" action + * correctly at the IP Only engine in the default case + */ +int UtilActionTest08(void) +{ + int res = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + Packet *p[3]; + p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.1", "192.168.1.5", + 80, 41424); + p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + + if (p[0] == NULL || p[1] == NULL ||p[2] == NULL) + goto end; + + char *sigs[3]; + sigs[0]= "alert ip any any -> any any (msg:\"sig 1\"; sid:1;)"; + sigs[1]= "pass ip 192.168.1.1 80 -> any any (msg:\"sig 2\"; sid:2;)"; + sigs[2]= "alert ip any any -> any any (msg:\"sig 3\"; sid:3;)"; + + uint32_t sid[3] = {1, 2, 3}; + + uint32_t results[3][3] = { + {1, 0, 1}, + {0, 0, 0}, + {1, 0, 1} }; + /* This means that with the second packet, the results will be + * all ({0,0,0}) since, we should match the "pass" rule first + */ + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto cleanup; + de_ctx->flags |= DE_QUIET; + + if (UTHAppendSigs(de_ctx, sigs, 3) == 0) + goto cleanup; + + SCSigRegisterSignatureOrderingFuncs(de_ctx); + SCSigOrderSignatures(de_ctx); + + Signature *s = de_ctx->sig_list; + uint16_t sig_id = 0; + /* Assing the internal id after sorting, so the IP Only engine + * process them in order too */ + while (s != NULL) { + s->num = sig_id++; + s = s->next; + } + + de_ctx->signum = sig_id; + + res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3); + +cleanup: + UTHFreePackets(p, 3); + + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + } + +end: + return res; +} + +/** + * \test Check that we handle the "pass" action + * correctly at the IP Only engine with more + * prio to drop + */ +int UtilActionTest09(void) +{ + int res = 1; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + Packet *p[3]; + + action_order_sigs[0] = ACTION_DROP; + action_order_sigs[1] = ACTION_PASS; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + + p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.1", "192.168.1.5", + 80, 41424); + p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + + if (p[0] == NULL || p[1] == NULL ||p[2] == NULL) + goto end; + + char *sigs[3]; + sigs[0]= "alert ip any any -> any any (msg:\"sig 1\"; sid:1;)"; + sigs[1]= "pass ip 192.168.1.1 80 -> any any (msg:\"sig 2\"; sid:2;)"; + sigs[2]= "drop ip any any -> any any (msg:\"sig 3\"; sid:3;)"; + + uint32_t sid[3] = {1, 2, 3}; + + uint32_t results[3][3] = { + {1, 0, 1}, + {0, 0, 1}, + {1, 0, 1} }; + /* This means that with the second packet, the results will be + * all ({0,0,1}) since, we should match the "drop" rule first. + * Later the "pass" rule will avoid the "alert" rule match + */ + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto cleanup; + de_ctx->flags |= DE_QUIET; + + if (UTHAppendSigs(de_ctx, sigs, 3) == 0) + goto cleanup; + + SCSigRegisterSignatureOrderingFuncs(de_ctx); + SCSigOrderSignatures(de_ctx); + + Signature *s = de_ctx->sig_list; + uint16_t sig_id = 0; + /* Assing the internal id after sorting, so the IP Only engine + * process them in order too */ + while (s != NULL) { + s->num = sig_id++; + s = s->next; + } + + de_ctx->signum = sig_id; + + res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3); + +cleanup: + UTHFreePackets(p, 3); + + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + } + +end: + /* Restore default values */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + return res; +} + +/** + * \test Check that we handle the "pass" action + * correctly at the detection engine in the default case + */ +int UtilActionTest10(void) +{ + int res = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + uint8_t *buf2 = (uint8_t *)"wo!"; + uint16_t buflen2 = strlen((char *)buf2); + Packet *p[3]; + p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[1] = UTHBuildPacketReal((uint8_t *)buf2, buflen2, IPPROTO_TCP, + "192.168.1.1", "192.168.1.5", + 80, 41424); + p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + + if (p[0] == NULL || p[1] == NULL ||p[2] == NULL) + goto end; + + char *sigs[3]; + sigs[0]= "alert ip any any -> any any (msg:\"sig 1\"; content:\"Hi all\"; sid:1;)"; + sigs[1]= "pass ip any any -> any any (msg:\"sig 2\"; content:\"wo\"; sid:2;)"; + sigs[2]= "alert ip any any -> any any (msg:\"sig 3\"; content:\"Hi all\"; sid:3;)"; + + uint32_t sid[3] = {1, 2, 3}; + + uint32_t results[3][3] = { + {1, 0, 1}, + {0, 0, 0}, + {1, 0, 1} }; + /* This means that with the second packet, the results will be + * all ({0,0,0}) since, we should match the "pass" rule first + */ + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto cleanup; + de_ctx->flags |= DE_QUIET; + + if (UTHAppendSigs(de_ctx, sigs, 3) == 0) + goto cleanup; + + SCSigRegisterSignatureOrderingFuncs(de_ctx); + SCSigOrderSignatures(de_ctx); + + Signature *s = de_ctx->sig_list; + uint16_t sig_id = 0; + /* Assing the internal id after sorting, so the IP Only engine + * process them in order too */ + while (s != NULL) { + s->num = sig_id++; + s = s->next; + } + + de_ctx->signum = sig_id; + + res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3); + +cleanup: + UTHFreePackets(p, 3); + + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + } + +end: + return res; +} + +/** + * \test Check that we handle the "pass" action + * correctly at the detection engine with more + * prio to drop + */ +int UtilActionTest11(void) +{ + int res = 1; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + uint8_t *buf2 = (uint8_t *)"Hi all wo!"; + uint16_t buflen2 = strlen((char *)buf2); + Packet *p[3]; + + action_order_sigs[0] = ACTION_DROP; + action_order_sigs[1] = ACTION_PASS; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + + p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[1] = UTHBuildPacketReal((uint8_t *)buf2, buflen2, IPPROTO_TCP, + "192.168.1.1", "192.168.1.5", + 80, 41424); + p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + + if (p[0] == NULL || p[1] == NULL ||p[2] == NULL) + goto end; + + char *sigs[3]; + sigs[0]= "alert tcp any any -> any any (msg:\"sig 1\"; content:\"Hi all\"; sid:1;)"; + sigs[1]= "pass tcp any any -> any any (msg:\"sig 2\"; content:\"wo\"; sid:2;)"; + sigs[2]= "drop tcp any any -> any any (msg:\"sig 3\"; content:\"Hi all\"; sid:3;)"; + + uint32_t sid[3] = {1, 2, 3}; + + uint32_t results[3][3] = { + {1, 0, 1}, + {0, 0, 1}, + {1, 0, 1} }; + /* This means that with the second packet, the results will be + * all ({0,0,1}) since, we should match the "drop" rule first. + * Later the "pass" rule will avoid the "alert" rule match + */ + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto cleanup; + de_ctx->flags |= DE_QUIET; + + if (UTHAppendSigs(de_ctx, sigs, 3) == 0) + goto cleanup; + + SCSigRegisterSignatureOrderingFuncs(de_ctx); + SCSigOrderSignatures(de_ctx); + + Signature *s = de_ctx->sig_list; + uint16_t sig_id = 0; + /* Assing the internal id after sorting, so the IP Only engine + * process them in order too */ + while (s != NULL) { + s->num = sig_id++; + s = s->next; + } + + de_ctx->signum = sig_id; + + res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3); + +cleanup: + UTHFreePackets(p, 3); + + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + } + +end: + /* Restore default values */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + return res; +} + +/** + * \test Check that we handle the "pass" action + * correctly at the detection engine in the default case + */ +int UtilActionTest12(void) +{ + int res = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + Packet *p[3]; + p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.1", "192.168.1.5", + 80, 41424); + p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + + if (p[0] == NULL || p[1] == NULL ||p[2] == NULL) + goto end; + + char *sigs[3]; + sigs[0]= "alert ip any any -> any any (msg:\"sig 1\"; sid:1;)"; + sigs[1]= "pass ip any any -> any any (msg:\"Testing normal 2\"; sid:2;)"; + sigs[2]= "alert ip any any -> any any (msg:\"sig 3\"; sid:3;)"; + + uint32_t sid[3] = {1, 2, 3}; + + uint32_t results[3][3] = { + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0} }; + /* All should match the 3 sigs, but the action pass has prio */ + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto cleanup; + de_ctx->flags |= DE_QUIET; + + if (UTHAppendSigs(de_ctx, sigs, 3) == 0) + goto cleanup; + + SCSigRegisterSignatureOrderingFuncs(de_ctx); + SCSigOrderSignatures(de_ctx); + + Signature *s = de_ctx->sig_list; + uint16_t sig_id = 0; + /* Assing the internal id after sorting, so the IP Only engine + * process them in order too */ + while (s != NULL) { + s->num = sig_id++; + s = s->next; + } + + de_ctx->signum = sig_id; + + res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3); + +cleanup: + UTHFreePackets(p, 3); + + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + } + +end: + return res; +} + +/** + * \test Check that we handle the "pass" action + * correctly at the detection engine with more + * prio to drop + */ +int UtilActionTest13(void) +{ + int res = 1; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + Packet *p[3]; + + action_order_sigs[0] = ACTION_DROP; + action_order_sigs[1] = ACTION_PASS; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + + p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.1", "192.168.1.5", + 80, 41424); + p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + + if (p[0] == NULL || p[1] == NULL ||p[2] == NULL) + goto end; + + char *sigs[3]; + sigs[0]= "alert tcp any any -> any any (msg:\"sig 1\"; content:\"Hi all\"; sid:1;)"; + sigs[1]= "pass tcp any any -> any any (msg:\"sig 2\"; content:\"Hi all\"; sid:2;)"; + sigs[2]= "drop tcp any any -> any any (msg:\"sig 3\"; content:\"Hi all\"; sid:3;)"; + + uint32_t sid[3] = {1, 2, 3}; + + uint32_t results[3][3] = { + {0, 0, 1}, + {0, 0, 1}, + {0, 0, 1} }; + /* All the patckets should match the 3 sigs. As drop has more + * priority than pass, it should alert on each packet */ + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto cleanup; + de_ctx->flags |= DE_QUIET; + + if (UTHAppendSigs(de_ctx, sigs, 3) == 0) + goto cleanup; + + SCSigRegisterSignatureOrderingFuncs(de_ctx); + SCSigOrderSignatures(de_ctx); + + Signature *s = de_ctx->sig_list; + uint16_t sig_id = 0; + /* Assing the internal id after sorting, so the IP Only engine + * process them in order too */ + while (s != NULL) { + s->num = sig_id++; + s = s->next; + } + + de_ctx->signum = sig_id; + + res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3); + +cleanup: + UTHFreePackets(p, 3); + + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + } + +end: + /* Restore default values */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + return res; +} + +/** + * \test Check that we handle the "pass" action + * correctly at the detection engine with more + * prio to drop and alert + */ +int UtilActionTest14(void) +{ + int res = 1; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint16_t buflen = strlen((char *)buf); + Packet *p[3]; + + action_order_sigs[0] = ACTION_DROP; + action_order_sigs[1] = ACTION_ALERT; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_PASS; + + p[0] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[1] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.1", "192.168.1.5", + 80, 41424); + p[2] = UTHBuildPacketReal((uint8_t *)buf, buflen, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + + if (p[0] == NULL || p[1] == NULL ||p[2] == NULL) + goto end; + + char *sigs[3]; + sigs[0]= "alert tcp any any -> any any (msg:\"sig 1\"; content:\"Hi all\"; sid:1;)"; + sigs[1]= "pass tcp any any -> any any (msg:\"sig 2\"; content:\"Hi all\"; sid:2;)"; + sigs[2]= "drop tcp any any -> any any (msg:\"sig 3\"; content:\"Hi all\"; sid:3;)"; + + uint32_t sid[3] = {1, 2, 3}; + + uint32_t results[3][3] = { + {1, 0, 1}, + {1, 0, 1}, + {1, 0, 1} }; + /* All the patckets should match the 3 sigs. As drop + * and alert have more priority than pass, both should + * alert on each packet */ + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto cleanup; + de_ctx->flags |= DE_QUIET; + + if (UTHAppendSigs(de_ctx, sigs, 3) == 0) + goto cleanup; + + SCSigRegisterSignatureOrderingFuncs(de_ctx); + SCSigOrderSignatures(de_ctx); + + Signature *s = de_ctx->sig_list; + uint16_t sig_id = 0; + /* Assing the internal id after sorting, so the IP Only engine + * process them in order too */ + while (s != NULL) { + s->num = sig_id++; + s = s->next; + } + + de_ctx->signum = sig_id; + + res = UTHMatchPacketsWithResults(de_ctx, p, 3, sid, (uint32_t *) results, 3); + +cleanup: + UTHFreePackets(p, 3); + + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + } + +end: + /* Restore default values */ + action_order_sigs[0] = ACTION_PASS; + action_order_sigs[1] = ACTION_DROP; + action_order_sigs[2] = ACTION_REJECT; + action_order_sigs[3] = ACTION_ALERT; + return res; +} + +#endif + +/* Register unittests */ +void UtilActionRegisterTests(void) { +#ifdef UNITTESTS + /* Generic tests */ + UtRegisterTest("UtilActionTest01", UtilActionTest01, 1); + UtRegisterTest("UtilActionTest02", UtilActionTest02, 1); + UtRegisterTest("UtilActionTest02", UtilActionTest02, 1); + UtRegisterTest("UtilActionTest03", UtilActionTest03, 1); + UtRegisterTest("UtilActionTest04", UtilActionTest04, 1); + UtRegisterTest("UtilActionTest05", UtilActionTest05, 1); + UtRegisterTest("UtilActionTest06", UtilActionTest06, 1); + UtRegisterTest("UtilActionTest07", UtilActionTest07, 1); + UtRegisterTest("UtilActionTest08", UtilActionTest08, 1); + UtRegisterTest("UtilActionTest09", UtilActionTest09, 1); + UtRegisterTest("UtilActionTest10", UtilActionTest10, 1); + UtRegisterTest("UtilActionTest11", UtilActionTest11, 1); + UtRegisterTest("UtilActionTest12", UtilActionTest12, 1); + UtRegisterTest("UtilActionTest13", UtilActionTest13, 1); + UtRegisterTest("UtilActionTest15", UtilActionTest13, 1); +#endif +} diff --git a/src/util-action.h b/src/util-action.h new file mode 100644 index 0000000000..dfe0495d2d --- /dev/null +++ b/src/util-action.h @@ -0,0 +1,12 @@ +/** + * Copyright(c) 2009 Open Information Security Foundation. + * \author Pablo Rincon + */ +#ifndef __ACTION_ORDER_H__ +#define __ACTION_ORDER_H__ +#include "suricata-common.h" +void ActionInitConfig(); +uint8_t ActionOrderVal(uint8_t); +void UtilActionRegisterTests(void); + +#endif /* __ACTION_ORDER_H__ */ diff --git a/src/util-error.c b/src/util-error.c index 065977438f..e3139e5980 100644 --- a/src/util-error.c +++ b/src/util-error.c @@ -42,6 +42,7 @@ const char * SCErrorToString(SCError err) switch (err) { CASE_CODE (SC_OK); CASE_CODE (SC_ERR_MEM_ALLOC); + CASE_CODE (SC_ERR_ACTION_ORDER); CASE_CODE (SC_ERR_PCRE_MATCH); CASE_CODE (SC_ERR_PCRE_GET_SUBSTRING); CASE_CODE (SC_ERR_PCRE_COMPILE); diff --git a/src/util-error.h b/src/util-error.h index a884999a4b..faed8bbe0b 100644 --- a/src/util-error.h +++ b/src/util-error.h @@ -30,6 +30,7 @@ typedef enum { SC_OK, SC_ERR_MEM_ALLOC, SC_ERR_PCRE_MATCH, + SC_ERR_ACTION_ORDER, SC_ERR_PCRE_GET_SUBSTRING, SC_ERR_PCRE_COMPILE, SC_ERR_PCRE_STUDY, diff --git a/suricata.yaml b/suricata.yaml index 9843066392..52366007de 100644 --- a/suricata.yaml +++ b/suricata.yaml @@ -5,6 +5,15 @@ # conservative 50. #max-pending-packets: 50 +# Set the order of alerts bassed on actions +# The default order is pass, drop, reject, alert +action-order: + - pass + - drop + - reject + - alert + + # The default logging directory. Any log or output file will be # placed here if its not specified with a full path name. This can be # overridden with the -l command line parameter.