[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