diff --git a/src/Makefile.am b/src/Makefile.am index 25952fcc98..7bf3ffb38c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -145,6 +145,7 @@ detect-http-raw-header.c detect-http-raw-header.h \ detect-http-uri.c detect-http-uri.h \ detect-http-raw-uri.c detect-http-raw-uri.h \ detect-file-data.c detect-file-data.h \ +detect-tls.c detect-tls.h \ detect-tls-version.c detect-tls-version.h \ detect-ssh-proto-version.c detect-ssh-proto-version.h \ detect-ssh-software-version.c detect-ssh-software-version.h \ @@ -282,6 +283,7 @@ app-layer-dcerpc-udp.c app-layer-dcerpc-udp.h \ app-layer-ftp.c app-layer-ftp.h \ app-layer-ssl.c app-layer-ssl.h \ app-layer-ssh.c app-layer-ssh.h \ +app-layer-tls-handshake.c app-layer-tls-handshake.h \ app-layer-smtp.c app-layer-smtp.h \ defrag.c defrag.h \ output.c output.h \ diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c index 80d57499c3..9988f8b339 100644 --- a/src/app-layer-ssl.c +++ b/src/app-layer-ssl.c @@ -19,6 +19,7 @@ * \file * * \author Anoop Saldanha + * \author Pierre Chifflier * */ @@ -39,6 +40,8 @@ #include "app-layer-parser.h" #include "app-layer-ssl.h" +#include "app-layer-tls-handshake.h" + #include "conf.h" #include "util-spm.h" @@ -98,6 +101,7 @@ static int SSLv3ParseHandshakeType(SSLState *ssl_state, uint8_t *input, { uint8_t *initial_input = input; uint32_t parsed = 0; + int rc; if (input_len == 0) { return 0; @@ -146,8 +150,37 @@ static int SSLv3ParseHandshakeType(SSLState *ssl_state, uint8_t *input, ssl_state->flags |= SSL_AL_FLAG_STATE_CLIENT_KEYX; break; - case SSLV3_HS_HELLO_REQUEST: case SSLV3_HS_CERTIFICATE: + if (ssl_state->trec == NULL) { + ssl_state->trec_len = 2 * ssl_state->record_length + SSLV3_RECORD_LEN + 1; + ssl_state->trec = SCMalloc( ssl_state->trec_len ); + } + if (ssl_state->trec_pos + input_len >= ssl_state->trec_len) { + ssl_state->trec_len = ssl_state->trec_len + 2 * input_len + 1; + ssl_state->trec = SCRealloc( ssl_state->trec, ssl_state->trec_len ); + } + if (ssl_state->trec == NULL) { + ssl_state->trec_len = 0; + /* error, skip packet */ + parsed += input_len; + ssl_state->bytes_processed += input_len; + break; + } + memcpy(ssl_state->trec + ssl_state->trec_pos, initial_input, input_len); + ssl_state->trec_pos += input_len; + + rc = DecodeTLSHandshakeServerCertificate(ssl_state, ssl_state->trec, ssl_state->trec_pos); + if (rc > 0) { + /* packet is incomplete - do not mark as parsed */ + } + if (rc < 0) { + /* error, skip packet */ + parsed += input_len; + ssl_state->bytes_processed += input_len; + return parsed; + } + break; + case SSLV3_HS_HELLO_REQUEST: case SSLV3_HS_CERTIFICATE_REQUEST: case SSLV3_HS_CERTIFICATE_VERIFY: case SSLV3_HS_FINISHED: @@ -188,6 +221,7 @@ static int SSLv3ParseHandshakeProtocol(SSLState *ssl_state, uint8_t *input, case 5: if (input_len >= 4) { ssl_state->handshake_type = *(input++); + // XXX we should *not* skip the next 3 bytes, they contain the Message length input += 3; input_len -= 4; ssl_state->bytes_processed += 4; @@ -431,7 +465,7 @@ static int SSLv2Decode(uint8_t direction, SSLState *ssl_state, switch (ssl_state->cur_content_type) { case SSLV2_MT_ERROR: - SCLogWarning(SC_ERR_ALPARSER, "SSLV2_MT_ERROR msg_type recived. " + SCLogWarning(SC_ERR_ALPARSER, "SSLV2_MT_ERROR msg_type received. " "Error encountered in establishing the sslv2 " "session, may be version"); @@ -851,7 +885,12 @@ void *SSLStateAlloc(void) */ void SSLStateFree(void *p) { - SCFree(p); + SSLState *ssl_state = (SSLState *)p; + + if (ssl_state->trec) + SCFree(ssl_state->trec); + SCFree(ssl_state->cert0_subject); + SCFree(ssl_state); return; } diff --git a/src/app-layer-ssl.h b/src/app-layer-ssl.h index ecea5792ac..518956cbcb 100644 --- a/src/app-layer-ssl.h +++ b/src/app-layer-ssl.h @@ -92,6 +92,14 @@ typedef struct SSLState_ { /* sslv2 client hello session id length */ uint16_t session_id_length; + + char *cert0_subject; + + /* buffer for the tls record. + * We use a malloced buffer, if the record is fragmented */ + uint8_t *trec; + uint16_t trec_len; + uint16_t trec_pos; } SSLState; void RegisterSSLParsers(void); diff --git a/src/app-layer-tls-handshake.c b/src/app-layer-tls-handshake.c new file mode 100644 index 0000000000..53a4757a3b --- /dev/null +++ b/src/app-layer-tls-handshake.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2011-2012 ANSSI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * \author Pierre Chifflier + * + * \brief Decode TLS Handshake messages, as described in RFC2246 + * + */ + +#include "suricata-common.h" +#include "debug.h" +#include "decode.h" + +#include "app-layer-ssl.h" + +#include "app-layer-tls-handshake.h" + +#include + +#include "util-decode-der.h" +#include "util-decode-der-get.h" + +#define SSLV3_RECORD_LEN 5 + +int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uint32_t input_len) +{ + uint32_t certificates_length, cur_cert_length; + int i; + Asn1Generic *cert; + char subject[256]; + int rc; + + if (input_len < 3) + return 1; + + certificates_length = input[0]<<16 | input[1]<<8 | input[2]; + /* check if the message is complete */ + if (input_len < certificates_length + 3) + return 1; + + input += 3; + ssl_state->bytes_processed += 3; + + i = 0; + while (certificates_length > 0) { + cur_cert_length = input[0]<<16 | input[1]<<8 | input[2]; + input += 3; + ssl_state->bytes_processed += 3; + + cert = DecodeDer(input, cur_cert_length); + if (cert == NULL) { + SCLogWarning(SC_ERR_ALPARSER, "decoding ASN.1 structure for X509 certificate failed\n"); + } + if (cert != NULL) { + rc = Asn1DerGetSubjectDN(cert, subject, sizeof(subject)); + if (rc != 0) { + SCLogWarning(SC_ERR_ALPARSER, "X509: could not get subject\n"); + } else { + //SCLogInfo("TLS Cert %d: %s\n", i, subject); + if (i==0) { + ssl_state->cert0_subject = SCStrdup(subject); + } + } + DerFree(cert); + } + + i++; + certificates_length -= (cur_cert_length + 3); + ssl_state->bytes_processed += cur_cert_length; + input += cur_cert_length; + } + + ssl_state->bytes_processed = input_len; + return 0; +} diff --git a/src/app-layer-tls-handshake.h b/src/app-layer-tls-handshake.h new file mode 100644 index 0000000000..6041f7fbc5 --- /dev/null +++ b/src/app-layer-tls-handshake.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011-2012 ANSSI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * \author Pierre Chifflier + * + */ + +#ifndef __APP_LAYER_TLS_HANDSHAKE_H__ +#define __APP_LAYER_TLS_HANDSHAKE_H__ + +int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uint32_t input_len); + +#endif /* __APP_LAYER_TLS_HANDSHAKE_H__ */ diff --git a/src/detect-tls.c b/src/detect-tls.c new file mode 100644 index 0000000000..01ccab74f5 --- /dev/null +++ b/src/detect-tls.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2011-2012 ANSSI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * \author Pierre Chifflier + * + * Implements the tls.* keywords + */ + +#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-engine-state.h" + +#include "flow.h" +#include "flow-var.h" +#include "flow-util.h" + +#include "util-debug.h" +#include "util-unittest.h" +#include "util-unittest-helper.h" + +#include "app-layer.h" + +#include "app-layer-ssl.h" +#include "detect-tls.h" + +#include "stream-tcp.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; + +static int DetectTlsSubjectMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *); +static int DetectTlsSubjectSetup (DetectEngineCtx *, Signature *, char *); +static void DetectTlsSubjectRegisterTests(void); +static void DetectTlsSubjectFree(void *); + +/** + * \brief Registration function for keyword: tls.version + */ +void DetectTlsRegister (void) { + sigmatch_table[DETECT_AL_TLS_SUBJECT].name = "tls.subject"; + sigmatch_table[DETECT_AL_TLS_SUBJECT].Match = NULL; + sigmatch_table[DETECT_AL_TLS_SUBJECT].AppLayerMatch = DetectTlsSubjectMatch; + sigmatch_table[DETECT_AL_TLS_SUBJECT].alproto = ALPROTO_TLS; + sigmatch_table[DETECT_AL_TLS_SUBJECT].Setup = DetectTlsSubjectSetup; + sigmatch_table[DETECT_AL_TLS_SUBJECT].Free = DetectTlsSubjectFree; + sigmatch_table[DETECT_AL_TLS_SUBJECT].RegisterTests = DetectTlsSubjectRegisterTests; + + const char *eb; + int eo; + int opts = 0; + + SCLogDebug("registering tls.subject rule option"); + + parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL); + if (parse_regex == NULL) { + SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s", + PARSE_REGEX, eo, eb); + goto error; + } + + parse_regex_study = pcre_study(parse_regex, 0, &eb); + if (eb != NULL) { + SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb); + goto error; + } + return; + +error: + return; +} + +/** + * \brief match the specified Subject 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 DetectTlsData + * + * \retval 0 no match + * \retval 1 match + */ +static int DetectTlsSubjectMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m) +{ + SCEnter(); + + DetectTlsData *tls_data = (DetectTlsData *)m->ctx; + SSLState *ssl_state = (SSLState *)state; + if (ssl_state == NULL) { + SCLogDebug("no tls state, no match"); + SCReturnInt(0); + } + + int ret = 0; + SCMutexLock(&f->m); + + if (ssl_state->cert0_subject != NULL) { + SCLogDebug("TLS: Subject is [%s], looking for [%s]\n", ssl_state->cert0_subject, tls_data->subject); + + if (strstr(ssl_state->cert0_subject, tls_data->subject) != NULL) { + ret = 1; + } + } + + SCMutexUnlock(&f->m); + + SCReturnInt(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 DetectTlsData on success + * \retval NULL on failure + */ +static DetectTlsData *DetectTlsSubjectParse (char *str) +{ + DetectTlsData *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) { + SCLogError(SC_ERR_PCRE_MATCH, "invalid tls.subject 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) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + /* We have a correct id option */ + tls = SCMalloc(sizeof(DetectTlsData)); + if (tls == NULL) + goto error; + tls->subject = NULL; + + orig = SCStrdup((char*)str_ptr); + tmp_str=orig; + if (tmp_str == NULL) { + goto error; + } + + /* Let's see if we need to escape "'s */ + if (tmp_str[0] == '"') + { + tmp_str[strlen(tmp_str) - 1] = '\0'; + tmp_str += 1; + } + + tls->subject = SCStrdup(tmp_str); + + SCFree(orig); + + SCLogDebug("will look for TLS subject %s", tls->subject); + } + + return tls; + +error: + if (tls != NULL) + DetectTlsSubjectFree(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 idstr pointer to the user provided "id" option + * + * \retval 0 on Success + * \retval -1 on Failure + */ +static int DetectTlsSubjectSetup (DetectEngineCtx *de_ctx, Signature *s, char *str) +{ + DetectTlsData *tls = NULL; + SigMatch *sm = NULL; + + tls = DetectTlsSubjectParse(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_SUBJECT; + sm->ctx = (void *)tls; + + SigMatchAppendAppLayer(s, sm); + + if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS) { + SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords."); + goto error; + } + + s->alproto = ALPROTO_TLS; + return 0; + +error: + if (tls != NULL) + DetectTlsSubjectFree(tls); + if (sm != NULL) + SCFree(sm); + return -1; + +} + +/** + * \brief this function will free memory associated with DetectTlsData + * + * \param id_d pointer to DetectTlsData + */ +static void DetectTlsSubjectFree(void *ptr) { + DetectTlsData *id_d = (DetectTlsData *)ptr; + if (ptr == NULL) + return; + if (id_d->subject != NULL) + SCFree(id_d->subject); + SCFree(id_d); +} + +/** + * \brief this function registers unit tests for DetectTlsSubject + */ +static void DetectTlsSubjectRegisterTests(void) { +} diff --git a/src/detect-tls.h b/src/detect-tls.h new file mode 100644 index 0000000000..cc57cbed92 --- /dev/null +++ b/src/detect-tls.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011-2012 ANSSI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * \author Pierre Chifflier + */ + +#ifndef __DETECT_TLS_H__ +#define __DETECT_TLS_H__ + +typedef struct DetectTlsData_ { + uint16_t ver; /** tls version to match */ + char * subject; /** tls certificate subject substring to match */ +} DetectTlsData; + +/* prototypes */ +void DetectTlsRegister (void); + +#endif /* __DETECT_TLS_H__ */ diff --git a/src/detect.c b/src/detect.c index ce39d38e19..b6be898826 100644 --- a/src/detect.c +++ b/src/detect.c @@ -140,6 +140,7 @@ #include "app-layer.h" #include "app-layer-protos.h" #include "app-layer-htp.h" +#include "detect-tls.h" #include "detect-tls-version.h" #include "detect-ssh-proto-version.h" #include "detect-ssh-software-version.h" @@ -4516,6 +4517,7 @@ void SigTableSetup(void) { DetectHttpCookieRegister(); DetectHttpMethodRegister(); DetectHttpStatMsgRegister(); + DetectTlsRegister(); DetectTlsVersionRegister(); DetectUrilenRegister(); DetectDetectionFilterRegister(); diff --git a/src/detect.h b/src/detect.h index d2a609e160..eefd897060 100644 --- a/src/detect.h +++ b/src/detect.h @@ -987,6 +987,8 @@ enum { DETECT_MARK, DETECT_AL_TLS_VERSION, + DETECT_AL_TLS_SUBJECT, + DETECT_AL_HTTP_COOKIE, DETECT_AL_HTTP_METHOD, DETECT_AL_URILEN,