diff --git a/src/Makefile.am b/src/Makefile.am index c86d9d96c6..7851ead44a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -98,6 +98,7 @@ counters.c counter.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-protos.h \ conf.c conf.h diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index 0652f1a5ae..7125efe22b 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -182,19 +182,16 @@ void AppLayerDetectProtoThreadInit(void) { AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SSH, "SSH-", 4, 0, STREAM_TOCLIENT); AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SSH, "SSH-", 4, 0, STREAM_TOSERVER); - /** SSLv3 */ + /** SSLv2 */ AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SSL, "|01 03 00|", 5, 2, STREAM_TOSERVER); AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SSL, "|16 03 00|", 5, 2, STREAM_TOSERVER); - AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SSL, "|16 03 00|", 3, 0, STREAM_TOCLIENT); + /** SSLv3 */ + AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_TLS, "|16 03 00|", 3, 0, STREAM_TOCLIENT); /** TLSv1 */ - AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SSL, "|01 03 01|", 3, 0, STREAM_TOSERVER); - AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SSL, "|01 03 01|", 5, 2, STREAM_TOSERVER); /** midstream */ - AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SSL, "|01 03 01|", 5, 2, STREAM_TOCLIENT); /** midstream */ - AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SSL, "|16 03 01|", 3, 0, STREAM_TOSERVER); - AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SSL, "|16 03 01|", 3, 0, STREAM_TOCLIENT); - AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SSL, "|17 03 01|", 3, 0, STREAM_TOSERVER); /** midstream */ - AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SSL, "|17 03 01|", 3, 0, STREAM_TOCLIENT); /** midstream */ + AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_TLS, "|01 03 01|", 3, 0, STREAM_TOSERVER); + AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_TLS, "|16 03 01|", 3, 0, STREAM_TOSERVER); + AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_TLS, "|16 03 01|", 3, 0, STREAM_TOCLIENT); /** IMAP */ AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_IMAP, "|2A 20|OK|20|", 5, 0, STREAM_TOCLIENT); @@ -206,6 +203,10 @@ void AppLayerDetectProtoThreadInit(void) { AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SMTP, "ESMTP ", 64, 4, STREAM_TOSERVER); AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_SMTP, "SMTP ", 64, 4, STREAM_TOSERVER); + /** FTP */ + AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_FTP, "USER ", 5, 0, STREAM_TOCLIENT); + AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_FTP, "AUTH SSL", 8, 0, STREAM_TOCLIENT); + /** MSN Messenger */ AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_MSN, "MSNP", 10, 6, STREAM_TOCLIENT); AlpProtoAdd(&alp_proto_ctx, IPPROTO_TCP, ALPROTO_MSN, "MSNP", 10, 6, STREAM_TOSERVER); diff --git a/src/app-layer-http.c b/src/app-layer-http.c index 70fc1a059d..1eb7c685ba 100644 --- a/src/app-layer-http.c +++ b/src/app-layer-http.c @@ -227,6 +227,7 @@ static int HTTPParseRequest(void *http_state, AppLayerParserState *pstate, uint8 } pstate->parse_field = 0; + pstate->flags |= APP_LAYER_PARSER_DONE; return 1; } @@ -365,6 +366,7 @@ static int HTTPParseResponse(void *http_state, AppLayerParserState *pstate, uint } pstate->parse_field = 0; + pstate->flags |= APP_LAYER_PARSER_DONE; return 1; } diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index e485ef8ff8..17403c2b49 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -90,6 +90,57 @@ static void AlpStoreField(AppLayerParserResult *output, uint16_t idx, uint8_t *p //PrintRawDataFp(stdout, e->data_ptr,e->data_len); } +/** \brief Parse a field up to we reach the size limit + * + * \retval 1 Field found and stored. + * \retval 0 Field parsing in progress. + * \retval -1 error + */ +int AlpParseFieldBySize(AppLayerParserResult *output, AppLayerParserState *pstate, uint16_t field_idx, uint32_t size, uint8_t *input, uint32_t input_len, uint32_t *offset) { + if ((pstate->store_len + input_len) < size) { + if (pstate->store_len == 0) { + pstate->store = malloc(input_len); + if (pstate->store == NULL) + return -1; + + memcpy(pstate->store, input, input_len); + pstate->store_len = input_len; + } else { + pstate->store = realloc(pstate->store, (input_len + pstate->store_len)); + if (pstate->store == NULL) + return -1; + + memcpy(pstate->store+pstate->store_len, input, input_len); + pstate->store_len += input_len; + } + } else { + if (pstate->store_len == 0) { + AlpStoreField(output, field_idx, input, size, 0); + (*offset) += size; + return 1; + } else { + uint32_t diff = size - pstate->store_len; + + pstate->store = realloc(pstate->store, (diff + pstate->store_len)); + if (pstate->store == NULL) + return -1; + + memcpy(pstate->store+pstate->store_len, input, diff); + pstate->store_len += diff; + + AlpStoreField(output, field_idx, pstate->store, pstate->store_len, 1); + + (*offset) += diff; + + pstate->store = NULL; + pstate->store_len = 0; + return 1; + } + } + + return 0; +} + /** \brief Parse a field up to the EOF * * \retval 1 Field found and stored. @@ -484,7 +535,7 @@ int AppLayerParse(Flow *f, uint8_t proto, uint8_t flags, uint8_t *input, uint32_ } } - if (parser_idx == 0) { + if (parser_idx == 0 || parser_state->flags & APP_LAYER_PARSER_DONE) { //printf("AppLayerParse: no parser for protocol %" PRIu32 "\n", proto); return 0; } diff --git a/src/app-layer-parser.h b/src/app-layer-parser.h index 5d216f123c..264e6306d1 100644 --- a/src/app-layer-parser.h +++ b/src/app-layer-parser.h @@ -44,8 +44,9 @@ typedef struct AppLayerParserResult_ { uint32_t cnt; } AppLayerParserResult; -#define APP_LAYER_PARSER_USE 0x01 -#define APP_LAYER_PARSER_EOF 0x02 +#define APP_LAYER_PARSER_USE 0x01 +#define APP_LAYER_PARSER_EOF 0x02 +#define APP_LAYER_PARSER_DONE 0x04 /* parser is done, ignore more msgs */ typedef struct AppLayerParserState_ { uint8_t flags; @@ -81,6 +82,7 @@ void AppLayerRegisterStateFuncs(uint16_t proto, void *(*StateAlloc)(void), void int AppLayerParse(Flow *f, uint8_t proto, uint8_t flags, uint8_t *input, uint32_t input_len); +int AlpParseFieldBySize(AppLayerParserResult *, AppLayerParserState *, uint16_t, uint32_t, uint8_t *, uint32_t, uint32_t *); int AlpParseFieldByEOF(AppLayerParserResult *, AppLayerParserState *, uint16_t, uint8_t *, uint32_t); int AlpParseFieldByDelimiter(AppLayerParserResult *, AppLayerParserState *, uint16_t, const uint8_t *, uint8_t, uint8_t *, uint32_t, uint32_t *); uint16_t AlpGetStateIdx(uint16_t); diff --git a/src/app-layer-protos.h b/src/app-layer-protos.h index f11631e1d9..b21a13f602 100644 --- a/src/app-layer-protos.h +++ b/src/app-layer-protos.h @@ -6,7 +6,8 @@ enum { ALPROTO_HTTP, ALPROTO_FTP, ALPROTO_SMTP, - ALPROTO_SSL, + ALPROTO_SSL, /* SSLv2 */ + ALPROTO_TLS, /* SSLv3 & TLSv1 */ ALPROTO_SSH, ALPROTO_IMAP, ALPROTO_MSN, diff --git a/src/app-layer-tls.c b/src/app-layer-tls.c new file mode 100644 index 0000000000..9f520751f2 --- /dev/null +++ b/src/app-layer-tls.c @@ -0,0 +1,370 @@ +/* Copyright (c) 2009 Victor Julien */ + +#include "eidps-common.h" +#include "debug.h" +#include "decode.h" +#include "threads.h" + +#include "util-print.h" +#include "util-pool.h" + +#include "stream-tcp-private.h" +#include "stream-tcp-reassemble.h" +#include "stream.h" + +#include "app-layer-protos.h" +#include "app-layer-parser.h" + +#include "util-binsearch.h" +#include "util-unittest.h" + +enum { + TLS_FIELD_NONE = 0, + + TLS_FIELD_CLIENT_CONTENT_TYPE, /* len 1 */ + TLS_FIELD_CLIENT_VERSION, /* len 2 */ + + TLS_FIELD_SERVER_CONTENT_TYPE, /* len 1 */ + TLS_FIELD_SERVER_VERSION, /* len 2 */ + + /* must be last */ + TLS_FIELD_MAX, +}; + +typedef struct TlsState_ { + uint8_t client_content_type; + uint16_t client_version; + + uint8_t server_content_type; + uint16_t server_version; +} TlsState; + +static int TLSParseClientContentType(void *tls_state, AppLayerParserState *pstate, uint8_t *input, uint32_t input_len, AppLayerParserResult *output) { + TlsState *state = (TlsState *)tls_state; + + if (input_len != 1) { + return 0; + } + state->client_content_type = *input; + + //printf("TLSParseClientContentType: content_type %02"PRIx8"\n", state->client_content_type); + return 1; +} + +static int TLSParseClientVersion(void *tls_state, AppLayerParserState *pstate, uint8_t *input, uint32_t input_len, AppLayerParserResult *output) { + TlsState *state = (TlsState *)tls_state; + + if (input_len != 2) + return 0; + + /** \todo there must be an easier way to get from uint8_t * to a uint16_t */ + struct u16conv_ { + uint16_t u; + } *u16conv; + u16conv = (struct u16conv_ *)input; + + state->client_version = ntohs(u16conv->u); + + //printf("TLSParseClientVersion: version %04"PRIx16"\n", state->client_version); + return 1; +} + +static int TLSParseClientRecord(void *tls_state, AppLayerParserState *pstate, uint8_t *input, uint32_t input_len, AppLayerParserResult *output) { + //printf("TLSParseRequestLine: tls_state %p, pstate %p, input %p, input_len %" PRIu32 "\n", tls_state, pstate, input, input_len); + //PrintRawDataFp(stdout, input,input_len); + + uint16_t max_fields = 2; + uint16_t u = 0; + uint32_t offset = 0; + + if (pstate == NULL) + return -1; + + for (u = pstate->parse_field; u < max_fields; u++) { + //printf("TLSParseRequestLine: u %" PRIu32 "\n", u); + + switch(u) { + case 0: /* TLS CONTENT TYPE */ + { + int r = AlpParseFieldBySize(output, pstate, TLS_FIELD_CLIENT_CONTENT_TYPE, /* single byte field */1, input, input_len, &offset); + //printf("TLSParseClientRecord: r = %" PRId32 "\n", r); + + if (r == 0) { + pstate->parse_field = 0; + return 0; + } + break; + } + case 1: /* TLS VERSION */ + { + uint8_t *data = input + offset; + uint32_t data_len = input_len - offset; + + int r = AlpParseFieldBySize(output, pstate, TLS_FIELD_CLIENT_VERSION, /* 2 byte field */2, data, data_len, &offset); + if (r == 0) { + pstate->parse_field = 1; + return 0; + } + break; + } + } + } + + pstate->parse_field = 0; + pstate->flags |= APP_LAYER_PARSER_DONE; + return 1; +} + +static void *TLSStateAlloc(void) { + void *s = malloc(sizeof(TlsState)); + if (s == NULL) + return NULL; + + memset(s, 0, sizeof(TlsState)); + return s; +} + +static void TLSStateFree(void *s) { + free(s); +} + +void RegisterTLSParsers(void) { + AppLayerRegisterProto("tls", ALPROTO_TLS, STREAM_TOSERVER, TLSParseClientRecord); + AppLayerRegisterParser("tls.client.content_type", ALPROTO_TLS, TLS_FIELD_CLIENT_CONTENT_TYPE, TLSParseClientContentType, "tls"); + AppLayerRegisterParser("tls.client.version", ALPROTO_TLS, TLS_FIELD_CLIENT_VERSION, TLSParseClientVersion, "tls"); + AppLayerRegisterStateFuncs(ALPROTO_TLS, TLSStateAlloc, TLSStateFree); +} + +/* UNITTESTS */ +#ifdef UNITTESTS + +/** \test Send a get request in one chunk. */ +static int TLSParserTest01(void) { + int result = 1; + Flow f; + uint8_t tlsbuf[] = { 0x16, 0x03, 0x01 }; + uint32_t tlslen = sizeof(tlsbuf); + TcpSession ssn; + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.stream = (void *)&ssn; + + int r = AppLayerParse(&f, ALPROTO_TLS, STREAM_TOSERVER|STREAM_EOF, tlsbuf, tlslen); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + TlsState *tls_state = ssn.l7data[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 != 0x0301) { + printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", 0x0301, tls_state->client_version); + result = 0; + goto end; + } +end: + return result; +} + +/** \test Send a get request in two chunks. */ +static int TLSParserTest02(void) { + int result = 1; + Flow f; + uint8_t tlsbuf1[] = { 0x16 }; + uint32_t tlslen1 = sizeof(tlsbuf1); + uint8_t tlsbuf2[] = { 0x03, 0x01 }; + uint32_t tlslen2 = sizeof(tlsbuf2); + TcpSession ssn; + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.stream = (void *)&ssn; + + int r = AppLayerParse(&f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf1, tlslen1); + 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); + if (r != 0) { + printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + TlsState *tls_state = ssn.l7data[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 != 0x0301) { + printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", 0x0301, tls_state->client_version); + result = 0; + goto end; + } +end: + return result; +} + +/** \test Send a get request in three chunks. */ +static int TLSParserTest03(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); + TcpSession ssn; + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.stream = (void *)&ssn; + + int r = AppLayerParse(&f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf1, tlslen1); + 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); + 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); + if (r != 0) { + printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + TlsState *tls_state = ssn.l7data[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 != 0x0301) { + printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", 0x0301, tls_state->client_version); + result = 0; + goto end; + } +end: + return result; +} + +/** \test Send a get request in three chunks + more data. */ +static int TLSParserTest04(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; + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.stream = (void *)&ssn; + + int r = AppLayerParse(&f, ALPROTO_TLS, STREAM_TOSERVER, tlsbuf1, tlslen1); + 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); + 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); + 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); + if (r != 0) { + printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + TlsState *tls_state = ssn.l7data[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 != 0x0301) { + printf("expected version %04" PRIu16 ", got %04" PRIu16 ": ", 0x0301, tls_state->client_version); + result = 0; + goto end; + } +end: + return result; +} + +void TLSParserRegisterTests(void) { + UtRegisterTest("TLSParserTest01", TLSParserTest01, 1); + UtRegisterTest("TLSParserTest02", TLSParserTest02, 1); + UtRegisterTest("TLSParserTest03", TLSParserTest03, 1); + UtRegisterTest("TLSParserTest04", TLSParserTest04, 1); +} + +#endif /* UNITTESTS */ diff --git a/src/app-layer-tls.h b/src/app-layer-tls.h new file mode 100644 index 0000000000..26559f10b5 --- /dev/null +++ b/src/app-layer-tls.h @@ -0,0 +1,8 @@ +#ifndef __APP_LAYER_SSL_H__ +#define __APP_LAYER_SSL_H__ + +void RegisterTLSParsers(void); +void TLSParserRegisterTests(void); + +#endif /* __APP_LAYER_SSL_H__ */ + diff --git a/src/eidps.c b/src/eidps.c index 05beb90623..98cc4ad726 100644 --- a/src/eidps.c +++ b/src/eidps.c @@ -64,6 +64,7 @@ #include "app-layer-detect-proto.h" #include "app-layer-parser.h" #include "app-layer-http.h" +#include "app-layer-tls.h" #include "util-cidr.h" #include "util-unittest.h" @@ -928,6 +929,7 @@ int main(int argc, char **argv) AppLayerDetectProtoThreadInit(); RegisterAppLayerParsers(); RegisterHTTPParsers(); + RegisterTLSParsers(); AppLayerParsersInitPostProcess(); TmModuleReceiveNFQRegister(); @@ -968,6 +970,7 @@ int main(int argc, char **argv) PerfRegisterTests(); DecodePPPRegisterTests(); HTTPParserRegisterTests(); + TLSParserRegisterTests(); DecodePPPOERegisterTests(); DecodeICMPV4RegisterTests(); DecodeIPV4RegisterTests();