[Oisf-devel] [PATCH 2/3] TLS handshake: decode the SERVER_CERTIFICATE message
Pierre Chifflier
pierre.chifflier at ssi.gouv.fr
Tue Oct 25 12:10:59 UTC 2011
Add a decoder for the SERVER_CERTIFICATE during a TLS handshake, extracts the
certificates and keep the subject name.
Add the tls.subject keyword for substring match in rules (TLS layer).
Signed-off-by: Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
---
src/Makefile.am | 2 +
src/app-layer-ssl.c | 38 ++++++-
src/app-layer-ssl.h | 8 ++
src/decode-tls-handshake.c | 90 +++++++++++++++
src/decode-tls-handshake.h | 31 +++++
src/detect-tls.c | 269 ++++++++++++++++++++++++++++++++++++++++++++
src/detect-tls.h | 36 ++++++
src/detect.c | 2 +
src/detect.h | 2 +
9 files changed, 475 insertions(+), 3 deletions(-)
create mode 100644 src/decode-tls-handshake.c
create mode 100644 src/decode-tls-handshake.h
create mode 100644 src/detect-tls.c
create mode 100644 src/detect-tls.h
diff --git a/src/Makefile.am b/src/Makefile.am
index a59443d..de8fdd1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -39,6 +39,7 @@ decode-raw.c decode-raw.h \
decode-icmpv4.c decode-icmpv4.h \
decode-icmpv6.c decode-icmpv6.h \
decode-tcp.c decode-tcp.h \
+decode-tls-handshake.c decode-tls-handshake.h \
decode-udp.c decode-udp.h \
decode-sctp.c decode-sctp.h \
flow.c flow.h \
@@ -135,6 +136,7 @@ detect-http-header.c detect-http-header.h \
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-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 \
diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c
index 03fc821..a1df3ee 100644
--- a/src/app-layer-ssl.c
+++ b/src/app-layer-ssl.c
@@ -19,6 +19,7 @@
* \file
*
* \author Anoop Saldanha <poonaatsoc at gmail.com>
+ * \author Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
*
*/
@@ -39,6 +40,8 @@
#include "app-layer-parser.h"
#include "app-layer-ssl.h"
+#include "decode-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,30 @@ 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 );
+ }
+ 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 +214,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 +458,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 +878,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 999841b..9065695 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/decode-tls-handshake.c b/src/decode-tls-handshake.c
new file mode 100644
index 0000000..93a0485
--- /dev/null
+++ b/src/decode-tls-handshake.c
@@ -0,0 +1,90 @@
+/* Copyright (C) 2007-2011 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 Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ *
+ * \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 "decode-tls-handshake.h"
+
+#include <stdint.h>
+
+#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;
+
+ certificates_length = input[0]<<16 | input[1]<<8 | input[2];
+ /* check if the record 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 = strdup(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/decode-tls-handshake.h b/src/decode-tls-handshake.h
new file mode 100644
index 0000000..8a8f2c6
--- /dev/null
+++ b/src/decode-tls-handshake.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2011 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 Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ *
+ */
+
+#ifndef __DECODE_TLS_HANDSHAKE_H__
+#define __DECODE_TLS_HANDSHAKE_H__
+
+int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uint32_t input_len);
+
+#endif /* __DECODE_TLS_HANDSHAKE_H__ */
+
diff --git a/src/detect-tls.c b/src/detect-tls.c
new file mode 100644
index 0000000..16c7f46
--- /dev/null
+++ b/src/detect-tls.c
@@ -0,0 +1,269 @@
+/* Copyright (C) 2007-2011 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 Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ *
+ * 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 = strdup(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;
+ 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 0000000..9e5b02a
--- /dev/null
+++ b/src/detect-tls.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2007-2011 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 Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ */
+
+#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 2bfbfe7..4163a7a 100644
--- a/src/detect.c
+++ b/src/detect.c
@@ -130,6 +130,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"
@@ -4304,6 +4305,7 @@ void SigTableSetup(void) {
DetectHttpCookieRegister();
DetectHttpMethodRegister();
DetectHttpStatMsgRegister();
+ DetectTlsRegister();
DetectTlsVersionRegister();
DetectUrilenRegister();
DetectDetectionFilterRegister();
diff --git a/src/detect.h b/src/detect.h
index d35a4e1..46ba3c1 100644
--- a/src/detect.h
+++ b/src/detect.h
@@ -999,6 +999,8 @@ enum {
DETECT_MARK,
DETECT_AL_TLS_VERSION,
+ DETECT_AL_TLS_SUBJECT,
+
DETECT_AL_HTTP_COOKIE,
DETECT_AL_HTTP_METHOD,
DETECT_AL_URILEN,
--
1.7.7
More information about the Oisf-devel
mailing list