diff --git a/src/Makefile.am b/src/Makefile.am index e2369c70f7..b81bb7be56 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -87,6 +87,7 @@ detect-depth.c detect-depth.h \ detect-detection-filter.c detect-detection-filter.h \ detect-distance.c detect-distance.h \ detect-dns-query.c detect-dns-query.h \ +detect-tls-sni.c detect-tls-sni.h \ detect-dsize.c detect-dsize.h \ detect-engine-address.c detect-engine-address.h \ detect-engine-address-ipv4.c detect-engine-address-ipv4.h \ @@ -98,6 +99,7 @@ detect-engine.c detect-engine.h \ detect-engine-content-inspection.c detect-engine-content-inspection.h \ detect-engine-dcepayload.c detect-engine-dcepayload.h \ detect-engine-dns.c detect-engine-dns.h \ +detect-engine-tls.c detect-engine-tls.h \ detect-engine-modbus.c detect-engine-modbus.h \ detect-engine-event.c detect-engine-event.h \ detect-engine-file.c detect-engine-file.h \ diff --git a/src/detect-engine-analyzer.c b/src/detect-engine-analyzer.c index 80d76e607c..ade8ec0946 100644 --- a/src/detect-engine-analyzer.c +++ b/src/detect-engine-analyzer.c @@ -478,6 +478,8 @@ static void EngineAnalysisRulesPrintFP(const Signature *s) fprintf(rule_engine_analysis_FD, "http user agent content"); else if (list_type == DETECT_SM_LIST_DNSQUERYNAME_MATCH) fprintf(rule_engine_analysis_FD, "dns query name content"); + else if (list_type == DETECT_SM_LIST_TLSSNI_MATCH) + fprintf(rule_engine_analysis_FD, "tls sni extension content"); fprintf(rule_engine_analysis_FD, "\" buffer.\n"); diff --git a/src/detect-engine-content-inspection.h b/src/detect-engine-content-inspection.h index 5e80b9b8cc..7636fdf2c0 100644 --- a/src/detect-engine-content-inspection.h +++ b/src/detect-engine-content-inspection.h @@ -49,6 +49,7 @@ enum { DETECT_ENGINE_CONTENT_INSPECTION_MODE_HHHD, DETECT_ENGINE_CONTENT_INSPECTION_MODE_HRHHD, DETECT_ENGINE_CONTENT_INSPECTION_MODE_DNSQUERY, + DETECT_ENGINE_CONTENT_INSPECTION_MODE_TLSSNI, DETECT_ENGINE_CONTENT_INSPECTION_MODE_FD_SMTP, DETECT_ENGINE_CONTENT_INSPECTION_MODE_BASE64, DETECT_ENGINE_CONTENT_INSPECTION_MODE_TEMPLATE_BUFFER, diff --git a/src/detect-engine-mpm.c b/src/detect-engine-mpm.c index 75232c5034..04cca7844f 100644 --- a/src/detect-engine-mpm.c +++ b/src/detect-engine-mpm.c @@ -106,6 +106,8 @@ AppLayerMpms app_mpms[] = { { "dns_query", 0, SIG_FLAG_TOSERVER, DETECT_SM_LIST_DNSQUERYNAME_MATCH, SIG_GROUP_HEAD_MPM_DNSQUERY, 17}, + { "tls_sni", 0, SIG_FLAG_TOSERVER, DETECT_SM_LIST_TLSSNI_MATCH, SIG_GROUP_HEAD_MPM_TLSSNI, 18}, + { NULL, 0, 0, 0, 0, 0, } }; @@ -1207,7 +1209,9 @@ void MpmStoreFixup(SigGroupHead *sgh) SET_TS(sgh, sgh->mpm_dnsquery_ctx_ts); - BUG_ON(APP_MPMS_MAX != 18 || i != 18); + SET_TS(sgh, sgh->mpm_tlssni_ctx_ts); + + BUG_ON(APP_MPMS_MAX != 19 || i != 19); #undef SET_TS #undef SET_TC diff --git a/src/detect-engine-state.h b/src/detect-engine-state.h index c5264816ee..7b48f2c5b7 100644 --- a/src/detect-engine-state.h +++ b/src/detect-engine-state.h @@ -87,7 +87,8 @@ #define DE_STATE_FLAG_FD_SMTP_INSPECT (1 << 21) #define DE_STATE_FLAG_DNSREQUEST_INSPECT (1 << 22) #define DE_STATE_FLAG_DNSRESPONSE_INSPECT (1 << 23) -#define DE_STATE_FLAG_TEMPLATE_BUFFER_INSPECT (1 << 24) +#define DE_STATE_FLAG_TLSSNI_INSPECT (1 << 24) +#define DE_STATE_FLAG_TEMPLATE_BUFFER_INSPECT (1 << 25) /* state flags */ #define DETECT_ENGINE_STATE_FLAG_FILE_STORE_DISABLED 0x0001 diff --git a/src/detect-engine-tls.c b/src/detect-engine-tls.c new file mode 100644 index 0000000000..2cac96a01c --- /dev/null +++ b/src/detect-engine-tls.c @@ -0,0 +1,145 @@ +/* Copyright (C) 2016 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** \file + * + * \author Mats Klepsland + * + */ + +#include "suricata-common.h" +#include "suricata.h" +#include "decode.h" + +#include "detect.h" +#include "detect-engine.h" +#include "detect-parse.h" +#include "detect-engine-state.h" +#include "detect-engine-content-inspection.h" + +#include "flow-util.h" +#include "util-debug.h" +#include "util-print.h" +#include "flow.h" + +#include "app-layer.h" +#include "app-layer-parser.h" +#include "app-layer-protos.h" +#include "app-layer-ssl.h" + +#include "util-unittest.h" +#include "util-unittest-helper.h" +#include "util-validate.h" + +/** + * \brief TLS sni match -- searches for one pattern per signature. + * + * \param det_ctx Detection engine thread ctx + * \param hrh Buffer to inspect + * \param hrh_len Buffer length + * \param flags Flags + * + * \retval ret Number of matches + */ +static inline uint32_t TlsSniPatternSearch(DetectEngineThreadCtx *det_ctx, + const uint8_t *buffer, + const uint32_t buffer_len, + const uint8_t flags) +{ + SCEnter(); + + uint32_t ret = 0; + + DEBUG_VALIDATE_BUG_ON(flags & STREAM_TOCLIENT); + DEBUG_VALIDATE_BUG_ON(det_ctx->sgh->mpm_tlssni_ctx_ts == NULL); + + if (buffer_len >= det_ctx->sgh->mpm_tlssni_ctx_ts->minlen) { + ret = mpm_table[det_ctx->sgh->mpm_tlssni_ctx_ts->mpm_type]. + Search(det_ctx->sgh->mpm_tlssni_ctx_ts, &det_ctx->mtcu, + &det_ctx->pmq, buffer, buffer_len); + } + + SCReturnUInt(ret); +} + +/** + * \brief Run the pattern matcher against the SNI buffer + * + * \param det_ctx Detection engine thread ctx + * \param f Locked flow + * \param dns_state Initialized dns state + * \param flags Flags + * + * \retval cnt Number of matches + */ +uint32_t DetectTlsSniInspectMpm(DetectEngineThreadCtx *det_ctx, Flow *f, + SSLState *ssl_state, uint8_t flags) +{ + SCEnter(); + + uint8_t *buffer; + uint32_t buffer_len; + uint32_t cnt = 0; + + if (ssl_state->client_connp.sni == NULL) + return 0; + + buffer = (uint8_t *)ssl_state->client_connp.sni; + buffer_len = strlen(ssl_state->client_connp.sni); + + cnt = TlsSniPatternSearch(det_ctx, buffer, buffer_len, flags); + + SCReturnUInt(cnt); +} + +/** \brief Do the content inspection and validation for a signature + * + * \param de_ctx Detection engine context + * \param det_ctx Detection engine thread context + * \param s Signature to inspect + * \param sm SigMatch to inspect + * \param f Flow + * \param flags App layer flags + * \param state App layer state + * + * \retval 0 No match + * \retval 1 Match + */ +int DetectEngineInspectTlsSni(ThreadVars *tv, DetectEngineCtx *de_ctx, + DetectEngineThreadCtx *det_ctx, Signature *s, + Flow *f, uint8_t flags, void *alstate, void *txv, + uint64_t tx_id) +{ + uint8_t *buffer; + uint16_t buffer_len; + int cnt = 0; + + SSLState *ssl_state = (SSLState *)alstate; + + if (ssl_state->client_connp.sni == NULL) + return 0; + + buffer = (uint8_t *)ssl_state->client_connp.sni; + buffer_len = strlen(ssl_state->client_connp.sni); + + cnt = DetectEngineContentInspection(de_ctx, det_ctx, s, + s->sm_lists[DETECT_SM_LIST_TLSSNI_MATCH], + f, buffer, buffer_len, 0, + DETECT_ENGINE_CONTENT_INSPECTION_MODE_TLSSNI, NULL); + + return cnt; +} diff --git a/src/detect-engine-tls.h b/src/detect-engine-tls.h new file mode 100644 index 0000000000..3a9d530f0c --- /dev/null +++ b/src/detect-engine-tls.h @@ -0,0 +1,31 @@ +/* Copyright (C) 2016 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** \file + * + * \author Mats Klepsland + */ + +#ifndef __DETECT_ENGINE_TLS_H__ +#define __DETECT_ENGINE_TLS_H__ + +int DetectEngineInspectTlsSni(ThreadVars *tv, DetectEngineCtx *de_ctx, + DetectEngineThreadCtx *det_ctx, + Signature *s, Flow *f, uint8_t flags, + void *alstate, void *txv, uint64_t tx_id); + +#endif /* __DETECT_ENGINE_TLS_H__ */ diff --git a/src/detect-engine.c b/src/detect-engine.c index 1546aef3ee..6a66a45d9f 100644 --- a/src/detect-engine.c +++ b/src/detect-engine.c @@ -59,6 +59,7 @@ #include "detect-engine-hrhhd.h" #include "detect-engine-file.h" #include "detect-engine-dns.h" +#include "detect-engine-tls.h" #include "detect-engine-modbus.h" #include "detect-engine-filedata-smtp.h" #include "detect-engine-template.h" @@ -269,6 +270,13 @@ void DetectEngineRegisterAppInspectionEngines(void) DE_STATE_FLAG_DNSREQUEST_INSPECT, 0, DetectEngineInspectDnsRequest }, + /* TLS */ + { IPPROTO_TCP, + ALPROTO_TLS, + DETECT_SM_LIST_TLSSNI_MATCH, + DE_STATE_FLAG_TLSSNI_INSPECT, + 0, + DetectEngineInspectTlsSni }, /* SMTP */ { IPPROTO_TCP, ALPROTO_SMTP, @@ -2726,6 +2734,9 @@ const char *DetectSigmatchListEnumToString(enum DetectSigmatchListEnum type) case DETECT_SM_LIST_DNSRESPONSE_MATCH: return "dns response"; + case DETECT_SM_LIST_TLSSNI_MATCH: + return "tls sni extension"; + case DETECT_SM_LIST_MODBUS_MATCH: return "modbus"; diff --git a/src/detect-fast-pattern.c b/src/detect-fast-pattern.c index dff7a4cc4a..dc7d94c64e 100644 --- a/src/detect-fast-pattern.c +++ b/src/detect-fast-pattern.c @@ -136,6 +136,8 @@ void SupportFastPatternForSigMatchTypes(void) SupportFastPatternForSigMatchList(DETECT_SM_LIST_DNSQUERYNAME_MATCH, 2); + SupportFastPatternForSigMatchList(DETECT_SM_LIST_TLSSNI_MATCH, 2); + #if 0 SCFPSupportSMList *tmp = sm_fp_support_smlist_list; while (tmp != NULL) { @@ -202,15 +204,16 @@ static int DetectFastPatternSetup(DetectEngineCtx *de_ctx, Signature *s, char *a s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH] == NULL && s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH] == NULL && s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH] == NULL && - s->sm_lists_tail[DETECT_SM_LIST_DNSQUERYNAME_MATCH] == NULL) { + s->sm_lists_tail[DETECT_SM_LIST_DNSQUERYNAME_MATCH] == NULL && + s->sm_lists_tail[DETECT_SM_LIST_TLSSNI_MATCH] == NULL) { SCLogWarning(SC_WARN_COMPATIBILITY, "fast_pattern found inside the " "rule, without a preceding content based keyword. " "Currently we provide fast_pattern support for content, " "uricontent, http_client_body, http_server_body, http_header, " "http_raw_header, http_method, http_cookie, " "http_raw_uri, http_stat_msg, http_stat_code, " - "http_user_agent, http_host, http_raw_host or " - "dns_query option"); + "http_user_agent, http_host, http_raw_host, " + "dns_query or tls_sni option"); return -1; } @@ -229,7 +232,8 @@ static int DetectFastPatternSetup(DetectEngineCtx *de_ctx, Signature *s, char *a DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH], DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH], DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH], - DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_DNSQUERYNAME_MATCH]); + DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_DNSQUERYNAME_MATCH], + DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_TLSSNI_MATCH]); if (pm == NULL) { SCLogError(SC_ERR_INVALID_SIGNATURE, "fast_pattern found inside " "the rule, without a content context. Please use a " diff --git a/src/detect-parse.c b/src/detect-parse.c index d86826cdf1..4adef8f84e 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -167,6 +167,7 @@ const char *DetectListToHumanString(int list) CASE_CODE_STRING(DETECT_SM_LIST_DNSREQUEST_MATCH, "dns_request"); CASE_CODE_STRING(DETECT_SM_LIST_DNSRESPONSE_MATCH, "dns_response"); CASE_CODE_STRING(DETECT_SM_LIST_DNSQUERYNAME_MATCH, "dns_query"); + CASE_CODE_STRING(DETECT_SM_LIST_TLSSNI_MATCH, "tls_sni"); CASE_CODE_STRING(DETECT_SM_LIST_MODBUS_MATCH, "modbus"); CASE_CODE_STRING(DETECT_SM_LIST_TEMPLATE_BUFFER_MATCH, "template"); CASE_CODE_STRING(DETECT_SM_LIST_POSTMATCH, "postmatch"); @@ -207,6 +208,7 @@ const char *DetectListToString(int list) CASE_CODE(DETECT_SM_LIST_DNSREQUEST_MATCH); CASE_CODE(DETECT_SM_LIST_DNSRESPONSE_MATCH); CASE_CODE(DETECT_SM_LIST_DNSQUERYNAME_MATCH); + CASE_CODE(DETECT_SM_LIST_TLSSNI_MATCH); CASE_CODE(DETECT_SM_LIST_MODBUS_MATCH); CASE_CODE(DETECT_SM_LIST_TEMPLATE_BUFFER_MATCH); CASE_CODE(DETECT_SM_LIST_POSTMATCH); @@ -1554,6 +1556,10 @@ static Signature *SigInitHelper(DetectEngineCtx *de_ctx, char *sigstr, sig->flags |= SIG_FLAG_STATE_MATCH; } + /* TLS */ + if (sig->sm_lists[DETECT_SM_LIST_TLSSNI_MATCH]) + sig->flags |= SIG_FLAG_STATE_MATCH; + if (sig->sm_lists[DETECT_SM_LIST_MODBUS_MATCH]) sig->flags |= SIG_FLAG_STATE_MATCH; if (sig->sm_lists[DETECT_SM_LIST_APP_EVENT]) diff --git a/src/detect-tls-sni.c b/src/detect-tls-sni.c new file mode 100644 index 0000000000..31c001c1f7 --- /dev/null +++ b/src/detect-tls-sni.c @@ -0,0 +1,341 @@ +/* Copyright (C) 2007-2016 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Mats Klepsland + * + * Implements support for tls_sni keyword. + */ + +#include "suricata-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 "detect-content.h" +#include "detect-pcre.h" + +#include "flow.h" +#include "flow-util.h" +#include "flow-var.h" + +#include "util-debug.h" +#include "util-unittest.h" +#include "util-spm.h" +#include "util-print.h" + +#include "stream-tcp.h" + +#include "app-layer.h" +#include "app-layer-ssl.h" + +#include "util-unittest.h" +#include "util-unittest-helper.h" + +static int DetectTlsSniSetup(DetectEngineCtx *, Signature *, char *); +static void DetectTlsSniRegisterTests(void); + +/** + * \brief Registration function for keyword: tls_sni + */ +void DetectTlsSniRegister(void) +{ + sigmatch_table[DETECT_AL_TLS_SNI].name = "tls_sni"; + sigmatch_table[DETECT_AL_TLS_SNI].desc = "content modifier to match specifically and only on the TLS SNI buffer"; + sigmatch_table[DETECT_AL_TLS_SNI].Match = NULL; + sigmatch_table[DETECT_AL_TLS_SNI].AppLayerMatch = NULL; + sigmatch_table[DETECT_AL_TLS_SNI].alproto = ALPROTO_TLS; + sigmatch_table[DETECT_AL_TLS_SNI].Setup = DetectTlsSniSetup; + sigmatch_table[DETECT_AL_TLS_SNI].Free = NULL; + sigmatch_table[DETECT_AL_TLS_SNI].RegisterTests = DetectTlsSniRegisterTests; + + sigmatch_table[DETECT_AL_TLS_SNI].flags |= SIGMATCH_NOOPT; + sigmatch_table[DETECT_AL_TLS_SNI].flags |= SIGMATCH_PAYLOAD; +} + + +/** + * \brief this function setup the tls_sni modifier keyword used in the rule + * + * \param de_ctx Pointer to the Detection Engine Context + * \param s Pointer to the Signature to which the current keyword belongs + * \param str Should hold an empty string always + * + * \retval 0 On success + */ +static int DetectTlsSniSetup(DetectEngineCtx *de_ctx, Signature *s, char *str) +{ + s->list = DETECT_SM_LIST_TLSSNI_MATCH; + s->alproto = ALPROTO_TLS; + return 0; +} + +#ifdef UNITTESTS + +/** + * \test Test matching on a simple google.com SNI + */ +static int DetectTlsSniTest01(void) +{ + /* client hello */ + uint8_t buf[] = { 0x16, 0x03, 0x03, 0x00, 0xAE, 0x01, 0x00, 0x00, 0xAA, + 0x03, 0x03, 0x57, 0x04, 0x9F, 0x5D, 0xC9, 0x5C, 0x87, + 0xAE, 0xF2, 0xA7, 0x4A, 0xFC, 0x59, 0x78, 0x23, 0x31, + 0x61, 0x2D, 0x29, 0x92, 0xB6, 0x70, 0xA5, 0xA1, 0xFC, + 0x0E, 0x79, 0xFE, 0xC3, 0x97, 0x37, 0xC0, 0x00, 0x00, + 0x44, 0x00, 0x04, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x0D, + 0x00, 0x10, 0x00, 0x13, 0x00, 0x16, 0x00, 0x2F, 0x00, + 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, + 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, + 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F, 0x00, 0x40, + 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x66, 0x00, + 0x67, 0x00, 0x68, 0x00, 0x69, 0x00, 0x6A, 0x00, 0x6B, + 0x00, 0x84, 0x00, 0x87, 0x00, 0xFF, 0x01, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0D, 0x00, 0x00, + 0x0A, 0x67, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x2E, 0x63, + 0x6F, 0x6D, }; + + int result = 0; + Flow f; + SSLState *ssl_state = NULL; + Packet *p = NULL; + Signature *s = NULL; + ThreadVars tv; + DetectEngineThreadCtx *det_ctx = NULL; + TcpSession ssn; + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&f, 0, sizeof(Flow)); + memset(&ssn, 0, sizeof(TcpSession)); + + p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 443); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.flags |= FLOW_IPV4; + f.proto = IPPROTO_TCP; + f.protomap = FlowGetProtoMapping(f.proto); + + p->flow = &f; + p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + p->flowflags |= FLOW_PKT_TOSERVER|FLOW_PKT_ESTABLISHED; + f.alproto = ALPROTO_TLS; + + StreamTcpInitConfig(TRUE); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + de_ctx->mpm_matcher = DEFAULT_MPM; + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert tls any any -> any any " + "(msg:\"Test tls_sni option\"; " + "tls_sni; content:\"google.com\"; sid:1;)"); + if (s == NULL) { + goto end; + } + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf, sizeof(buf)); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + ssl_state = f.alstate; + if (ssl_state == NULL) { + printf("no ssl state: "); + goto end; + } + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + if (!(PacketAlertCheck(p, 1))) { + printf("sig 1 didn't alert, but it should have: "); + goto end; + } + + result = 1; + +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&tv, det_ctx); + if (de_ctx != NULL) + SigGroupCleanup(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + +/** + * \test Test matching on a simple google.com SNI with pcre + */ +static int DetectTlsSniTest02(void) +{ + /* client hello */ + uint8_t buf[] = { 0x16, 0x03, 0x03, 0x00, 0xAE, 0x01, 0x00, 0x00, 0xAA, + 0x03, 0x03, 0x57, 0x04, 0x9F, 0x5D, 0xC9, 0x5C, 0x87, + 0xAE, 0xF2, 0xA7, 0x4A, 0xFC, 0x59, 0x78, 0x23, 0x31, + 0x61, 0x2D, 0x29, 0x92, 0xB6, 0x70, 0xA5, 0xA1, 0xFC, + 0x0E, 0x79, 0xFE, 0xC3, 0x97, 0x37, 0xC0, 0x00, 0x00, + 0x44, 0x00, 0x04, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x0D, + 0x00, 0x10, 0x00, 0x13, 0x00, 0x16, 0x00, 0x2F, 0x00, + 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, + 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, + 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F, 0x00, 0x40, + 0x00, 0x41, 0x00, 0x44, 0x00, 0x45, 0x00, 0x66, 0x00, + 0x67, 0x00, 0x68, 0x00, 0x69, 0x00, 0x6A, 0x00, 0x6B, + 0x00, 0x84, 0x00, 0x87, 0x00, 0xFF, 0x01, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0D, 0x00, 0x00, + 0x0A, 0x67, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x2E, 0x63, + 0x6F, 0x6D, }; + + int result = 0; + Flow f; + SSLState *ssl_state = NULL; + Packet *p = NULL; + Signature *s = NULL; + ThreadVars tv; + DetectEngineThreadCtx *det_ctx = NULL; + TcpSession ssn; + AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc(); + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&f, 0, sizeof(Flow)); + memset(&ssn, 0, sizeof(TcpSession)); + + p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 443); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.flags |= FLOW_IPV4; + f.proto = IPPROTO_TCP; + f.protomap = FlowGetProtoMapping(f.proto); + + p->flow = &f; + p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST; + p->flowflags |= FLOW_PKT_TOSERVER|FLOW_PKT_ESTABLISHED; + f.alproto = ALPROTO_TLS; + + StreamTcpInitConfig(TRUE); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + de_ctx->mpm_matcher = DEFAULT_MPM; + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert tls any any -> any any " + "(msg:\"Test tls_sni option\"; " + "tls_sni; content:\"google\"; nocase; " + "pcre:\"/google\\.com$/i\"; sid:1;)"); + if (s == NULL) { + goto end; + } + + s = DetectEngineAppendSig(de_ctx, "alert tls any any -> any any " + "(msg:\"Test tls_sni option\"; " + "tls_sni; content:\"google\"; nocase; " + "pcre:\"/^\\.[a-z]{2,3}$/iR\"; sid:2;)"); + if (s == NULL) { + goto end; + } + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx); + + SCMutexLock(&f.m); + int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_TLS, STREAM_TOSERVER, buf, sizeof(buf)); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + SCMutexUnlock(&f.m); + goto end; + } + SCMutexUnlock(&f.m); + + ssl_state = f.alstate; + if (ssl_state == NULL) { + printf("no ssl state: "); + goto end; + } + + /* do detect */ + SigMatchSignatures(&tv, de_ctx, det_ctx, p); + + if (!(PacketAlertCheck(p, 1))) { + printf("sig 1 didn't alert, but it should have: "); + goto end; + } + + if (!(PacketAlertCheck(p, 2))) { + printf("sig 2 didn't alert, but it should have: "); + goto end; + } + + result = 1; + +end: + if (alp_tctx != NULL) + AppLayerParserThreadCtxFree(alp_tctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&tv, det_ctx); + if (de_ctx != NULL) + SigGroupCleanup(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + +#endif + +static void DetectTlsSniRegisterTests(void) +{ +#ifdef UNITTESTS + UtRegisterTest("DetectTlsSniTest01", DetectTlsSniTest01); + UtRegisterTest("DetectTlsSniTest02", DetectTlsSniTest02); +#endif +} diff --git a/src/detect-tls-sni.h b/src/detect-tls-sni.h new file mode 100644 index 0000000000..8e6d93086c --- /dev/null +++ b/src/detect-tls-sni.h @@ -0,0 +1,33 @@ +/* Copyright (C) 2016 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Mats Klepsland + */ + +#ifndef __DETECT_TLS_SNI_H__ +#define __DETECT_TLS_SNI_H__ + +#include "app-layer-ssl.h" + +void DetectTlsSniRegister(void); +uint32_t DetectTlsSniInspectMpm(DetectEngineThreadCtx *det_ctx, Flow *f, + SSLState *ssl_state, uint8_t flags); + +#endif /* __DETECT_TLS_SNI_H__ */ diff --git a/src/detect.c b/src/detect.c index 60ca9795f5..3c5929516b 100644 --- a/src/detect.c +++ b/src/detect.c @@ -48,6 +48,7 @@ #include "detect-engine-dcepayload.h" #include "detect-engine-uri.h" #include "detect-dns-query.h" +#include "detect-tls-sni.h" #include "detect-engine-state.h" #include "detect-engine-analyzer.h" #include "detect-engine-filedata-smtp.h" @@ -1065,6 +1066,24 @@ static inline void DetectMpmPrefilter(DetectEngineCtx *de_ctx, DetectDnsQueryInspectMpm(det_ctx, p->flow, alstate, flags, tx, idx); PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_DNSQUERY); } + FLOWLOCK_UNLOCK(p->flow); + } + } + } else if (alproto == ALPROTO_TLS && has_state) { + if (p->flowflags & FLOW_PKT_TOSERVER) { + if (det_ctx->sgh->flags & SIG_GROUP_HEAD_MPM_TLSSNI) { + FLOWLOCK_RDLOCK(p->flow); + void *alstate = FlowGetAppState(p->flow); + if (alstate == NULL) { + SCLogDebug("no alstate"); + FLOWLOCK_UNLOCK(p->flow); + return; + } + + PACKET_PROFILING_DETECT_START(p, PROF_DETECT_MPM_TLSSNI); + DetectTlsSniInspectMpm(det_ctx, p->flow, alstate, flags); + PACKET_PROFILING_DETECT_END(p, PROF_DETECT_MPM_TLSSNI); + FLOWLOCK_UNLOCK(p->flow); } } @@ -4407,6 +4426,7 @@ void SigTableSetup(void) DetectLuaRegister(); DetectIPRepRegister(); DetectDnsQueryRegister(); + DetectTlsSniRegister(); DetectModbusRegister(); DetectAppLayerProtocolRegister(); DetectBase64DecodeRegister(); diff --git a/src/detect.h b/src/detect.h index 333381457b..90c6502984 100644 --- a/src/detect.h +++ b/src/detect.h @@ -124,6 +124,8 @@ enum DetectSigmatchListEnum { DETECT_SM_LIST_DNSRESPONSE_MATCH, /**< per DNS response tx match list */ DETECT_SM_LIST_DNSQUERYNAME_MATCH, /**< per query in a tx list */ + DETECT_SM_LIST_TLSSNI_MATCH, + DETECT_SM_LIST_MODBUS_MATCH, DETECT_SM_LIST_BASE64_DATA, @@ -929,9 +931,10 @@ typedef struct SigTableElmt_ { #define SIG_GROUP_HEAD_HAVEFILEMD5 (1 << 21) #define SIG_GROUP_HEAD_HAVEFILESIZE (1 << 22) #define SIG_GROUP_HEAD_MPM_DNSQUERY (1 << 23) -#define SIG_GROUP_HEAD_MPM_FD_SMTP (1 << 24) +#define SIG_GROUP_HEAD_MPM_TLSSNI (1 << 24) +#define SIG_GROUP_HEAD_MPM_FD_SMTP (1 << 25) -#define APP_MPMS_MAX 18 +#define APP_MPMS_MAX 19 enum MpmBuiltinBuffers { MPMB_TCP_PKT_TS, @@ -1009,6 +1012,7 @@ typedef struct SigGroupHead_ { const MpmCtx *mpm_hhhd_ctx_ts; const MpmCtx *mpm_hrhhd_ctx_ts; const MpmCtx *mpm_dnsquery_ctx_ts; + const MpmCtx *mpm_tlssni_ctx_ts; const MpmCtx *mpm_smtp_filedata_ctx_ts; }; struct { @@ -1209,6 +1213,7 @@ enum { DETECT_IPREP, DETECT_AL_DNS_QUERY, + DETECT_AL_TLS_SNI, DETECT_AL_MODBUS, DETECT_XBITS, diff --git a/src/suricata-common.h b/src/suricata-common.h index 18791c3b04..141e8671d9 100644 --- a/src/suricata-common.h +++ b/src/suricata-common.h @@ -320,6 +320,7 @@ typedef enum PacketProfileDetectId_ { PROF_DETECT_MPM_HHHD, PROF_DETECT_MPM_HRHHD, PROF_DETECT_MPM_DNSQUERY, + PROF_DETECT_MPM_TLSSNI, PROF_DETECT_IPONLY, PROF_DETECT_RULES, PROF_DETECT_STATEFUL, diff --git a/src/util-profiling.c b/src/util-profiling.c index ee25076e8c..f63ee92987 100644 --- a/src/util-profiling.c +++ b/src/util-profiling.c @@ -1028,6 +1028,7 @@ const char * PacketProfileDetectIdToString(PacketProfileDetectId id) CASE_CODE (PROF_DETECT_MPM_HSCD); CASE_CODE (PROF_DETECT_MPM_HUAD); CASE_CODE (PROF_DETECT_MPM_DNSQUERY); + CASE_CODE (PROF_DETECT_MPM_TLSSNI); CASE_CODE (PROF_DETECT_IPONLY); CASE_CODE (PROF_DETECT_RULES); CASE_CODE (PROF_DETECT_PREFILTER);