From f1f7df076680f520d0cbbdead820b9b3486c5cc8 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Sun, 25 Oct 2009 09:00:37 +0100 Subject: [PATCH] First iteration of doing app layer detection. --- src/Makefile.am | 2 + src/app-layer-detect.c | 24 ++ src/app-layer-detect.h | 33 +++ src/app-layer-parser.c | 22 ++ src/app-layer-parser.h | 4 + src/app-layer-tls-detect-version.c | 396 +++++++++++++++++++++++++++++ src/app-layer-tls-detect-version.h | 12 + src/app-layer-tls.c | 34 ++- src/app-layer-tls.h | 8 + src/app-layer.c | 30 +++ src/app-layer.h | 15 ++ src/detect-engine-proto.c | 6 +- src/detect-parse.c | 114 ++++++++- src/detect.c | 127 ++++++++- src/detect.h | 12 + 15 files changed, 822 insertions(+), 17 deletions(-) create mode 100644 src/app-layer-detect.c create mode 100644 src/app-layer-detect.h create mode 100644 src/app-layer-tls-detect-version.c create mode 100644 src/app-layer-tls-detect-version.h create mode 100644 src/app-layer.c create mode 100644 src/app-layer.h diff --git a/src/Makefile.am b/src/Makefile.am index bf37e2f475..59881f087c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -125,12 +125,14 @@ stream-tcp-reassemble.c stream-tcp-reassemble.h \ respond-reject.c respond-reject.h \ respond-reject-libnet11.h respond-reject-libnet11.c \ counters.c counter.h \ +app-layer.c app-layer.h \ app-layer-detect-proto.c app-layer-detect-proto.h \ app-layer-parser.c app-layer-parser.h \ app-layer-http.c app-layer-http.h \ app-layer-tls.c app-layer-tls.h \ app-layer-smb.c app-layer-smb.h \ app-layer-dcerpc.c app-layer-dcerpc.h \ +app-layer-tls-detect-version.c app-layer-tls-detect-version.h \ app-layer-protos.h \ conf.c conf.h \ conf-yaml-loader.c conf-yaml-loader.h \ diff --git a/src/app-layer-detect.c b/src/app-layer-detect.c new file mode 100644 index 0000000000..00de5c379c --- /dev/null +++ b/src/app-layer-detect.c @@ -0,0 +1,24 @@ +#include "eidps-common.h" +#include "app-layer-detect.h" + +/** \brief alloc a app layer detection ctx + * \retval alde_ctx ptr or NULL in case of error + */ +AlDetectEngineCtx *AlDetectEngineCtxAlloc(void) { + AlDetectEngineCtx *alde_ctx = malloc(sizeof(AlDetectEngineCtx)); + if (alde_ctx == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "malloc failed: %s", strerror(errno)); + return NULL; + } + memset(alde_ctx, 0x00, sizeof(AlDetectEngineCtx)); + + return alde_ctx; +} + +/** \brief free a app layer detection ctx + * \param alde_ctx ptr to app layer detection ctx + */ +void AlDetectEngineCtxAllocFree(AlDetectEngineCtx *alde_ctx) { + free(alde_ctx); +} + diff --git a/src/app-layer-detect.h b/src/app-layer-detect.h new file mode 100644 index 0000000000..415f755093 --- /dev/null +++ b/src/app-layer-detect.h @@ -0,0 +1,33 @@ +#include "eidps-common.h" +#include "detect.h" + +#include "app-layer-protos.h" + +/** detection engine will be grouped: + * - app layer protocol + * - flow direction + * - phase + * - src ip/dst ip + * - ports? (maybe unnecessary as proto detection is in place) + */ + +#define AL_DETECT_FLOW_PHASES 4 + +typedef struct AlDetectProto_ { + DetectAddressGroupsHead *src[AL_DETECT_FLOW_PHASES]; + DetectAddressGroupsHead *tmp[AL_DETECT_FLOW_PHASES]; +} AlDetectProto; + +/** 2 flow states: to_client, to_server */ +#define AL_DETECT_FLOW_STATES 2 + +typedef struct AlDetectFlow_ { + AlDetectProto *proto[ALPROTO_MAX]; +} AlDetectFlow; + +typedef struct AlDetectEngineCtx_ { + /* flow direction */ + AlDetectFlow *flow[AL_DETECT_FLOW_STATES]; + +} AlDetectEngineCtx; + diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 1e31b916b4..2a00d2f1d3 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -343,6 +343,26 @@ uint16_t AppLayerParserGetStorageId(void) { return app_layer_sid; } +uint16_t AppLayerGetProtoByName(const char *name) { + uint8_t u = 1; + SCLogDebug("looking for name %s", name); + + for ( ; u < ALPROTO_MAX; u++) { + if (al_proto_table[u].name == NULL) + continue; + + SCLogDebug("name %s proto %"PRIu16"", + al_proto_table[u].name, u); + + if (strcasecmp(name,al_proto_table[u].name) == 0) { + SCLogDebug("match, returning %"PRIu16"", u); + return u; + } + } + + return ALPROTO_UNKNOWN; +} + /** \brief Description: register a parser. * * \param name full parser name, e.g. "http.request_line" @@ -393,6 +413,8 @@ int AppLayerRegisterProto(char *name, uint8_t proto, uint8_t flags, int (*AppLay al_parser_table[al_max_parsers].name = name; al_parser_table[al_max_parsers].AppLayerParser = AppLayerParser; + al_proto_table[proto].name = name; + /* create proto, direction -- parser mapping */ if (flags & STREAM_TOSERVER) { al_proto_table[proto].to_server = al_max_parsers; diff --git a/src/app-layer-parser.h b/src/app-layer-parser.h index e42609064d..05fab06a3b 100644 --- a/src/app-layer-parser.h +++ b/src/app-layer-parser.h @@ -12,6 +12,8 @@ typedef struct AppLayerLocalMap_ { * Map the proto to the parsers for the to_client and to_server directions. */ typedef struct AppLayerProto_ { + char *name; /**< name of the registered proto */ + uint16_t to_server; uint16_t to_client; uint8_t storage_id; @@ -93,6 +95,8 @@ int AlpParseFieldByEOF(AppLayerParserResult *, AppLayerParserState *, uint16_t, int AlpParseFieldByDelimiter(AppLayerParserResult *, AppLayerParserState *, uint16_t, const uint8_t *, uint8_t, uint8_t *, uint32_t, uint32_t *); uint16_t AlpGetStateIdx(uint16_t); +uint16_t AppLayerGetProtoByName(const char *); + #include "stream-tcp-private.h" void AppLayerParserCleanupState(TcpSession *); diff --git a/src/app-layer-tls-detect-version.c b/src/app-layer-tls-detect-version.c new file mode 100644 index 0000000000..1a30918ad3 --- /dev/null +++ b/src/app-layer-tls-detect-version.c @@ -0,0 +1,396 @@ +/** + * Copyright (c) 2009 Open Information Security Foundation + * + * \file + * \author Victor Julien + */ + +#include "eidps-common.h" +#include "threads.h" +#include "debug.h" +#include "decode.h" +#include "detect.h" + +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-mpm.h" + +#include "flow.h" +#include "flow-var.h" + +#include "util-debug.h" +#include "util-unittest.h" + +#include "app-layer.h" + +#include "app-layer-tls.h" +#include "app-layer-tls-detect-version.h" + + +/** + * \brief Regex for parsing "id" option, matching number or "number" + */ +#define PARSE_REGEX "^\\s*([A-z0-9\\.]+|\"[A-z0-9\\.]+\")\\s*$" + +static pcre *parse_regex; +static pcre_extra *parse_regex_study; + +int AppLayerTlsDetectVersionMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *); +int AppLayerTlsDetectVersionSetup (DetectEngineCtx *, Signature *, SigMatch *, char *); +void AppLayerTlsDetectVersionRegisterTests(void); +void AppLayerTlsDetectVersionFree(void *); + +/** + * \brief Registration function for keyword: tls.version + */ +void AppLayerTlsDetectVersionRegister (void) { + sigmatch_table[DETECT_AL_TLS_VERSION].name = "tls.version"; + sigmatch_table[DETECT_AL_TLS_VERSION].Match = NULL; + sigmatch_table[DETECT_AL_TLS_VERSION].AppLayerMatch = AppLayerTlsDetectVersionMatch; + sigmatch_table[DETECT_AL_TLS_VERSION].Setup = AppLayerTlsDetectVersionSetup; + sigmatch_table[DETECT_AL_TLS_VERSION].Free = AppLayerTlsDetectVersionFree; + sigmatch_table[DETECT_AL_TLS_VERSION].RegisterTests = AppLayerTlsDetectVersionRegisterTests; + + const char *eb; + int eo; + int opts = 0; + + SCLogDebug("registering tls.version rule option\n"); + + parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL); + if (parse_regex == NULL) { + SCLogDebug("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) { + SCLogDebug("pcre study failed: %s\n", eb); + goto error; + } + return; + +error: + return; +} + +/** + * \brief match the specified version on a tls session + * + * \param t pointer to thread vars + * \param det_ctx pointer to the pattern matcher thread + * \param p pointer to the current packet + * \param m pointer to the sigmatch that we will cast into AppLayerTlsDetectVersionData + * + * \retval 0 no match + * \retval 1 match + */ +int AppLayerTlsDetectVersionMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m) +{ + AppLayerTlsDetectVersionData *tls_data = (AppLayerTlsDetectVersionData *)m->ctx; + TlsState *tls_state = (TlsState *)state; + if (tls_state == NULL) + return 0; + + int ret = 0; + mutex_lock(&f->m); + if (flags & STREAM_TOCLIENT) { + if (tls_data->ver == tls_state->client_version) + ret = 1; + } else if (flags & STREAM_TOSERVER) { + if (tls_data->ver == tls_state->server_version) + ret = 1; + } + mutex_unlock(&f->m); + return ret; +} + +/** + * \brief This function is used to parse IPV4 ip_id passed via keyword: "id" + * + * \param idstr Pointer to the user provided id option + * + * \retval id_d pointer to AppLayerTlsDetectVersionData on success + * \retval NULL on failure + */ +AppLayerTlsDetectVersionData *AppLayerTlsDetectVersionParse (char *str) +{ + uint8_t temp; + AppLayerTlsDetectVersionData *tls = NULL; + #define MAX_SUBSTRINGS 30 + int ret = 0, res = 0; + int ov[MAX_SUBSTRINGS]; + + ret = pcre_exec(parse_regex, parse_regex_study, str, strlen(str), 0, 0, + ov, MAX_SUBSTRINGS); + + if (ret < 1 || ret > 3) { + SCLogDebug("invalid tls.version option"); + goto error; + } + + if (ret > 1) { + const char *str_ptr; + char *orig; + char *tmp_str; + res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr); + if (res < 0) { + SCLogDebug("AppLayerTlsDetectVersionParse: pcre_get_substring failed\n"); + goto error; + } + + /* We have a correct id option */ + tls = malloc(sizeof(AppLayerTlsDetectVersionData)); + if (tls == NULL) { + SCLogDebug("AppLayerTlsDetectVersionParse malloc failed\n"); + goto error; + } + + orig = strdup((char*)str_ptr); + tmp_str=orig; + /* Let's see if we need to scape "'s */ + if (tmp_str[0] == '"') + { + tmp_str[strlen(tmp_str) - 1] = '\0'; + tmp_str += 1; + } + + if (strcmp("1.0", tmp_str) == 0) { + temp = TLS_VERSION_10; + } else if (strcmp("1.1", tmp_str) == 0) { + temp = TLS_VERSION_11; + } else if (strcmp("1.2", tmp_str) == 0) { + temp = TLS_VERSION_12; + } else { + goto error; + } + + tls->ver = temp; + + free(orig); + + SCLogDebug("will look for tls %"PRIu8"\n", tls->ver); + } + + return tls; + +error: + if (tls != NULL) + AppLayerTlsDetectVersionFree(tls); + return NULL; + +} + +/** + * \brief this function is used to add the parsed "id" option + * \brief into the current signature + * + * \param de_ctx pointer to the Detection Engine Context + * \param s pointer to the Current Signature + * \param m pointer to the Current SigMatch + * \param idstr pointer to the user provided "id" option + * + * \retval 0 on Success + * \retval -1 on Failure + */ +int AppLayerTlsDetectVersionSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, char *str) +{ + AppLayerTlsDetectVersionData *tls = NULL; + SigMatch *sm = NULL; + + tls = AppLayerTlsDetectVersionParse(str); + if (tls == NULL) goto error; + + /* Okay so far so good, lets get this into a SigMatch + * and put it in the Signature. */ + sm = SigMatchAlloc(); + if (sm == NULL) + goto error; + + sm->type = DETECT_AL_TLS_VERSION; + sm->ctx = (void *)tls; + + SigMatchAppend(s,m,sm); + + return 0; + +error: + if (tls != NULL) AppLayerTlsDetectVersionFree(tls); + if (sm != NULL) free(sm); + return -1; + +} + +/** + * \brief this function will free memory associated with AppLayerTlsDetectVersionData + * + * \param id_d pointer to AppLayerTlsDetectVersionData + */ +void AppLayerTlsDetectVersionFree(void *ptr) { + AppLayerTlsDetectVersionData *id_d = (AppLayerTlsDetectVersionData *)ptr; + free(id_d); +} + +#ifdef UNITTESTS /* UNITTESTS */ + +/** + * \test AppLayerTlsDetectVersionTestParse01 is a test to make sure that we parse the "id" + * option correctly when given valid id option + */ +int AppLayerTlsDetectVersionTestParse01 (void) { + AppLayerTlsDetectVersionData *tls = NULL; + tls = AppLayerTlsDetectVersionParse("1.0"); + if (tls != NULL && tls->ver == TLS_VERSION_10) { + AppLayerTlsDetectVersionFree(tls); + return 1; + } + + return 0; +} + +/** + * \test AppLayerTlsDetectVersionTestParse02 is a test to make sure that we parse the "id" + * option correctly when given an invalid id option + * it should return id_d = NULL + */ +int AppLayerTlsDetectVersionTestParse02 (void) { + AppLayerTlsDetectVersionData *tls = NULL; + tls = AppLayerTlsDetectVersionParse("2.5"); + if (tls == NULL) { + AppLayerTlsDetectVersionFree(tls); + return 1; + } + + return 0; +} + +#include "stream-tcp-reassemble.h" + +/** \test Send a get request in three chunks + more data. */ +static int AppLayerTlsDetectVersionTestDetect01(void) { + int result = 1; + Flow f; + uint8_t tlsbuf1[] = { 0x16 }; + uint32_t tlslen1 = sizeof(tlsbuf1); + uint8_t tlsbuf2[] = { 0x03 }; + uint32_t tlslen2 = sizeof(tlsbuf2); + uint8_t tlsbuf3[] = { 0x01 }; + uint32_t tlslen3 = sizeof(tlsbuf3); + uint8_t tlsbuf4[] = { 0x01, 0x00, 0x00, 0xad, 0x03, 0x01 }; + uint32_t tlslen4 = sizeof(tlsbuf4); + TcpSession ssn; + Packet p; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + memset(&th_v, 0, sizeof(th_v)); + memset(&p, 0, sizeof(p)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + p.src.family = AF_INET; + p.dst.family = AF_INET; + p.payload = NULL; + p.payload_len = 0; + p.proto = IPPROTO_TCP; + + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.protoctx = (void *)&ssn; + p.flow = &f; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + + //de_ctx->flags |= DE_QUIET; + + s = de_ctx->sig_list = SigInit(de_ctx,"alert tls any any -> any any (msg:\"TLS\"; tls.version:1.0; sid:1;)"); + if (s == NULL) { + goto end; + } + + SigGroupBuild(de_ctx); + PatternMatchPrepare(mpm_ctx, MPM_B2G); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParse(&f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf1, tlslen1, FALSE); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + r = AppLayerParse(&f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf2, tlslen2, FALSE); + if (r != 0) { + printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + r = AppLayerParse(&f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf3, tlslen3, FALSE); + if (r != 0) { + printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + r = AppLayerParse(&f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf4, tlslen4, FALSE); + if (r != 0) { + printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + TlsState *tls_state = ssn.aldata[AlpGetStateIdx(ALPROTO_TLS)]; + if (tls_state == NULL) { + printf("no tls state: "); + result = 0; + goto end; + } + + if (tls_state->client_content_type != 0x16) { + printf("expected content_type %" PRIu8 ", got %" PRIu8 ": ", 0x16, tls_state->client_content_type); + result = 0; + goto end; + } + + if (tls_state->client_version != TLS_VERSION_10) { + printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", TLS_VERSION_10, tls_state->client_version); + result = 0; + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); + + if (!(PacketAlertCheck(&p, 1))) { + goto end; + } + + result = 1; +end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + PatternMatchDestroy(mpm_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +#endif /* UNITTESTS */ + +/** + * \brief this function registers unit tests for AppLayerTlsDetectVersion + */ +void AppLayerTlsDetectVersionRegisterTests(void) { +#ifdef UNITTESTS /* UNITTESTS */ + UtRegisterTest("AppLayerTlsDetectVersionTestParse01", AppLayerTlsDetectVersionTestParse01, 1); + UtRegisterTest("AppLayerTlsDetectVersionTestParse02", AppLayerTlsDetectVersionTestParse02, 1); + UtRegisterTest("AppLayerTlsDetectVersionTestDetect01", AppLayerTlsDetectVersionTestDetect01, 1); +#endif /* UNITTESTS */ +} diff --git a/src/app-layer-tls-detect-version.h b/src/app-layer-tls-detect-version.h new file mode 100644 index 0000000000..0cc811e23f --- /dev/null +++ b/src/app-layer-tls-detect-version.h @@ -0,0 +1,12 @@ +#ifndef __APP_LAYER_TLS_DETECT_VERSION_H__ +#define __APP_LAYER_TLS_DETECT_VERSION_H__ + +typedef struct AppLayerTlsDetectVersionData_ { + uint8_t ver; /** tls version to match */ +} AppLayerTlsDetectVersionData; + +/* prototypes */ +void AppLayerTlsDetectVersionRegister (void); + +#endif /* __APP_LAYER_TLS_DETECT_VERSION_H__ */ + diff --git a/src/app-layer-tls.c b/src/app-layer-tls.c index 5a96639a74..f9f8088eb5 100644 --- a/src/app-layer-tls.c +++ b/src/app-layer-tls.c @@ -18,6 +18,8 @@ #include "conf.h" +#include "app-layer-tls.h" + #include "util-binsearch.h" #include "util-unittest.h" #include "util-debug.h" @@ -111,7 +113,17 @@ static int TLSParseClientVersion(void *tls_state, AppLayerParserState *pstate, } *u16conv; u16conv = (struct u16conv_ *)input; - state->client_version = ntohs(u16conv->u); + switch (ntohs(u16conv->u)) { + case 0x0301: + state->client_version = TLS_VERSION_10; + break; + case 0x0302: + state->client_version = TLS_VERSION_11; + break; + case 0x0303: + state->client_version = TLS_VERSION_12; + break; + } SCLogDebug("version %04"PRIx16"", state->client_version); return 1; @@ -517,9 +529,8 @@ static int TLSParserTest01(void) { goto end; } - if (tls_state->client_version != 0x0301) { - printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", 0x0301, - tls_state->client_version); + if (tls_state->client_version != TLS_VERSION_10) { + printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", TLS_VERSION_10, tls_state->client_version); result = 0; goto end; } @@ -571,9 +582,8 @@ static int TLSParserTest02(void) { goto end; } - if (tls_state->client_version != 0x0301) { - printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", 0x0301, - tls_state->client_version); + if (tls_state->client_version != TLS_VERSION_10) { + printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", TLS_VERSION_10, tls_state->client_version); result = 0; goto end; } @@ -634,9 +644,8 @@ static int TLSParserTest03(void) { goto end; } - if (tls_state->client_version != 0x0301) { - printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", 0x0301, - tls_state->client_version); + if (tls_state->client_version != TLS_VERSION_10) { + printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", TLS_VERSION_10, tls_state->client_version); result = 0; goto end; } @@ -1005,9 +1014,8 @@ static int TLSParserMultimsgTest01(void) { goto end; } - if (tls_state->client_version != 0x0301) { - printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", 0x0301, - tls_state->client_version); + if (tls_state->client_version != TLS_VERSION_10) { + printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", TLS_VERSION_10, tls_state->client_version); result = 0; goto end; } diff --git a/src/app-layer-tls.h b/src/app-layer-tls.h index b30f34cf67..a7af36d319 100644 --- a/src/app-layer-tls.h +++ b/src/app-layer-tls.h @@ -34,6 +34,14 @@ typedef struct TlsState_ { uint8_t records; /**< no of records */ } TlsState; +enum { + TLS_VERSION_INVALID = 0, + TLS_VERSION_VALID, + TLS_VERSION_10, + TLS_VERSION_11, + TLS_VERSION_12, +}; + void RegisterTLSParsers(void); void TLSParserRegisterTests(void); diff --git a/src/app-layer.c b/src/app-layer.c new file mode 100644 index 0000000000..9cc8bfad93 --- /dev/null +++ b/src/app-layer.c @@ -0,0 +1,30 @@ +#include "eidps-common.h" +#include "app-layer.h" +#include "stream-tcp-private.h" + +/** \brief Get the active app layer state from the packet */ +void *AppLayerGetProtoStateFromPacket(Packet *p) { + if (p == NULL || p->flow == NULL) + return NULL; + + TcpSession *ssn = (TcpSession *)p->flow->protoctx; + if (ssn == NULL || ssn->aldata == NULL) + return NULL; + + void *alstate = ssn->aldata[ssn->alproto]; + return alstate; +} + +/** \brief Get the active app layer state from the flow */ +void *AppLayerGetProtoStateFromFlow(Flow *f) { + if (f == NULL) + return NULL; + + TcpSession *ssn = (TcpSession *)f->protoctx; + if (ssn == NULL || ssn->aldata == NULL) + return NULL; + + void *alstate = ssn->aldata[ssn->alproto]; + return alstate; +} + diff --git a/src/app-layer.h b/src/app-layer.h new file mode 100644 index 0000000000..5a9aa107f1 --- /dev/null +++ b/src/app-layer.h @@ -0,0 +1,15 @@ +#ifndef __APP_LAYER_H__ +#define __APP_LAYER_H__ + +#include "flow.h" +#include "decode.h" + +#include "app-layer-protos.h" +#include "app-layer-parser.h" +#include "stream.h" + +void *AppLayerGetProtoStateFromPacket(Packet *); +void *AppLayerGetProtoStateFromFlow(Flow *); + +#endif /* __APP_LAYER_H__ */ + diff --git a/src/detect-engine-proto.c b/src/detect-engine-proto.c index fa3f5ace16..ea470e4ec5 100644 --- a/src/detect-engine-proto.c +++ b/src/detect-engine-proto.c @@ -95,6 +95,10 @@ int DetectProtoParse(DetectProto *dp, char *str) memset(dp->proto, 0xff, sizeof(dp->proto)); SCLogDebug("DetectProtoParse: IP protocol detected"); } else { + goto error; + + /** \todo are numeric protocols even valid? */ +#if 0 uint8_t proto_u8; /* Used to avoid sign extension */ /* Extract out a 0-256 value with validation checks */ @@ -111,8 +115,8 @@ int DetectProtoParse(DetectProto *dp, char *str) } else { dp->proto[proto / 8] |= 1<<(proto % 8); } +#endif } - return 0; error: diff --git a/src/detect-parse.c b/src/detect-parse.c index 7f70cef1ed..c4d3c118b6 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -16,6 +16,10 @@ #include "util-rule-vars.h" #include "conf.h" #include "conf-yaml-loader.h" + +#include "app-layer-protos.h" +#include "app-layer-parser.h" + #include "util-unittest.h" #include "util-debug.h" @@ -44,7 +48,7 @@ static uint32_t dbg_dstportany_cnt = 0; // action protocol src sp dir dst dp options #define CONFIG_PCRE "^([A-z]+)\\s+([A-z0-9]+)\\s+([\\[\\]A-z0-9\\.\\:_\\$\\!\\-,\\/]+)\\s+([\\:A-z0-9_\\$\\!,]+)\\s+(-\\>|\\<\\>)\\s+([\\[\\]A-z0-9\\.\\:_\\$\\!\\-,/]+)\\s+([\\:A-z0-9_\\$\\!,]+)(?:\\s+\\((.*)?(?:\\s*)\\))?(?:(?:\\s*)\\n)?$" #define OPTION_PARTS 3 -#define OPTION_PCRE "^\\s*([A-z_0-9-]+)(?:\\s*\\:\\s*(.*)(?proto, (char *)protostr); - if (r < 0) + if (r < 0) { + s->alproto = AppLayerGetProtoByName(protostr); + if (s->alproto != ALPROTO_UNKNOWN) { + /* indicate that the signature is app-layer */ + s->flags |= SIG_FLAG_APPLAYER; + return 0; + } + return -1; + } return 0; } @@ -854,6 +866,7 @@ end: DetectEngineCtxFree(de_ctx); return result; } + /** * \test check that we don't allow invalid negation options */ @@ -960,6 +973,100 @@ end: return result; } +/** + * \test test tls (app layer) rule + */ +static int SigParseTestAppLayerTLS01(void) { + int result = 0; + DetectEngineCtx *de_ctx; + Signature *s=NULL; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + de_ctx->flags |= DE_QUIET; + + s = SigInit(de_ctx,"alert tls any any -> any any (msg:\"SigParseTestAppLayerTLS01 \"; classtype:misc-activity; sid:410006; rev:1;)"); + if (s == NULL) { + printf("parsing sig failed: "); + goto end; + } + + if (s->alproto == 0) { + printf("alproto not set: "); + goto end; + } + + result = 1; +end: + if (s->alproto == 0) { + printf("alproto not set: "); + if (s != NULL) + SigFree(s); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test test tls (app layer) rule + */ +static int SigParseTestAppLayerTLS02(void) { + int result = 0; + DetectEngineCtx *de_ctx; + Signature *s=NULL; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + de_ctx->flags |= DE_QUIET; + + s = SigInit(de_ctx,"alert tls any any -> any any (msg:\"SigParseTestAppLayerTLS02 \"; tls.version:1.0; classtype:misc-activity; sid:410006; rev:1;)"); + if (s == NULL) { + printf("parsing sig failed: "); + goto end; + } + + if (s->alproto == 0) { + printf("alproto not set: "); + goto end; + } + + result = 1; +end: + if (s != NULL) + SigFree(s); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test test tls (app layer) rule + */ +static int SigParseTestAppLayerTLS03(void) { + int result = 0; + DetectEngineCtx *de_ctx; + Signature *s=NULL; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + de_ctx->flags |= DE_QUIET; + + s = SigInit(de_ctx,"alert tls any any -> any any (msg:\"SigParseTestAppLayerTLS03 \"; tls.version:2.5; classtype:misc-activity; sid:410006; rev:1;)"); + if (s != NULL) { + SigFree(s); + goto end; + } + + result = 1; +end: + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} #endif /* UNITTESTS */ void SigParseRegisterTests(void) { @@ -978,5 +1085,8 @@ void SigParseRegisterTests(void) { UtRegisterTest("SigParseTestNegation07", SigParseTestNegation07, 1); UtRegisterTest("SigParseTestMpm01", SigParseTestMpm01, 1); UtRegisterTest("SigParseTestMpm02", SigParseTestMpm02, 1); + UtRegisterTest("SigParseTestAppLayerTLS01", SigParseTestAppLayerTLS01, 1); + UtRegisterTest("SigParseTestAppLayerTLS02", SigParseTestAppLayerTLS02, 1); + UtRegisterTest("SigParseTestAppLayerTLS03", SigParseTestAppLayerTLS03, 1); #endif /* UNITTESTS */ } diff --git a/src/detect.c b/src/detect.c index 1ef30d82ce..a1e2ae0244 100644 --- a/src/detect.c +++ b/src/detect.c @@ -62,6 +62,10 @@ #include "detect-fast-pattern.h" #include "util-rule-vars.h" + +#include "app-layer.h" +#include "app-layer-tls-detect-version.h" + #include "action-globals.h" #include "tm-modules.h" @@ -344,6 +348,102 @@ inline SigGroupHead *SigMatchSignaturesGetSgh(ThreadVars *th_v, DetectEngineCtx return sgh; } +static int SigMatchSignaturesAppLayer(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, SigGroupHead *sgh, Packet *p) +{ + int match = 0, fmatch = 0; + Signature *s = NULL; + SigMatch *sm = NULL; + uint32_t idx,sig; + + void *alstate = AppLayerGetProtoStateFromPacket(p); + if (alstate == NULL) + return 0; + + uint8_t flags = 0; + + /* if we didn't get a sig group head, we + * have nothing to do.... */ + if (sgh == NULL) { + return 0; + } + + /* inspect the sigs against the packet */ + for (idx = 0; idx < sgh->sig_cnt; idx++) { + sig = sgh->match_array[idx]; + s = de_ctx->sig_array[sig]; + + /* only inspect app layer sigs here */ + if (!(s->flags & SIG_FLAG_APPLAYER)) + continue; + + /* filter out the sigs that inspects the payload, if packet + no payload inspection flag is set*/ + if ((p->flags & PKT_NOPAYLOAD_INSPECTION) && (s->flags & SIG_FLAG_PAYLOAD)) + continue; + + /* check the source & dst port in the sig */ + if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP) { + if (!(s->flags & SIG_FLAG_DP_ANY)) { + DetectPort *dport = DetectPortLookupGroup(s->dp,p->dp); + if (dport == NULL) + continue; + + } + if (!(s->flags & SIG_FLAG_SP_ANY)) { + DetectPort *sport = DetectPortLookupGroup(s->sp,p->sp); + if (sport == NULL) + continue; + } + } + + /* check the source address */ + if (!(s->flags & SIG_FLAG_SRC_ANY)) { + DetectAddressGroup *saddr = DetectAddressLookupGroup(&s->src,&p->src); + if (saddr == NULL) + continue; + } + /* check the destination address */ + if (!(s->flags & SIG_FLAG_DST_ANY)) { + DetectAddressGroup *daddr = DetectAddressLookupGroup(&s->dst,&p->dst); + if (daddr == NULL) + continue; + } + + /* reset pkt ptr and offset */ + det_ctx->pkt_ptr = NULL; + det_ctx->pkt_off = 0; + + sm = s->match; + while (sm) { + if (sigmatch_table[sm->type].AppLayerMatch == NULL) + continue; + + match = sigmatch_table[sm->type].AppLayerMatch(th_v, det_ctx, p->flow, flags, alstate, s, sm); + if (match) { + /* okay, try the next match */ + sm = sm->next; + + /* only if the last matched as well, we have a hit */ + if (sm == NULL) { + fmatch = 1; + //printf("DE : sig %" PRIu32 " matched\n", s->id); + if (!(s->flags & SIG_FLAG_NOALERT)) { + PacketAlertAppend(p, 1, s->id, s->rev, s->prio, s->msg); + + /* set verdict on packet */ + p->action = s->action; + } + } + } else { + /* done with this sig */ + sm = NULL; + } + } + } + + return fmatch; +} + int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p) { int match = 0, fmatch = 0; @@ -427,6 +527,10 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh continue; } + /* don't inspect app layer sigs here */ + if (s->flags & SIG_FLAG_APPLAYER) + continue; + /* filter out sigs that want pattern matches, but * have no matches */ if (!(det_ctx->pmq.sig_bitarray[(sig / 8)] & (1<<(sig % 8))) && @@ -499,6 +603,9 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh do { sm = s->match; while (sm) { + if (sigmatch_table[sm->type].Match == NULL) + continue; + match = sigmatch_table[sm->type].Match(th_v, det_ctx, p, s, sm); if (match) { /* okay, try the next match */ @@ -534,6 +641,9 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh SCLogDebug("running match functions, sm %p", sm); while (sm) { + if (sigmatch_table[sm->type].Match == NULL) + continue; + match = sigmatch_table[sm->type].Match(th_v, det_ctx, p, s, sm); if (match) { /* okay, try the next match */ @@ -560,6 +670,12 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh /* cleanup pkt specific part of the patternmatcher */ PacketPatternCleanup(th_v, det_ctx); + + match = SigMatchSignaturesAppLayer(th_v, de_ctx, det_ctx, det_ctx->sgh, p); + if (match > 0) { + fmatch = 1; + } + SCReturnInt(fmatch); } @@ -629,6 +745,13 @@ void SigCleanSignatures(DetectEngineCtx *de_ctx) de_ctx->sig_list = NULL; } +int SignatureIsAppLayer(DetectEngineCtx *de_ctx, Signature *s) { + if (s->alproto != 0) + return 1; + + return 0; +} + /** \brief Test is a initialized signature is IP only * \param de_ctx detection engine ctx * \param s the signature @@ -2184,7 +2307,7 @@ int SigAddressPrepareStage3(DetectEngineCtx *de_ctx) { SCLogInfo("MPM max patcnt %" PRIu32 ", avg %" PRIu32 "", de_ctx->mpm_max_patcnt, de_ctx->mpm_unique?de_ctx->mpm_tot_patcnt/de_ctx->mpm_unique:0); if (de_ctx->mpm_uri_tot_patcnt && de_ctx->mpm_uri_unique) SCLogInfo("MPM (URI) max patcnt %" PRIu32 ", avg %" PRIu32 " (%" PRIu32 "/%" PRIu32 ")", de_ctx->mpm_uri_max_patcnt, de_ctx->mpm_uri_tot_patcnt/de_ctx->mpm_uri_unique, de_ctx->mpm_uri_tot_patcnt, de_ctx->mpm_uri_unique); - SCLogInfo("port maxgroups: %" PRIu32 ", avg %" PRIu32 ", tot %" PRIu32 "", g_groupportlist_maxgroups, g_groupportlist_totgroups/g_groupportlist_groupscnt, g_groupportlist_totgroups); + SCLogInfo("port maxgroups: %" PRIu32 ", avg %" PRIu32 ", tot %" PRIu32 "", g_groupportlist_maxgroups, g_groupportlist_groupscnt ? g_groupportlist_totgroups/g_groupportlist_groupscnt : 0, g_groupportlist_totgroups); SCLogInfo("building signature grouping structure, stage 3: building destination address lists... done"); } return 0; @@ -2625,6 +2748,8 @@ void SigTableSetup(void) { DetectTtlRegister(); DetectFastPatternRegister(); + AppLayerTlsDetectVersionRegister(); + uint8_t i = 0; for (i = 0; i < DETECT_TBLSIZE; i++) { if (sigmatch_table[i].RegisterTests == NULL) { diff --git a/src/detect.h b/src/detect.h index fd57326163..826de26eed 100644 --- a/src/detect.h +++ b/src/detect.h @@ -3,6 +3,8 @@ #include +#include "flow.h" + #include "detect-engine-proto.h" #include "packet-queue.h" @@ -127,6 +129,7 @@ typedef struct DetectPort_ { #define SIG_FLAG_DSIZE 0x0400 /**< signature has a dsize setting */ #define SIG_FLAG_FLOW 0x0800 /**< signature has a flow setting */ #define SIG_FLAG_MPM_NEGCONTENT 0x1000 /**< sig has negative mpm portion(!content) */ +#define SIG_FLAG_APPLAYER 0x2000 /**< signature applies to app layer instead of packets */ /* Detection Engine flags */ #define DE_QUIET 0x01 /**< DE is quiet (esp for unittests) */ @@ -166,6 +169,9 @@ typedef struct Signature_ { /* helper for init phase */ uint16_t mpm_content_maxlen; uint16_t mpm_uricontent_maxlen; + + /* app layer signature stuff */ + uint8_t alproto; } Signature; /** \brief IP only rules matching ctx. @@ -332,7 +338,11 @@ typedef struct SigMatch_ { /** \brief element in sigmatch type table. */ typedef struct SigTableElmt_ { + /** Packet match function */ int (*Match)(ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, SigMatch *); + /** AppLayer match function */ + int (*AppLayerMatch)(ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t flags, void *alstate, Signature *, SigMatch *); + int (*Setup)(DetectEngineCtx *, Signature *, SigMatch *, char *); void (*Free)(void *); void (*RegisterTests)(void); @@ -453,6 +463,8 @@ enum { DETECT_FRAGBITS, DETECT_GID, + DETECT_AL_TLS_VERSION, + /* make sure this stays last */ DETECT_TBLSIZE, };