]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
app-layer-ssl: reimplement function for decoding certificates
authorMats Klepsland <mats.klepsland@gmail.com>
Wed, 21 Mar 2018 22:29:01 +0000 (23:29 +0100)
committerVictor Julien <victor@inliniac.net>
Wed, 28 Mar 2018 06:49:20 +0000 (08:49 +0200)
Do a complete rewrite of the function for decoding the SSL/TLS
certificate from the handshake.

src/app-layer-ssl.c
src/util-crypt.h

index f848f61870eaec0fdc9856d60ae40e2bb85cb4d8..4a8c39225a0c6f77102e99c833a14d1e08c5b9e2 100644 (file)
@@ -43,6 +43,9 @@
 #include "decode-events.h"
 #include "conf.h"
 
+#include "util-crypt.h"
+#include "util-decode-der.h"
+#include "util-decode-der-get.h"
 #include "util-spm.h"
 #include "util-unittest.h"
 #include "util-debug.h"
@@ -139,6 +142,8 @@ SslConfig ssl_config;
 
 #define SSL_RECORD_MINIMUM_LENGTH       6
 
+#define SHA1_STRING_LENGTH             60
+
 #define HAS_SPACE(n) ((uint32_t)((input) + (n) - (initial_input)) > (uint32_t)(input_len)) ?  0 : 1
 
 static void SSLParserReset(SSLState *ssl_state)
@@ -255,6 +260,250 @@ static void SSLSetTxDetectFlags(void *vtx, uint8_t dir, uint64_t flags)
     }
 }
 
+static void TlsDecodeHSCertificateErrSetEvent(SSLState *ssl_state, uint32_t err)
+{
+    switch (err) {
+        case ERR_DER_UNKNOWN_ELEMENT:
+            SSLSetEvent(ssl_state,
+                        TLS_DECODER_EVENT_CERTIFICATE_UNKNOWN_ELEMENT);
+            break;
+        case ERR_DER_ELEMENT_SIZE_TOO_BIG:
+        case ERR_DER_INVALID_SIZE:
+        case ERR_DER_RECURSION_LIMIT:
+            SSLSetEvent(ssl_state,
+                        TLS_DECODER_EVENT_CERTIFICATE_INVALID_LENGTH);
+            break;
+        case ERR_DER_UNSUPPORTED_STRING:
+            SSLSetEvent(ssl_state,
+                        TLS_DECODER_EVENT_CERTIFICATE_INVALID_STRING);
+            break;
+        case ERR_DER_MISSING_ELEMENT:
+            SSLSetEvent(ssl_state,
+                        TLS_DECODER_EVENT_CERTIFICATE_MISSING_ELEMENT);
+            break;
+        case ERR_DER_INVALID_TAG:
+        case ERR_DER_INVALID_OBJECT:
+        case ERR_DER_GENERIC:
+        default:
+            SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_CERTIFICATE);
+            break;
+    }
+}
+
+static inline int TlsDecodeHSCertificateSubject(SSLState *ssl_state,
+                                                Asn1Generic *cert)
+{
+    if (unlikely(ssl_state->server_connp.cert0_subject != NULL))
+        return 0;
+
+    uint32_t err = 0;
+    char buffer[512];
+
+    int rc = Asn1DerGetSubjectDN(cert, buffer, sizeof(buffer), &err);
+    if (rc != 0) {
+        TlsDecodeHSCertificateErrSetEvent(ssl_state, err);
+        return 0;
+    }
+
+    ssl_state->server_connp.cert0_subject = SCStrdup(buffer);
+    if (ssl_state->server_connp.cert0_subject == NULL)
+        return -1;
+
+    return 0;
+}
+
+static inline int TlsDecodeHSCertificateIssuer(SSLState *ssl_state,
+                                               Asn1Generic *cert)
+{
+    if (unlikely(ssl_state->server_connp.cert0_issuerdn != NULL))
+        return 0;
+
+    uint32_t err = 0;
+    char buffer[512];
+
+    int rc = Asn1DerGetIssuerDN(cert, buffer, sizeof(buffer), &err);
+    if (rc != 0) {
+        TlsDecodeHSCertificateErrSetEvent(ssl_state, err);
+        return 0;
+    }
+
+    ssl_state->server_connp.cert0_issuerdn = SCStrdup(buffer);
+    if (ssl_state->server_connp.cert0_issuerdn == NULL)
+        return -1;
+
+    return 0;
+}
+
+static inline int TlsDecodeHSCertificateSerial(SSLState *ssl_state,
+                                               Asn1Generic *cert)
+{
+    if (unlikely(ssl_state->server_connp.cert0_serial != NULL))
+        return 0;
+
+    uint32_t err = 0;
+    char buffer[512];
+
+    int rc = Asn1DerGetSerial(cert, buffer, sizeof(buffer), &err);
+    if (rc != 0) {
+        TlsDecodeHSCertificateErrSetEvent(ssl_state, err);
+        return 0;
+    }
+
+    ssl_state->server_connp.cert0_serial = SCStrdup(buffer);
+    if (ssl_state->server_connp.cert0_serial == NULL)
+        return -1;
+
+    return 0;
+}
+
+static inline int TlsDecodeHSCertificateValidity(SSLState *ssl_state,
+                                                 Asn1Generic *cert)
+{
+    uint32_t err = 0;
+    time_t not_before;
+    time_t not_after;
+
+    int rc = Asn1DerGetValidity(cert, &not_before, &not_after, &err);
+    if (rc != 0) {
+        TlsDecodeHSCertificateErrSetEvent(ssl_state, err);
+        return 0;
+    }
+
+    ssl_state->server_connp.cert0_not_before = not_before;
+    ssl_state->server_connp.cert0_not_after = not_after;
+
+    return 0;
+}
+
+static inline int TlsDecodeHSCertificateFingerprint(SSLState *ssl_state,
+                                                    const uint8_t *input,
+                                                    uint32_t cert_len)
+{
+    if (unlikely(ssl_state->server_connp.cert0_fingerprint != NULL))
+        return 0;
+
+    ssl_state->server_connp.cert0_fingerprint = SCCalloc(1, SHA1_STRING_LENGTH *
+                                                         sizeof(char));
+    if (ssl_state->server_connp.cert0_fingerprint == NULL)
+        return -1;
+
+    uint8_t *hash = ComputeSHA1((uint8_t *)input, cert_len);
+    if (hash == NULL)
+        return 0;
+
+    int i, x;
+    for (i = 0, x = 0; x < SHA1_LENGTH; x++)
+    {
+        i += snprintf(ssl_state->server_connp.cert0_fingerprint + i,
+                      SHA1_STRING_LENGTH - i, i == 0 ? "%02x" : ":%02x",
+                      *(hash + x));
+    }
+
+    SCFree(hash);
+
+    return 0;
+}
+
+static inline int TlsDecodeHSCertificateAddCertToChain(SSLState *ssl_state,
+                                                       const uint8_t *input,
+                                                       uint32_t cert_len)
+{
+    SSLCertsChain *cert = SCCalloc(1, sizeof(SSLCertsChain));
+    if (cert == NULL)
+        return -1;
+
+    cert->cert_data = (uint8_t *)input;
+    cert->cert_len = cert_len;
+    TAILQ_INSERT_TAIL(&ssl_state->server_connp.certs, cert, next);
+
+    return 0;
+}
+
+static int TlsDecodeHSCertificate(SSLState *ssl_state,
+                                  const uint8_t * const initial_input,
+                                  const uint32_t input_len)
+{
+    const uint8_t *input = (uint8_t *)initial_input;
+
+    Asn1Generic *cert;
+
+    if (!(HAS_SPACE(3)))
+        return 1;
+
+    uint32_t cert_chain_len = *input << 16 | *(input + 1) << 8 | *(input + 2);
+    input += 3;
+
+    if (!(HAS_SPACE(cert_chain_len)))
+        return 0;
+
+    uint32_t processed_len = 0;
+    while (processed_len < cert_chain_len)
+    {
+        if (!(HAS_SPACE(3)))
+            goto invalid_cert;
+
+        uint32_t cert_len = *input << 16 | *(input + 1) << 8 | *(input + 2);
+        input += 3;
+
+        if (!(HAS_SPACE(cert_len)))
+            goto invalid_cert;
+
+        uint32_t err = 0;
+        int rc = 0;
+
+        /* only store fields from the first certificate in the chain */
+        if (processed_len == 0) {
+            cert = DecodeDer(input, cert_len, &err);
+            if (cert == NULL) {
+                TlsDecodeHSCertificateErrSetEvent(ssl_state, err);
+                goto next;
+            }
+
+            rc = TlsDecodeHSCertificateSubject(ssl_state, cert);
+            if (rc != 0)
+                goto error;
+
+            rc = TlsDecodeHSCertificateIssuer(ssl_state, cert);
+            if (rc != 0)
+                goto error;
+
+            rc = TlsDecodeHSCertificateSerial(ssl_state, cert);
+            if (rc != 0)
+                goto error;
+
+            rc = TlsDecodeHSCertificateValidity(ssl_state, cert);
+            if (rc != 0)
+                goto error;
+
+            rc = TlsDecodeHSCertificateFingerprint(ssl_state, input, cert_len);
+            if (rc != 0)
+                goto error;
+
+            DerFree(cert);
+        }
+
+        rc = TlsDecodeHSCertificateAddCertToChain(ssl_state, input, cert_len);
+        if (rc != 0)
+            goto error;
+
+next:
+        input += cert_len;
+        processed_len += cert_len + 3;
+    }
+
+    return (input - initial_input);
+
+error:
+    if (cert != NULL)
+        DerFree(cert);
+    return -1;
+
+invalid_cert:
+    SCLogDebug("TLS invalid certificate");
+    SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_CERTIFICATE);
+    return -1;
+}
+
 /**
  * \inline
  * \brief Check if value is GREASE.
@@ -937,9 +1186,8 @@ static int SSLv3ParseHandshakeType(SSLState *ssl_state, uint8_t *input,
                     ssl_state->curr_connp->trec_pos, initial_input, write_len);
             ssl_state->curr_connp->trec_pos += write_len;
 
-            rc = DecodeTLSHandshakeServerCertificate(ssl_state,
-                    ssl_state->curr_connp->trec,
-                    ssl_state->curr_connp->trec_pos);
+            rc = TlsDecodeHSCertificate(ssl_state, ssl_state->curr_connp->trec,
+                                        ssl_state->curr_connp->trec_pos);
 
             if (rc > 0) {
                 /* do not return normally if the packet was fragmented:
index 7a3540b29fe44279ac8626ea2ff061544535ee54..f0404fb4f41156450456c3f29a237fa0d85f9776 100644 (file)
@@ -42,6 +42,8 @@ typedef enum {
 
 #ifndef HAVE_NSS
 
+#define SHA1_LENGTH 20
+
 #define LOAD32H(x, y)                            \
      { x = ((unsigned long)((y)[0] & 255)<<24) | \
            ((unsigned long)((y)[1] & 255)<<16) | \