diff --git a/src/Makefile.am b/src/Makefile.am index cf65239eb1..4242761c1e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -53,6 +53,7 @@ detect-recursive.c detect-recursive.h \ detect-rawbytes.c detect-rawbytes.h \ detect-bytetest.c detect-bytetest.h \ detect-bytejump.c detect-bytejump.h \ +detect-ipproto.c detect-ipproto.h \ detect-within.c detect-within.h \ detect-distance.c detect-distance.h \ detect-offset.c detect-offset.h \ diff --git a/src/detect-engine-proto.c b/src/detect-engine-proto.c index 280d855644..79811a0cd0 100644 --- a/src/detect-engine-proto.c +++ b/src/detect-engine-proto.c @@ -11,6 +11,7 @@ #include "flow-var.h" #include "util-cidr.h" +#include "util-byte.h" #include "util-unittest.h" #include "detect-engine-siggroup.h" @@ -69,15 +70,31 @@ int DetectProtoParse(DetectProto *dp, char *str) { } else if (strcasecmp(str, "icmp") == 0) { proto = IPPROTO_ICMP; dp->proto[proto / 8] |= 1 << (proto % 8); - } else if (strcasecmp(str, "ip") == 0) { + } else if (strcasecmp(str,"ip") == 0) { + /* Proto "ip" is treated as an "any" */ dp->flags |= DETECT_PROTO_ANY; - memset(&dp->proto, 0xFF, sizeof(dp->proto)); } else { - proto = atoi(str); - dp->proto[proto / 8] |= 1 << (proto % 8); + uint8_t proto_u8; /* Used to avoid sign extension */ + + /* Extract out a 0-256 value with validation checks */ + if (ByteExtractStringUint8(&proto_u8, 10, 0, str) == -1) { + // XXX + goto error; + } + proto = (int)proto_u8; + + /* Proto 0 is the same as "ip" above */ + if (proto == IPPROTO_IP) { + dp->flags |= DETECT_PROTO_ANY; + } else { + dp->proto[proto / 8] |= 1<<(proto % 8); + } } return 0; + +error: + return -1; } /* XXX remove */ @@ -124,6 +141,19 @@ static int ProtoTestParse03 (void) { return 0; } + +static int ProtoTestParse04 (void) { + DetectProto dp; + memset(&dp,0,sizeof(DetectProto)); + + /* Check for a bad number */ + int r = DetectProtoParse(&dp, "4242"); + if (r == -1) { + return 1; + } + + return 0; +} #endif /* UNITTESTS */ @@ -132,6 +162,7 @@ void DetectProtoTests(void) { UtRegisterTest("ProtoTestParse01", ProtoTestParse01, 1); UtRegisterTest("ProtoTestParse02", ProtoTestParse02, 1); UtRegisterTest("ProtoTestParse03", ProtoTestParse03, 1); + UtRegisterTest("ProtoTestParse04", ProtoTestParse04, 1); #endif /* UNITTESTS */ } diff --git a/src/detect-ipproto.c b/src/detect-ipproto.c new file mode 100644 index 0000000000..8edecd1ae5 --- /dev/null +++ b/src/detect-ipproto.c @@ -0,0 +1,513 @@ +/* Copyright (c) 2009 Open Information Security Foundation */ + +/** + * \file + * \author Brian Rectanus + */ + +#include + +#include "eidps-common.h" +#include "debug.h" +#include "decode.h" +#include "detect.h" + +#include "detect-ipproto.h" + +#include "util-byte.h" +#include "util-unittest.h" + +/** + * \brief Regex for parsing our options + */ +#define PARSE_REGEX "^\\s*" \ + "([!<>]?)" \ + "\\s*([^\\s]+)" \ + "\\s*$" + +static pcre *parse_regex; +static pcre_extra *parse_regex_study; + +int DetectIPProtoSetup(DetectEngineCtx *de_ctx, Signature *s, + SigMatch *m, char *optstr); +DetectIPProtoData *DetectIPProtoParse(const char *optstr); +void DetectIPProtoRegisterTests(void); + +void DetectIPProtoRegister (void) { + const char *eb; + int eo; + int opts = 0; + + sigmatch_table[DETECT_IPPROTO].name = "ip_proto"; + sigmatch_table[DETECT_IPPROTO].Match = NULL; + sigmatch_table[DETECT_IPPROTO].Setup = DetectIPProtoSetup; + sigmatch_table[DETECT_IPPROTO].Free = NULL; + sigmatch_table[DETECT_IPPROTO].RegisterTests = DetectIPProtoRegisterTests; + + parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL); + if(parse_regex == NULL) + { + printf("DetectIPProtoRegister: pcre compile of \"%s\" failed at offset %" PRId32 ": %s\n", PARSE_REGEX, eo, eb); + goto error; + } + + parse_regex_study = pcre_study(parse_regex, 0, &eb); + if(eb != NULL) + { + printf("DetectIPProtoRegister: pcre study failed: %s\n", eb); + goto error; + } + return; + +error: + /* XXX */ + return; +} + +/** + * \internal + * \brief Parse ip_proto options string. + * + * \param optstr Options string to parse + * + * \return New ip_proto data structure + */ +DetectIPProtoData *DetectIPProtoParse(const char *optstr) +{ + DetectIPProtoData *data = NULL; + char *args[9] = { NULL, NULL }; +#define MAX_SUBSTRINGS 30 + int ret = 0, res = 0; + int ov[MAX_SUBSTRINGS]; + int i; + const char *str_ptr; + + /* Execute the regex and populate args with captures. */ + ret = pcre_exec(parse_regex, parse_regex_study, optstr, + strlen(optstr), 0, 0, ov, MAX_SUBSTRINGS); + if (ret != 3) { + printf("DetectIPProtoParse: parse error, ret %" PRId32 + ", string %s\n", ret, optstr); + goto error; + } + for (i = 0; i < (ret - 1); i++) { + res = pcre_get_substring((char *)optstr, ov, MAX_SUBSTRINGS, + i + 1, &str_ptr); + if (res < 0) { + printf("DetectIPProtoParse: pcre_get_substring failed " + "for arg %d\n", i + 1); + goto error; + } + args[i] = (char *)str_ptr; + } + + /* Initialize the data */ + data = malloc(sizeof(DetectIPProtoData)); + if (data == NULL) { + printf("DetectIPProtoParse: malloc failed\n"); + goto error; + } + data->op = DETECT_IPPROTO_OP_EQ; + data->proto = 0; + + /* Operator */ + if (*(args[0]) != '\0') { + data->op = *(args[0]); + } + + /* Protocol name/number */ + if (!isdigit(*(args[1]))) { + struct protoent *pent = getprotobyname(args[1]); + if (pent == NULL) { + printf("DetectIPProtoParse: Malformed protocol name: %s\n", str_ptr); + goto error; + } + data->proto = (uint8_t)pent->p_proto; + } + else { + if (ByteExtractStringUint8(&data->proto, 10, 0, args[1]) <= 0) { + printf("DetectIPProtoParse: Malformed protocol number: %s\n", str_ptr); + goto error; + } + } + + for (i = 0; i < (ret - 1); i++){ + if (args[i] != NULL) free(args[i]); + } + return data; + +error: + for (i = 0; i < (ret - 1); i++){ + if (args[i] != NULL) free(args[i]); + } + if (data != NULL) free(data); + return NULL; +} + +/** + * \internal + * \brief Setup ip_proto keyword. + * + * \param de_ctx Detection engine context + * \param s Signature + * \param m Signature match + * \param optstr Options string + * + * \return Non-zero on error + */ +int DetectIPProtoSetup(DetectEngineCtx *de_ctx, Signature *s, + SigMatch *m, char *optstr) +{ + DetectIPProtoData *data = NULL; + int i; + + //printf("DetectIPProtoSetup: \'%s\'\n", optstr); + + data = DetectIPProtoParse((const char *)optstr); + if (data == NULL) goto error; + + s->proto.flags &= ~DETECT_PROTO_ANY; + switch (data->op) { + case DETECT_IPPROTO_OP_EQ: + s->proto.proto[data->proto/8] |= 1 << (data->proto%8); + break; + case DETECT_IPPROTO_OP_GT: + s->proto.proto[data->proto/8] |= 0xff << (data->proto%8); + for (i = (data->proto/8) + 1; i < (256/8); i++) { + s->proto.proto[i] = 0xff; + } + break; + case DETECT_IPPROTO_OP_LT: + for (i = 0; i < (data->proto/8); i++) { + s->proto.proto[i] = 0xff; + } + s->proto.proto[data->proto/8] |= ~(0xff << (data->proto%8)); + break; + case DETECT_IPPROTO_OP_NOT: + s->proto.proto[data->proto/8] &= ~(1 << (data->proto%8)); + break; + } +#if DEBUG + printf("op='%c' bits=\"", data->op); + for (i = 0; i < (256/8); i++) { + printf("%02x", s->proto.proto[i]); + } + printf("\"\n"); +#endif + + return 0; + +error: + if (data != NULL) free(data); + return -1; +} + + +/* UNITTESTS */ +#ifdef UNITTESTS + +#include "detect-engine.h" +#include "detect-parse.h" + +static int DetectIPProtoInitTest(DetectEngineCtx **de_ctx, Signature **sig, DetectIPProtoData **data, const char *str) { + char fullstr[1024]; + int result = 0; + + *de_ctx = NULL; + *sig = NULL; + + if (snprintf(fullstr, 1024, "alert ip any any -> any any (msg:\"IPProto test\"; ip_proto:%s; sid:1;)", str) >= 1024) { + goto end; + } + + *de_ctx = DetectEngineCtxInit(); + if (*de_ctx == NULL) { + goto end; + } + + (*de_ctx)->flags |= DE_QUIET; + + (*de_ctx)->sig_list = SigInit(*de_ctx, fullstr); + if ((*de_ctx)->sig_list == NULL) { + goto end; + } + + *sig = (*de_ctx)->sig_list; + if ((*sig)->proto.flags & DETECT_PROTO_ANY) { + goto end; + } + + *data = DetectIPProtoParse(str); + + result = 1; + +end: + return result; +} + +/** + * \test DetectIPProtoTestParse01 is a test for an invalid proto number + */ +int DetectIPProtoTestParse01(void) { + int result = 0; + DetectIPProtoData *data = NULL; + data = DetectIPProtoParse("999"); + if (data == NULL) { + result = 1; + } + + return result; +} + +/** + * \test DetectIPProtoTestParse02 is a test for an invalid proto name + */ +int DetectIPProtoTestParse02(void) { + int result = 0; + DetectIPProtoData *data = NULL; + data = DetectIPProtoParse("foobarbooeek"); + if (data == NULL) { + result = 1; + } + + return result; +} + +/** + * \test DetectIPProtoTestSetup01 is a test for a protocol number + */ +int DetectIPProtoTestSetup01(void) { + DetectIPProtoData *data = NULL; + Signature *sig = NULL; + DetectEngineCtx *de_ctx = NULL; + int result = 0; + int i; + + result = DetectIPProtoInitTest(&de_ctx, &sig, &data, "14"); + if (result == 0) { + goto end; + } + + result = 0; + + if (data == NULL) { + goto cleanup; + } + + if ( (data->op != DETECT_IPPROTO_OP_EQ) + || (data->proto != 14)) + { + goto cleanup; + } + + /* The 6th bit is the only one that should be set */ + if (sig->proto.proto[1] != 0x40) { + goto cleanup; + } + for (i = 2; i < 256/8; i++) { + if (sig->proto.proto[i] != 0) { + goto cleanup; + } + } + + result = 1; + +cleanup: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); +end: + return result; +} + +/** + * \test DetectIPProtoTestSetup02 is a test for a protocol name + */ +int DetectIPProtoTestSetup02(void) { + DetectIPProtoData *data = NULL; + Signature *sig = NULL; + DetectEngineCtx *de_ctx = NULL; + int result = 0; + + result = DetectIPProtoInitTest(&de_ctx, &sig, &data, "tcp"); + if (result == 0) { + goto end; + } + + result = 0; + + if (data == NULL) { + goto cleanup; + } + + if ( (data->op != DETECT_IPPROTO_OP_EQ) + || (data->proto != 6)) + { + goto cleanup; + } + + /* The 6th bit is the only one that should be set */ + if (sig->proto.proto[0] != 0x40) { + goto cleanup; + } + + result = 1; + +cleanup: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); +end: + return result; +} + +/** + * \test DetectIPProtoTestSetup03 is a test for a < operator + */ +int DetectIPProtoTestSetup03(void) { + DetectIPProtoData *data = NULL; + Signature *sig = NULL; + DetectEngineCtx *de_ctx = NULL; + int result = 0; + + result = DetectIPProtoInitTest(&de_ctx, &sig, &data, "<14"); + if (result == 0) { + printf("ERR1\n"); + goto end; + } + + result = 0; + + if (data == NULL) { + goto cleanup; + } + + if ( (data->op != DETECT_IPPROTO_OP_LT) + || (data->proto != 14)) + { + goto cleanup; + } + + if ( (sig->proto.proto[0] != 0xff) + || (sig->proto.proto[1] != 0x3f)) + { + goto cleanup; + } + + result = 1; + +cleanup: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); +end: + return result; +} + +/** + * \test DetectIPProtoTestSetup04 is a test for a > operator + */ +int DetectIPProtoTestSetup04(void) { + DetectIPProtoData *data = NULL; + Signature *sig = NULL; + DetectEngineCtx *de_ctx = NULL; + int result = 0; + int i; + + result = DetectIPProtoInitTest(&de_ctx, &sig, &data, ">14"); + if (result == 0) { + goto end; + } + + result = 0; + + if (data == NULL) { + goto cleanup; + } + + if ( (data->op != DETECT_IPPROTO_OP_GT) + || (data->proto != 14)) + { + goto cleanup; + } + + if (sig->proto.proto[1] != 0xc0) { + goto cleanup; + } + for (i = 2; i < 256/8; i++) { + if (sig->proto.proto[i] != 0xff) { + goto cleanup; + } + } + + result = 1; + +cleanup: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); +end: + return result; +} + +/** + * \test DetectIPProtoTestSetup05 is a test for a ! operator + */ +int DetectIPProtoTestSetup05(void) { + DetectIPProtoData *data = NULL; + Signature *sig = NULL; + DetectEngineCtx *de_ctx = NULL; + int result = 0; + int i; + + result = DetectIPProtoInitTest(&de_ctx, &sig, &data, "!14"); + if (result == 0) { + goto end; + } + + result = 0; + + if (data == NULL) { + goto cleanup; + } + + if ( (data->op != DETECT_IPPROTO_OP_NOT) + || (data->proto != 14)) + { + goto cleanup; + } + + for (i = 1; i < 256/8; i++) { + if (sig->proto.proto[i] != 0) { + goto cleanup; + } + } + + result = 1; + +cleanup: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); +end: + return result; +} + +#endif /* UNITTESTS */ + +/** + * \internal + * \brief Register ip_proto tests. + */ +void DetectIPProtoRegisterTests(void) { +#ifdef UNITTESTS + UtRegisterTest("DetectIPProtoTestParse01", DetectIPProtoTestParse01, 1); + UtRegisterTest("DetectIPProtoTestParse02", DetectIPProtoTestParse02, 1); + UtRegisterTest("DetectIPProtoTestSetup01", DetectIPProtoTestSetup01, 1); + UtRegisterTest("DetectIPProtoTestSetup02", DetectIPProtoTestSetup02, 1); + UtRegisterTest("DetectIPProtoTestSetup03", DetectIPProtoTestSetup03, 1); + UtRegisterTest("DetectIPProtoTestSetup04", DetectIPProtoTestSetup04, 1); + UtRegisterTest("DetectIPProtoTestSetup05", DetectIPProtoTestSetup05, 1); +#endif /* UNITTESTS */ +} + diff --git a/src/detect-ipproto.h b/src/detect-ipproto.h new file mode 100644 index 0000000000..8b39e70233 --- /dev/null +++ b/src/detect-ipproto.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2009 Open Information Security Foundation */ + +/** + * \file + * \author Brian Rectanus + */ + +#ifndef __DETECT_IPPROTO_H__ +#define __DETECT_IPPROTO_H__ + +/** IPProto Operators */ +#define DETECT_IPPROTO_OP_EQ '=' /**< "equals" operator (default) */ +#define DETECT_IPPROTO_OP_NOT '!' /**< "not" operator */ +#define DETECT_IPPROTO_OP_LT '<' /**< "less than" operator */ +#define DETECT_IPPROTO_OP_GT '>' /**< "greater than" operator */ + +/** ip_proto data */ +typedef struct DetectIPProtoData_ { + uint8_t op; /**< Operator used to compare */ + uint8_t proto; /**< Protocol used to compare */ +} DetectIPProtoData; + +/* prototypes */ + +/** + * \brief Registration function for ip_proto keyword. + */ +void DetectIPProtoRegister (void); + +#endif /* __DETECT_IPPROTO_H__ */ + diff --git a/src/detect.c b/src/detect.c index 5fac1db5ba..0519e4a100 100644 --- a/src/detect.c +++ b/src/detect.c @@ -28,6 +28,7 @@ #include "detect-rawbytes.h" #include "detect-bytetest.h" #include "detect-bytejump.h" +#include "detect-ipproto.h" #include "detect-within.h" #include "detect-distance.h" #include "detect-offset.h" @@ -2550,6 +2551,7 @@ void SigTableSetup(void) { DetectRawbytesRegister(); DetectBytetestRegister(); DetectBytejumpRegister(); + DetectIPProtoRegister(); DetectWithinRegister(); DetectDistanceRegister(); DetectOffsetRegister(); @@ -6403,6 +6405,96 @@ end: return result; } +static int SigTest41Real (int mpm_type) { + uint8_t *buf = (uint8_t *) + "GET /one/ HTTP/1.1\r\n" + "Host: one.example.org\r\n" + "\r\n"; + uint16_t buflen = strlen((char *)buf); + Packet p; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + int result = 0; + + memset(&th_v, 0, sizeof(th_v)); + memset(&p, 0, sizeof(p)); + p.src.family = AF_INET; + p.dst.family = AF_INET; + p.payload = buf; + p.payload_len = buflen; + p.proto = IPPROTO_TCP; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + + //de_ctx->flags |= DE_QUIET; + + s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Not tcp\"; ip_proto:!tcp; content:\"GET \"; sid:1;)"); + if (s == NULL) { + goto end; + } + + s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Less than 7\"; content:\"GET \"; ip_proto:<7; sid:2;)"); + if (s == NULL) { + goto end; + } + + s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Greater than 5\"; content:\"GET \"; ip_proto:>5; sid:3;)"); + if (s == NULL) { + goto end; + } + + s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Equals tcp\"; content:\"GET \"; ip_proto:tcp; sid:4;)"); + if (s == NULL) { + goto end; + } + + SigGroupBuild(de_ctx); + PatternMatchPrepare(mpm_ctx, mpm_type); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); + if (PacketAlertCheck(&p, 1)) { + printf("sid 1 alerted, but should not have: "); + goto cleanup; + } else if (PacketAlertCheck(&p, 2) == 0) { + printf("sid 2 did not alert, but should have: "); + goto cleanup; + } else if (PacketAlertCheck(&p, 3) == 0) { + printf("sid 3 did not alert, but should have: "); + goto cleanup; + } else if (PacketAlertCheck(&p, 4) == 0) { + printf("sid 4 did not alert, but should have: "); + goto cleanup; + } + + result = 1; + +cleanup: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + PatternMatchDestroy(mpm_ctx); + DetectEngineCtxFree(de_ctx); + +end: + return result; +} + +static int SigTest41B2g (void) { + return SigTest41Real(MPM_B2G); +} +static int SigTest41B3g (void) { + return SigTest41Real(MPM_B3G); +} +static int SigTest41Wm (void) { + return SigTest41Real(MPM_WUMANBER); +} + #endif /* UNITTESTS */ void SigRegisterTests(void) { @@ -6554,6 +6646,9 @@ void SigRegisterTests(void) { UtRegisterTest("SigTest40SignatureIsIPOnly02", SigTest40IPOnly02, 1); UtRegisterTest("SigTest40SignatureIsIPOnly03", SigTest40IPOnly03, 1); + UtRegisterTest("SigTest41B2g -- ip_proto test", SigTest41B2g, 1); + UtRegisterTest("SigTest41B3g -- ip_proto test", SigTest41B3g, 1); + UtRegisterTest("SigTest41Wm -- ip_proto test", SigTest41Wm, 1); #endif /* UNITTESTS */ } diff --git a/src/detect.h b/src/detect.h index e35a64004f..dde1cb3f14 100644 --- a/src/detect.h +++ b/src/detect.h @@ -410,6 +410,7 @@ enum { DETECT_RAWBYTES, DETECT_BYTETEST, DETECT_BYTEJUMP, + DETECT_IPPROTO, DETECT_FLOW, DETECT_WINDOW, DETECT_ISDATAAT,