summaryrefslogtreecommitdiffstats
path: root/src/app-layer-ssl.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:39:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:39:49 +0000
commita0aa2307322cd47bbf416810ac0292925e03be87 (patch)
tree37076262a026c4b48c8a0e84f44ff9187556ca35 /src/app-layer-ssl.c
parentInitial commit. (diff)
downloadsuricata-a0aa2307322cd47bbf416810ac0292925e03be87.tar.xz
suricata-a0aa2307322cd47bbf416810ac0292925e03be87.zip
Adding upstream version 1:7.0.3.upstream/1%7.0.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/app-layer-ssl.c')
-rw-r--r--src/app-layer-ssl.c3108
1 files changed, 3108 insertions, 0 deletions
diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c
new file mode 100644
index 0000000..302225f
--- /dev/null
+++ b/src/app-layer-ssl.c
@@ -0,0 +1,3108 @@
+/* Copyright (C) 2007-2022 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 Anoop Saldanha <anoopsaldanha@gmail.com>
+ * \author Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>
+ * \author Mats Klepsland <mats.klepsland@gmail.com>
+ *
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "threads.h"
+
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+#include "stream-tcp.h"
+#include "stream.h"
+
+#include "app-layer.h"
+#include "app-layer-detect-proto.h"
+#include "app-layer-protos.h"
+#include "app-layer-parser.h"
+#include "app-layer-frames.h"
+#include "app-layer-ssl.h"
+
+#include "decode-events.h"
+#include "conf.h"
+
+#include "util-spm.h"
+#include "util-unittest.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "util-pool.h"
+#include "util-byte.h"
+#include "util-ja3.h"
+#include "util-enum.h"
+#include "flow-util.h"
+#include "flow-private.h"
+#include "util-validate.h"
+
+SCEnumCharMap tls_frame_table[] = {
+ {
+ "pdu",
+ TLS_FRAME_PDU,
+ },
+ {
+ "hdr",
+ TLS_FRAME_HDR,
+ },
+ {
+ "data",
+ TLS_FRAME_DATA,
+ },
+ {
+ "alert",
+ TLS_FRAME_ALERT_DATA,
+ },
+ {
+ "heartbeat",
+ TLS_FRAME_HB_DATA,
+ },
+ {
+ "ssl2.hdr",
+ TLS_FRAME_SSLV2_HDR,
+ },
+ {
+ "ssl2.pdu",
+ TLS_FRAME_SSLV2_PDU,
+ },
+ { NULL, -1 },
+};
+
+SCEnumCharMap tls_decoder_event_table[] = {
+ /* TLS protocol messages */
+ { "INVALID_SSLV2_HEADER", TLS_DECODER_EVENT_INVALID_SSLV2_HEADER },
+ { "INVALID_TLS_HEADER", TLS_DECODER_EVENT_INVALID_TLS_HEADER },
+ { "INVALID_RECORD_VERSION", TLS_DECODER_EVENT_INVALID_RECORD_VERSION },
+ { "INVALID_RECORD_TYPE", TLS_DECODER_EVENT_INVALID_RECORD_TYPE },
+ { "INVALID_RECORD_LENGTH", TLS_DECODER_EVENT_INVALID_RECORD_LENGTH },
+ { "INVALID_HANDSHAKE_MESSAGE", TLS_DECODER_EVENT_INVALID_HANDSHAKE_MESSAGE },
+ { "HEARTBEAT_MESSAGE", TLS_DECODER_EVENT_HEARTBEAT },
+ { "INVALID_HEARTBEAT_MESSAGE", TLS_DECODER_EVENT_INVALID_HEARTBEAT },
+ { "OVERFLOW_HEARTBEAT_MESSAGE", TLS_DECODER_EVENT_OVERFLOW_HEARTBEAT },
+ { "DATALEAK_HEARTBEAT_MISMATCH", TLS_DECODER_EVENT_DATALEAK_HEARTBEAT_MISMATCH },
+ { "HANDSHAKE_INVALID_LENGTH", TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH },
+ { "MULTIPLE_SNI_EXTENSIONS", TLS_DECODER_EVENT_MULTIPLE_SNI_EXTENSIONS },
+ { "INVALID_SNI_TYPE", TLS_DECODER_EVENT_INVALID_SNI_TYPE },
+ { "INVALID_SNI_LENGTH", TLS_DECODER_EVENT_INVALID_SNI_LENGTH },
+ { "TOO_MANY_RECORDS_IN_PACKET", TLS_DECODER_EVENT_TOO_MANY_RECORDS_IN_PACKET },
+ /* certificate decoding messages */
+ { "INVALID_CERTIFICATE", TLS_DECODER_EVENT_INVALID_CERTIFICATE },
+ { "CERTIFICATE_INVALID_LENGTH", TLS_DECODER_EVENT_CERTIFICATE_INVALID_LENGTH },
+ { "CERTIFICATE_INVALID_VERSION", TLS_DECODER_EVENT_CERTIFICATE_INVALID_VERSION },
+ { "CERTIFICATE_INVALID_SERIAL", TLS_DECODER_EVENT_CERTIFICATE_INVALID_SERIAL },
+ { "CERTIFICATE_INVALID_ALGORITHMIDENTIFIER",
+ TLS_DECODER_EVENT_CERTIFICATE_INVALID_ALGORITHMIDENTIFIER },
+ { "CERTIFICATE_INVALID_X509NAME", TLS_DECODER_EVENT_CERTIFICATE_INVALID_X509NAME },
+ { "CERTIFICATE_INVALID_DATE", TLS_DECODER_EVENT_CERTIFICATE_INVALID_DATE },
+ { "CERTIFICATE_INVALID_EXTENSIONS", TLS_DECODER_EVENT_CERTIFICATE_INVALID_EXTENSIONS },
+ { "CERTIFICATE_INVALID_DER", TLS_DECODER_EVENT_CERTIFICATE_INVALID_DER },
+ { "CERTIFICATE_INVALID_SUBJECT", TLS_DECODER_EVENT_CERTIFICATE_INVALID_SUBJECT },
+ { "CERTIFICATE_INVALID_ISSUER", TLS_DECODER_EVENT_CERTIFICATE_INVALID_ISSUER },
+ { "CERTIFICATE_INVALID_VALIDITY", TLS_DECODER_EVENT_CERTIFICATE_INVALID_VALIDITY },
+ { "ERROR_MESSAGE_ENCOUNTERED", TLS_DECODER_EVENT_ERROR_MSG_ENCOUNTERED },
+ /* used as a generic error event */
+ { "INVALID_SSL_RECORD", TLS_DECODER_EVENT_INVALID_SSL_RECORD },
+ { NULL, -1 },
+};
+
+enum {
+ /* X.509 error codes, returned by decoder
+ * THESE CONSTANTS MUST MATCH rust/src/x509/mod.rs ! */
+ ERR_INVALID_CERTIFICATE=1,
+ ERR_INVALID_LENGTH,
+ ERR_INVALID_VERSION,
+ ERR_INVALID_SERIAL,
+ ERR_INVALID_ALGORITHMIDENTIFIER,
+ ERR_INVALID_X509NAME,
+ ERR_INVALID_DATE,
+ ERR_INVALID_EXTENSIONS,
+ ERR_INVALID_DER,
+
+ /* error getting data */
+ ERR_EXTRACT_SUBJECT,
+ ERR_EXTRACT_ISSUER,
+ ERR_EXTRACT_VALIDITY,
+};
+
+/* JA3 fingerprints are disabled by default */
+#define SSL_CONFIG_DEFAULT_JA3 0
+
+enum SslConfigEncryptHandling {
+ SSL_CNF_ENC_HANDLE_DEFAULT = 0, /**< disable raw content, continue tracking */
+ SSL_CNF_ENC_HANDLE_BYPASS = 1, /**< skip processing of flow, bypass if possible */
+ SSL_CNF_ENC_HANDLE_FULL = 2, /**< handle fully like any other proto */
+};
+
+typedef struct SslConfig_ {
+ enum SslConfigEncryptHandling encrypt_mode;
+ /** dynamic setting for ja3: can be enabled on demand if not explicitly
+ * disabled. */
+ SC_ATOMIC_DECLARE(int, enable_ja3);
+ bool disable_ja3; /**< ja3 explicitly disabled. Don't enable on demand. */
+} SslConfig;
+
+SslConfig ssl_config;
+
+/* SSLv3 record types */
+#define SSLV3_CHANGE_CIPHER_SPEC 20
+#define SSLV3_ALERT_PROTOCOL 21
+#define SSLV3_HANDSHAKE_PROTOCOL 22
+#define SSLV3_APPLICATION_PROTOCOL 23
+#define SSLV3_HEARTBEAT_PROTOCOL 24
+
+/* SSLv3 handshake protocol types */
+#define SSLV3_HS_HELLO_REQUEST 0
+#define SSLV3_HS_CLIENT_HELLO 1
+#define SSLV3_HS_SERVER_HELLO 2
+#define SSLV3_HS_NEW_SESSION_TICKET 4
+#define SSLV3_HS_CERTIFICATE 11
+#define SSLV3_HS_SERVER_KEY_EXCHANGE 12
+#define SSLV3_HS_CERTIFICATE_REQUEST 13
+#define SSLV3_HS_SERVER_HELLO_DONE 14
+#define SSLV3_HS_CERTIFICATE_VERIFY 15
+#define SSLV3_HS_CLIENT_KEY_EXCHANGE 16
+#define SSLV3_HS_FINISHED 20
+#define SSLV3_HS_CERTIFICATE_URL 21
+#define SSLV3_HS_CERTIFICATE_STATUS 22
+
+/* SSLv2 protocol message types */
+#define SSLV2_MT_ERROR 0
+#define SSLV2_MT_CLIENT_HELLO 1
+#define SSLV2_MT_CLIENT_MASTER_KEY 2
+#define SSLV2_MT_CLIENT_FINISHED 3
+#define SSLV2_MT_SERVER_HELLO 4
+#define SSLV2_MT_SERVER_VERIFY 5
+#define SSLV2_MT_SERVER_FINISHED 6
+#define SSLV2_MT_REQUEST_CERTIFICATE 7
+#define SSLV2_MT_CLIENT_CERTIFICATE 8
+
+#define SSLV3_RECORD_HDR_LEN 5
+#define SSLV3_MESSAGE_HDR_LEN 4
+/** max length according to RFC 5246 6.2.2 is 2^14 + 1024 */
+#define SSLV3_RECORD_MAX_LEN ((1 << 14) + 1024)
+
+#define SSLV3_CLIENT_HELLO_VERSION_LEN 2
+#define SSLV3_CLIENT_HELLO_RANDOM_LEN 32
+
+/* TLS heartbeat protocol types */
+#define TLS_HB_REQUEST 1
+#define TLS_HB_RESPONSE 2
+
+#define SSL_RECORD_MINIMUM_LENGTH 6
+
+#define SHA1_STRING_LENGTH 60
+
+#define HAS_SPACE(n) ((uint64_t)(input - initial_input) + (uint64_t)(n) <= (uint64_t)(input_len))
+
+struct SSLDecoderResult {
+ int retval; // nr bytes consumed from input, or < 0 on error
+ uint32_t needed; // more bytes needed
+};
+#define SSL_DECODER_ERROR(e) \
+ (struct SSLDecoderResult) \
+ { \
+ (e), 0 \
+ }
+#define SSL_DECODER_OK(c) \
+ (struct SSLDecoderResult) \
+ { \
+ (c), 0 \
+ }
+#define SSL_DECODER_INCOMPLETE(c, n) \
+ (struct SSLDecoderResult) \
+ { \
+ (c), (n) \
+ }
+
+static inline int SafeMemcpy(void *dst, size_t dst_offset, size_t dst_size,
+ const void *src, size_t src_offset, size_t src_size, size_t src_tocopy) WARN_UNUSED;
+
+static inline int SafeMemcpy(void *dst, size_t dst_offset, size_t dst_size,
+ const void *src, size_t src_offset, size_t src_size, size_t src_tocopy)
+{
+ DEBUG_VALIDATE_BUG_ON(dst_offset >= dst_size);
+ DEBUG_VALIDATE_BUG_ON(src_offset >= src_size);
+ DEBUG_VALIDATE_BUG_ON(src_tocopy > (src_size - src_offset));
+ DEBUG_VALIDATE_BUG_ON(src_tocopy > (dst_size - dst_offset));
+
+ if (dst_offset < dst_size && src_offset < src_size &&
+ src_tocopy <= (src_size - src_offset) &&
+ src_tocopy <= (dst_size - dst_offset)) {
+ memcpy(dst + dst_offset, src + src_offset, src_tocopy);
+ return 0;
+ }
+ return -1;
+}
+
+#ifdef DEBUG_VALIDATION
+#define ValidateRecordState(connp) \
+ do { \
+ DEBUG_VALIDATE_BUG_ON(((connp)->record_length + SSLV3_RECORD_HDR_LEN) < \
+ (connp)->bytes_processed); \
+ } while(0);
+#else
+#define ValidateRecordState(...)
+#endif
+
+#define SSLParserHSReset(connp) \
+ do { \
+ (connp)->handshake_type = 0; \
+ (connp)->message_length = 0; \
+ } while (0)
+
+#define SSLParserReset(state) \
+ do { \
+ SCLogDebug("resetting state"); \
+ (state)->curr_connp->bytes_processed = 0; \
+ SSLParserHSReset((state)->curr_connp); \
+ } while(0)
+
+#define SSLSetEvent(ssl_state, event) \
+ do { \
+ SCLogDebug("setting event %u", (event)); \
+ if ((ssl_state) == NULL) { \
+ SCLogDebug("could not set decoder event %u", event); \
+ } else { \
+ AppLayerDecoderEventsSetEventRaw(&(ssl_state)->tx_data.events, (event)); \
+ (ssl_state)->events++; \
+ } \
+ } while (0)
+
+static void *SSLGetTx(void *state, uint64_t tx_id)
+{
+ SSLState *ssl_state = (SSLState *)state;
+ return ssl_state;
+}
+
+static uint64_t SSLGetTxCnt(void *state)
+{
+ /* single tx */
+ return 1;
+}
+
+static int SSLGetAlstateProgress(void *tx, uint8_t direction)
+{
+ SSLState *ssl_state = (SSLState *)tx;
+
+ /* we don't care about direction, only that app-layer parser is done
+ and have sent an EOF */
+ if (ssl_state->flags & SSL_AL_FLAG_STATE_FINISHED) {
+ return TLS_STATE_FINISHED;
+ }
+
+ /* we want the logger to log when the handshake is done, even if the
+ state is not finished */
+ if (ssl_state->flags & SSL_AL_FLAG_HANDSHAKE_DONE) {
+ return TLS_HANDSHAKE_DONE;
+ }
+
+ if (direction == STREAM_TOSERVER &&
+ (ssl_state->server_connp.cert0_subject != NULL ||
+ ssl_state->server_connp.cert0_issuerdn != NULL))
+ {
+ return TLS_STATE_CERT_READY;
+ }
+
+ return TLS_STATE_IN_PROGRESS;
+}
+
+static AppLayerTxData *SSLGetTxData(void *vtx)
+{
+ SSLState *ssl_state = (SSLState *)vtx;
+ return &ssl_state->tx_data;
+}
+
+static AppLayerStateData *SSLGetStateData(void *vstate)
+{
+ SSLState *ssl_state = (SSLState *)vstate;
+ return &ssl_state->state_data;
+}
+
+void SSLVersionToString(uint16_t version, char *buffer)
+{
+ buffer[0] = '\0';
+
+ switch (version) {
+ case TLS_VERSION_UNKNOWN:
+ strlcat(buffer, "UNDETERMINED", 13);
+ break;
+ case SSL_VERSION_2:
+ strlcat(buffer, "SSLv2", 6);
+ break;
+ case SSL_VERSION_3:
+ strlcat(buffer, "SSLv3", 6);
+ break;
+ case TLS_VERSION_10:
+ strlcat(buffer, "TLSv1", 6);
+ break;
+ case TLS_VERSION_11:
+ strlcat(buffer, "TLS 1.1", 8);
+ break;
+ case TLS_VERSION_12:
+ strlcat(buffer, "TLS 1.2", 8);
+ break;
+ case TLS_VERSION_13:
+ strlcat(buffer, "TLS 1.3", 8);
+ break;
+ case TLS_VERSION_13_DRAFT28:
+ strlcat(buffer, "TLS 1.3 draft-28", 17);
+ break;
+ case TLS_VERSION_13_DRAFT27:
+ strlcat(buffer, "TLS 1.3 draft-27", 17);
+ break;
+ case TLS_VERSION_13_DRAFT26:
+ strlcat(buffer, "TLS 1.3 draft-26", 17);
+ break;
+ case TLS_VERSION_13_DRAFT25:
+ strlcat(buffer, "TLS 1.3 draft-25", 17);
+ break;
+ case TLS_VERSION_13_DRAFT24:
+ strlcat(buffer, "TLS 1.3 draft-24", 17);
+ break;
+ case TLS_VERSION_13_DRAFT23:
+ strlcat(buffer, "TLS 1.3 draft-23", 17);
+ break;
+ case TLS_VERSION_13_DRAFT22:
+ strlcat(buffer, "TLS 1.3 draft-22", 17);
+ break;
+ case TLS_VERSION_13_DRAFT21:
+ strlcat(buffer, "TLS 1.3 draft-21", 17);
+ break;
+ case TLS_VERSION_13_DRAFT20:
+ strlcat(buffer, "TLS 1.3 draft-20", 17);
+ break;
+ case TLS_VERSION_13_DRAFT19:
+ strlcat(buffer, "TLS 1.3 draft-19", 17);
+ break;
+ case TLS_VERSION_13_DRAFT18:
+ strlcat(buffer, "TLS 1.3 draft-18", 17);
+ break;
+ case TLS_VERSION_13_DRAFT17:
+ strlcat(buffer, "TLS 1.3 draft-17", 17);
+ break;
+ case TLS_VERSION_13_DRAFT16:
+ strlcat(buffer, "TLS 1.3 draft-16", 17);
+ break;
+ case TLS_VERSION_13_PRE_DRAFT16:
+ strlcat(buffer, "TLS 1.3 draft-<16", 18);
+ break;
+ case TLS_VERSION_13_DRAFT20_FB:
+ strlcat(buffer, "TLS 1.3 draft-20-fb", 20);
+ break;
+ case TLS_VERSION_13_DRAFT21_FB:
+ strlcat(buffer, "TLS 1.3 draft-21-fb", 20);
+ break;
+ case TLS_VERSION_13_DRAFT22_FB:
+ strlcat(buffer, "TLS 1.3 draft-22-fb", 20);
+ break;
+ case TLS_VERSION_13_DRAFT23_FB:
+ strlcat(buffer, "TLS 1.3 draft-23-fb", 20);
+ break;
+ case TLS_VERSION_13_DRAFT26_FB:
+ strlcat(buffer, "TLS 1.3 draft-26-fb", 20);
+ break;
+ default:
+ snprintf(buffer, 7, "0x%04x", version);
+ break;
+ }
+}
+
+static void TlsDecodeHSCertificateErrSetEvent(SSLState *ssl_state, uint32_t err)
+{
+ switch(err) {
+ case ERR_EXTRACT_VALIDITY:
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_VALIDITY);
+ break;
+ case ERR_EXTRACT_ISSUER:
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_ISSUER);
+ break;
+ case ERR_EXTRACT_SUBJECT:
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_SUBJECT);
+ break;
+ case ERR_INVALID_DER:
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_DER);
+ break;
+ case ERR_INVALID_EXTENSIONS:
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_EXTENSIONS);
+ break;
+ case ERR_INVALID_DATE:
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_DATE);
+ break;
+ case ERR_INVALID_X509NAME:
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_X509NAME);
+ break;
+ case ERR_INVALID_ALGORITHMIDENTIFIER:
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_ALGORITHMIDENTIFIER);
+ break;
+ case ERR_INVALID_SERIAL:
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_SERIAL);
+ break;
+ case ERR_INVALID_VERSION:
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_VERSION);
+ break;
+ case ERR_INVALID_LENGTH:
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_CERTIFICATE_INVALID_LENGTH);
+ break;
+ case ERR_INVALID_CERTIFICATE:
+ default:
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_CERTIFICATE);
+ break;
+ }
+}
+
+static inline int TlsDecodeHSCertificateFingerprint(
+ SSLStateConnp *connp, const uint8_t *input, uint32_t cert_len)
+{
+ if (unlikely(connp->cert0_fingerprint != NULL))
+ return 0;
+
+ connp->cert0_fingerprint = SCCalloc(1, SHA1_STRING_LENGTH);
+ if (connp->cert0_fingerprint == NULL)
+ return -1;
+
+ uint8_t hash[SC_SHA1_LEN];
+ if (SCSha1HashBuffer(input, cert_len, hash, sizeof(hash)) == 1) {
+ rs_to_hex_sep(
+ (uint8_t *)connp->cert0_fingerprint, SHA1_STRING_LENGTH, ':', hash, SC_SHA1_LEN);
+ }
+ return 0;
+}
+
+static inline int TlsDecodeHSCertificateAddCertToChain(
+ SSLStateConnp *connp, 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(&connp->certs, cert, next);
+
+ return 0;
+}
+
+static int TlsDecodeHSCertificate(SSLState *ssl_state, SSLStateConnp *connp,
+ const uint8_t *const initial_input, const uint32_t input_len, const int certn)
+{
+ const uint8_t *input = (uint8_t *)initial_input;
+ uint32_t err_code = 0;
+ X509 *x509 = NULL;
+ int rc = 0;
+
+ 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;
+
+ /* only store fields from the first certificate in the chain */
+ if (certn == 0 && connp->cert0_subject == NULL && connp->cert0_issuerdn == NULL &&
+ connp->cert0_serial == NULL) {
+ x509 = rs_x509_decode(input, cert_len, &err_code);
+ if (x509 == NULL) {
+ TlsDecodeHSCertificateErrSetEvent(ssl_state, err_code);
+ goto next;
+ }
+
+ char *str = rs_x509_get_subject(x509);
+ if (str == NULL) {
+ err_code = ERR_EXTRACT_SUBJECT;
+ goto error;
+ }
+ connp->cert0_subject = str;
+
+ str = rs_x509_get_issuer(x509);
+ if (str == NULL) {
+ err_code = ERR_EXTRACT_ISSUER;
+ goto error;
+ }
+ connp->cert0_issuerdn = str;
+
+ str = rs_x509_get_serial(x509);
+ if (str == NULL) {
+ err_code = ERR_INVALID_SERIAL;
+ goto error;
+ }
+ connp->cert0_serial = str;
+
+ rc = rs_x509_get_validity(x509, &connp->cert0_not_before, &connp->cert0_not_after);
+ if (rc != 0) {
+ err_code = ERR_EXTRACT_VALIDITY;
+ goto error;
+ }
+
+ rs_x509_free(x509);
+ x509 = NULL;
+
+ rc = TlsDecodeHSCertificateFingerprint(connp, input, cert_len);
+ if (rc != 0) {
+ SCLogDebug("TlsDecodeHSCertificateFingerprint failed with %d", rc);
+ goto error;
+ }
+ }
+
+ rc = TlsDecodeHSCertificateAddCertToChain(connp, input, cert_len);
+ if (rc != 0) {
+ SCLogDebug("TlsDecodeHSCertificateAddCertToChain failed with %d", rc);
+ goto error;
+ }
+
+next:
+ input += cert_len;
+ return (input - initial_input);
+
+error:
+ if (err_code != 0)
+ TlsDecodeHSCertificateErrSetEvent(ssl_state, err_code);
+ if (x509 != NULL)
+ rs_x509_free(x509);
+ return -1;
+
+invalid_cert:
+ SCLogDebug("TLS invalid certificate");
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_CERTIFICATE);
+ return -1;
+}
+
+/** \internal
+ * \brief parse cert data in a certificate handshake message
+ * will be called with all data.
+ * \retval consumed bytes consumed or -1 on error
+ */
+static int TlsDecodeHSCertificates(SSLState *ssl_state, SSLStateConnp *connp,
+ const uint8_t *const initial_input, const uint32_t input_len)
+{
+ const uint8_t *input = (uint8_t *)initial_input;
+
+ if (!(HAS_SPACE(3)))
+ return -1;
+
+ const uint32_t cert_chain_len = *input << 16 | *(input + 1) << 8 | *(input + 2);
+ input += 3;
+
+ if (!(HAS_SPACE(cert_chain_len)))
+ return -1;
+
+ if (connp->certs_buffer != NULL) {
+ // TODO should we set an event here?
+ return -1;
+ }
+
+ connp->certs_buffer = SCCalloc(1, cert_chain_len);
+ if (connp->certs_buffer == NULL) {
+ return -1;
+ }
+ connp->certs_buffer_size = cert_chain_len;
+ memcpy(connp->certs_buffer, input, cert_chain_len);
+
+ int cert_cnt = 0;
+ uint32_t processed_len = 0;
+ /* coverity[tainted_data] */
+ while (processed_len < cert_chain_len) {
+ int rc = TlsDecodeHSCertificate(ssl_state, connp, connp->certs_buffer + processed_len,
+ connp->certs_buffer_size - processed_len, cert_cnt);
+ if (rc <= 0) { // 0 should be impossible, but lets be defensive
+ return -1;
+ }
+ DEBUG_VALIDATE_BUG_ON(processed_len + (uint32_t)rc > cert_chain_len);
+ if (processed_len + (uint32_t)rc > cert_chain_len) {
+ return -1;
+ }
+
+ processed_len += (uint32_t)rc;
+ }
+
+ return processed_len + 3;
+}
+
+/**
+ * \inline
+ * \brief Check if value is GREASE.
+ *
+ * http://tools.ietf.org/html/draft-davidben-tls-grease-00
+ *
+ * \param value Value to check.
+ *
+ * \retval 1 if is GREASE.
+ * \retval 0 if not is GREASE.
+ */
+static inline int TLSDecodeValueIsGREASE(const uint16_t value)
+{
+ switch (value)
+ {
+ case 0x0a0a:
+ case 0x1a1a:
+ case 0x2a2a:
+ case 0x3a3a:
+ case 0x4a4a:
+ case 0x5a5a:
+ case 0x6a6a:
+ case 0x7a7a:
+ case 0x8a8a:
+ case 0x9a9a:
+ case 0xaaaa:
+ case 0xbaba:
+ case 0xcaca:
+ case 0xdada:
+ case 0xeaea:
+ case 0xfafa:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int TLSDecodeHSHelloVersion(SSLState *ssl_state,
+ const uint8_t * const initial_input,
+ const uint32_t input_len)
+{
+ uint8_t *input = (uint8_t *)initial_input;
+
+ if (!(HAS_SPACE(SSLV3_CLIENT_HELLO_VERSION_LEN))) {
+ SCLogDebug("TLS handshake invalid length");
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
+ return -1;
+ }
+
+ uint16_t version = (uint16_t)(*input << 8) | *(input + 1);
+ ssl_state->curr_connp->version = version;
+
+ /* TLSv1.3 draft1 to draft21 use the version field as earlier TLS
+ versions, instead of using the supported versions extension. */
+ if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) &&
+ ((ssl_state->curr_connp->version == TLS_VERSION_13) ||
+ (((ssl_state->curr_connp->version >> 8) & 0xff) == 0x7f))) {
+ ssl_state->flags |= SSL_AL_FLAG_LOG_WITHOUT_CERT;
+ }
+
+ /* Catch some early TLSv1.3 draft implementations that does not conform
+ to the draft version. */
+ if ((ssl_state->curr_connp->version >= 0x7f01) &&
+ (ssl_state->curr_connp->version < 0x7f10)) {
+ ssl_state->curr_connp->version = TLS_VERSION_13_PRE_DRAFT16;
+ }
+
+ /* TLSv1.3 drafts from draft1 to draft15 use 0x0304 (TLSv1.3) as the
+ version number, which makes it hard to accurately pinpoint the
+ exact draft version. */
+ else if (ssl_state->curr_connp->version == TLS_VERSION_13) {
+ ssl_state->curr_connp->version = TLS_VERSION_13_PRE_DRAFT16;
+ }
+
+ if (SC_ATOMIC_GET(ssl_config.enable_ja3) && ssl_state->curr_connp->ja3_str == NULL) {
+ ssl_state->curr_connp->ja3_str = Ja3BufferInit();
+ if (ssl_state->curr_connp->ja3_str == NULL)
+ return -1;
+
+ int rc = Ja3BufferAddValue(&ssl_state->curr_connp->ja3_str, version);
+ if (rc != 0)
+ return -1;
+ }
+
+ input += SSLV3_CLIENT_HELLO_VERSION_LEN;
+
+ return (input - initial_input);
+}
+
+static inline int TLSDecodeHSHelloRandom(SSLState *ssl_state,
+ const uint8_t * const initial_input,
+ const uint32_t input_len)
+{
+ uint8_t *input = (uint8_t *)initial_input;
+
+ if (!(HAS_SPACE(SSLV3_CLIENT_HELLO_RANDOM_LEN))) {
+ SCLogDebug("TLS handshake invalid length");
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
+ return -1;
+ }
+
+ if (ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) {
+ memcpy(ssl_state->server_connp.random, input, TLS_RANDOM_LEN);
+ ssl_state->flags |= TLS_TS_RANDOM_SET;
+ } else if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
+ memcpy(ssl_state->client_connp.random, input, TLS_RANDOM_LEN);
+ ssl_state->flags |= TLS_TC_RANDOM_SET;
+ }
+
+ /* Skip random */
+ input += SSLV3_CLIENT_HELLO_RANDOM_LEN;
+
+ return (input - initial_input);
+}
+
+static inline int TLSDecodeHSHelloSessionID(SSLState *ssl_state,
+ const uint8_t * const initial_input,
+ const uint32_t input_len)
+{
+ uint8_t *input = (uint8_t *)initial_input;
+
+ if (!(HAS_SPACE(1)))
+ goto invalid_length;
+
+ uint8_t session_id_length = *input;
+ input += 1;
+
+ if (!(HAS_SPACE(session_id_length)))
+ goto invalid_length;
+
+ if (session_id_length != 0 && ssl_state->curr_connp->session_id == NULL) {
+ ssl_state->curr_connp->session_id = SCMalloc(session_id_length);
+
+ if (unlikely(ssl_state->curr_connp->session_id == NULL)) {
+ return -1;
+ }
+
+ if (SafeMemcpy(ssl_state->curr_connp->session_id, 0, session_id_length,
+ input, 0, input_len, session_id_length) != 0) {
+ return -1;
+ }
+ ssl_state->curr_connp->session_id_length = session_id_length;
+
+ if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) &&
+ ssl_state->client_connp.session_id != NULL &&
+ ssl_state->server_connp.session_id != NULL) {
+ if ((ssl_state->client_connp.session_id_length ==
+ ssl_state->server_connp.session_id_length) &&
+ (memcmp(ssl_state->server_connp.session_id,
+ ssl_state->client_connp.session_id, session_id_length) == 0)) {
+ ssl_state->flags |= SSL_AL_FLAG_SESSION_RESUMED;
+ }
+ }
+ }
+
+ input += session_id_length;
+
+ return (input - initial_input);
+
+invalid_length:
+ SCLogDebug("TLS handshake invalid length");
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
+ return -1;
+}
+
+static inline int TLSDecodeHSHelloCipherSuites(SSLState *ssl_state,
+ const uint8_t * const initial_input,
+ const uint32_t input_len)
+{
+ const uint8_t *input = initial_input;
+
+ if (!(HAS_SPACE(2)))
+ goto invalid_length;
+
+ uint16_t cipher_suites_length;
+
+ if (ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) {
+ cipher_suites_length = 2;
+ } else if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
+ cipher_suites_length = (uint16_t)(*input << 8) | *(input + 1);
+ input += 2;
+ } else {
+ return -1;
+ }
+
+ if (!(HAS_SPACE(cipher_suites_length)))
+ goto invalid_length;
+
+ /* Cipher suites length should always be divisible by 2 */
+ if ((cipher_suites_length % 2) != 0) {
+ goto invalid_length;
+ }
+
+ if (SC_ATOMIC_GET(ssl_config.enable_ja3)) {
+ JA3Buffer *ja3_cipher_suites = Ja3BufferInit();
+ if (ja3_cipher_suites == NULL)
+ return -1;
+
+ uint16_t processed_len = 0;
+ /* coverity[tainted_data] */
+ while (processed_len < cipher_suites_length)
+ {
+ if (!(HAS_SPACE(2))) {
+ Ja3BufferFree(&ja3_cipher_suites);
+ goto invalid_length;
+ }
+
+ uint16_t cipher_suite = (uint16_t)(*input << 8) | *(input + 1);
+ input += 2;
+
+ if (TLSDecodeValueIsGREASE(cipher_suite) != 1) {
+ int rc = Ja3BufferAddValue(&ja3_cipher_suites, cipher_suite);
+ if (rc != 0) {
+ return -1;
+ }
+ }
+
+ processed_len += 2;
+ }
+
+ int rc = Ja3BufferAppendBuffer(&ssl_state->curr_connp->ja3_str,
+ &ja3_cipher_suites);
+ if (rc == -1) {
+ return -1;
+ }
+
+ } else {
+ /* Skip cipher suites */
+ input += cipher_suites_length;
+ }
+
+ return (input - initial_input);
+
+invalid_length:
+ SCLogDebug("TLS handshake invalid length");
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
+ return -1;
+}
+
+static inline int TLSDecodeHSHelloCompressionMethods(SSLState *ssl_state,
+ const uint8_t * const initial_input,
+ const uint32_t input_len)
+{
+ const uint8_t *input = initial_input;
+
+ if (!(HAS_SPACE(1)))
+ goto invalid_length;
+
+ /* Skip compression methods */
+ if (ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) {
+ input += 1;
+ } else {
+ uint8_t compression_methods_length = *input;
+ input += 1;
+
+ if (!(HAS_SPACE(compression_methods_length)))
+ goto invalid_length;
+
+ input += compression_methods_length;
+ }
+
+ return (input - initial_input);
+
+invalid_length:
+ SCLogDebug("TLS handshake invalid_length");
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
+ return -1;
+}
+
+static inline int TLSDecodeHSHelloExtensionSni(SSLState *ssl_state,
+ const uint8_t * const initial_input,
+ const uint32_t input_len)
+{
+ uint8_t *input = (uint8_t *)initial_input;
+
+ /* Empty extension */
+ if (input_len == 0)
+ return 0;
+
+ if (!(HAS_SPACE(2)))
+ goto invalid_length;
+
+ /* Skip sni_list_length */
+ input += 2;
+
+ if (!(HAS_SPACE(1)))
+ goto invalid_length;
+
+ uint8_t sni_type = *input;
+ input += 1;
+
+ /* Currently the only type allowed is host_name
+ (RFC6066 section 3). */
+ if (sni_type != SSL_SNI_TYPE_HOST_NAME) {
+ SCLogDebug("Unknown SNI type");
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_INVALID_SNI_TYPE);
+ return -1;
+ }
+
+ if (!(HAS_SPACE(2)))
+ goto invalid_length;
+
+ uint16_t sni_len = (uint16_t)(*input << 8) | *(input + 1);
+ input += 2;
+
+ /* host_name contains the fully qualified domain name,
+ and should therefore be limited by the maximum domain
+ name length. */
+ if (!(HAS_SPACE(sni_len)) || sni_len > 255 || sni_len == 0) {
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_INVALID_SNI_LENGTH);
+ return -1;
+ }
+
+ /* There must not be more than one extension of the same
+ type (RFC5246 section 7.4.1.4). */
+ if (ssl_state->curr_connp->sni) {
+ SCLogDebug("Multiple SNI extensions");
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_MULTIPLE_SNI_EXTENSIONS);
+ input += sni_len;
+ return (input - initial_input);
+ }
+
+ const size_t sni_strlen = sni_len + 1;
+ ssl_state->curr_connp->sni = SCMalloc(sni_strlen);
+ if (unlikely(ssl_state->curr_connp->sni == NULL))
+ return -1;
+
+ const size_t consumed = input - initial_input;
+ if (SafeMemcpy(ssl_state->curr_connp->sni, 0, sni_strlen,
+ initial_input, consumed, input_len, sni_len) != 0) {
+ SCFree(ssl_state->curr_connp->sni);
+ ssl_state->curr_connp->sni = NULL;
+ return -1;
+ }
+ ssl_state->curr_connp->sni[sni_strlen-1] = 0;
+
+ input += sni_len;
+
+ return (input - initial_input);
+
+invalid_length:
+ SCLogDebug("TLS handshake invalid length");
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
+
+
+ return -1;
+}
+
+static inline int TLSDecodeHSHelloExtensionSupportedVersions(SSLState *ssl_state,
+ const uint8_t * const initial_input,
+ const uint32_t input_len)
+{
+ const uint8_t *input = initial_input;
+
+ /* Empty extension */
+ if (input_len == 0)
+ return 0;
+
+ if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
+ if (!(HAS_SPACE(1)))
+ goto invalid_length;
+
+ uint8_t supported_ver_len = *input;
+ input += 1;
+
+ if (supported_ver_len < 2)
+ goto invalid_length;
+
+ if (!(HAS_SPACE(supported_ver_len)))
+ goto invalid_length;
+
+ /* Use the first (and prefered) valid version as client version,
+ * skip over GREASE and other possible noise. */
+ uint16_t i = 0;
+ while (i + 1 < (uint16_t)supported_ver_len) {
+ uint16_t ver = (uint16_t)(input[i] << 8) | input[i + 1];
+ if (TLSVersionValid(ver)) {
+ ssl_state->curr_connp->version = ver;
+ break;
+ }
+ i += 2;
+ }
+
+ /* Set a flag to indicate that we have seen this extension */
+ ssl_state->flags |= SSL_AL_FLAG_CH_VERSION_EXTENSION;
+
+ input += supported_ver_len;
+ }
+ else if (ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) {
+ if (!(HAS_SPACE(2)))
+ goto invalid_length;
+
+ uint16_t ver = (uint16_t)(*input << 8) | *(input + 1);
+
+ if ((ssl_state->flags & SSL_AL_FLAG_CH_VERSION_EXTENSION) &&
+ (ver > TLS_VERSION_12)) {
+ ssl_state->flags |= SSL_AL_FLAG_LOG_WITHOUT_CERT;
+ }
+
+ ssl_state->curr_connp->version = ver;
+ input += 2;
+ }
+
+ return (input - initial_input);
+
+invalid_length:
+ SCLogDebug("TLS handshake invalid length");
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
+
+ return -1;
+}
+
+static inline int TLSDecodeHSHelloExtensionEllipticCurves(SSLState *ssl_state,
+ const uint8_t * const initial_input,
+ const uint32_t input_len,
+ JA3Buffer *ja3_elliptic_curves)
+{
+ const uint8_t *input = initial_input;
+
+ /* Empty extension */
+ if (input_len == 0)
+ return 0;
+
+ if (!(HAS_SPACE(2)))
+ goto invalid_length;
+
+ uint16_t elliptic_curves_len = (uint16_t)(*input << 8) | *(input + 1);
+ input += 2;
+
+ if (!(HAS_SPACE(elliptic_curves_len)))
+ goto invalid_length;
+
+ if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) &&
+ SC_ATOMIC_GET(ssl_config.enable_ja3)) {
+ uint16_t ec_processed_len = 0;
+ /* coverity[tainted_data] */
+ while (ec_processed_len < elliptic_curves_len)
+ {
+ if (!(HAS_SPACE(2)))
+ goto invalid_length;
+
+ uint16_t elliptic_curve = (uint16_t)(*input << 8) | *(input + 1);
+ input += 2;
+
+ if (TLSDecodeValueIsGREASE(elliptic_curve) != 1) {
+ int rc = Ja3BufferAddValue(&ja3_elliptic_curves,
+ elliptic_curve);
+ if (rc != 0)
+ return -1;
+ }
+
+ ec_processed_len += 2;
+ }
+
+ } else {
+ /* Skip elliptic curves */
+ input += elliptic_curves_len;
+ }
+
+ return (input - initial_input);
+
+invalid_length:
+ SCLogDebug("TLS handshake invalid length");
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
+
+ return -1;
+}
+
+static inline int TLSDecodeHSHelloExtensionEllipticCurvePF(SSLState *ssl_state,
+ const uint8_t * const initial_input,
+ const uint32_t input_len,
+ JA3Buffer *ja3_elliptic_curves_pf)
+{
+ const uint8_t *input = initial_input;
+
+ /* Empty extension */
+ if (input_len == 0)
+ return 0;
+
+ if (!(HAS_SPACE(1)))
+ goto invalid_length;
+
+ uint8_t ec_pf_len = *input;
+ input += 1;
+
+ if (!(HAS_SPACE(ec_pf_len)))
+ goto invalid_length;
+
+ if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) &&
+ SC_ATOMIC_GET(ssl_config.enable_ja3)) {
+ uint8_t ec_pf_processed_len = 0;
+ /* coverity[tainted_data] */
+ while (ec_pf_processed_len < ec_pf_len)
+ {
+ uint8_t elliptic_curve_pf = *input;
+ input += 1;
+
+ if (TLSDecodeValueIsGREASE(elliptic_curve_pf) != 1) {
+ int rc = Ja3BufferAddValue(&ja3_elliptic_curves_pf,
+ elliptic_curve_pf);
+ if (rc != 0)
+ return -1;
+ }
+
+ ec_pf_processed_len += 1;
+ }
+
+ } else {
+ /* Skip elliptic curve point formats */
+ input += ec_pf_len;
+ }
+
+ return (input - initial_input);
+
+invalid_length:
+ SCLogDebug("TLS handshake invalid length");
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
+
+ return -1;
+}
+
+static inline int TLSDecodeHSHelloExtensions(SSLState *ssl_state,
+ const uint8_t * const initial_input,
+ const uint32_t input_len)
+{
+ const uint8_t *input = initial_input;
+
+ int ret;
+ int rc;
+ const bool ja3 = (SC_ATOMIC_GET(ssl_config.enable_ja3) == 1);
+
+ JA3Buffer *ja3_extensions = NULL;
+ JA3Buffer *ja3_elliptic_curves = NULL;
+ JA3Buffer *ja3_elliptic_curves_pf = NULL;
+
+ if (ja3) {
+ ja3_extensions = Ja3BufferInit();
+ if (ja3_extensions == NULL)
+ goto error;
+
+ if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
+ ja3_elliptic_curves = Ja3BufferInit();
+ if (ja3_elliptic_curves == NULL)
+ goto error;
+
+ ja3_elliptic_curves_pf = Ja3BufferInit();
+ if (ja3_elliptic_curves_pf == NULL)
+ goto error;
+ }
+ }
+
+ /* Extensions are optional (RFC5246 section 7.4.1.2) */
+ if (!(HAS_SPACE(2)))
+ goto end;
+
+ uint16_t extensions_len = (uint16_t)(*input << 8) | *(input + 1);
+ input += 2;
+
+ if (!(HAS_SPACE(extensions_len)))
+ goto invalid_length;
+
+ uint16_t processed_len = 0;
+ /* coverity[tainted_data] */
+ while (processed_len < extensions_len)
+ {
+ if (!(HAS_SPACE(2)))
+ goto invalid_length;
+
+ uint16_t ext_type = (uint16_t)(*input << 8) | *(input + 1);
+ input += 2;
+
+ if (!(HAS_SPACE(2)))
+ goto invalid_length;
+
+ uint16_t ext_len = (uint16_t)(*input << 8) | *(input + 1);
+ input += 2;
+
+ if (!(HAS_SPACE(ext_len)))
+ goto invalid_length;
+
+ switch (ext_type) {
+ case SSL_EXTENSION_SNI:
+ {
+ /* coverity[tainted_data] */
+ ret = TLSDecodeHSHelloExtensionSni(ssl_state, input,
+ ext_len);
+ if (ret < 0)
+ goto end;
+
+ input += ret;
+
+ break;
+ }
+
+ case SSL_EXTENSION_ELLIPTIC_CURVES:
+ {
+ /* coverity[tainted_data] */
+ ret = TLSDecodeHSHelloExtensionEllipticCurves(ssl_state, input,
+ ext_len,
+ ja3_elliptic_curves);
+ if (ret < 0)
+ goto end;
+
+ input += ret;
+
+ break;
+ }
+
+ case SSL_EXTENSION_EC_POINT_FORMATS:
+ {
+ /* coverity[tainted_data] */
+ ret = TLSDecodeHSHelloExtensionEllipticCurvePF(ssl_state, input,
+ ext_len,
+ ja3_elliptic_curves_pf);
+ if (ret < 0)
+ goto end;
+
+ input += ret;
+
+ break;
+ }
+
+ case SSL_EXTENSION_EARLY_DATA:
+ {
+ if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
+ /* Used by 0-RTT to indicate that encrypted data will
+ be sent right after the ClientHello record. */
+ ssl_state->flags |= SSL_AL_FLAG_EARLY_DATA;
+ }
+
+ input += ext_len;
+
+ break;
+ }
+
+ case SSL_EXTENSION_SUPPORTED_VERSIONS:
+ {
+ ret = TLSDecodeHSHelloExtensionSupportedVersions(ssl_state, input,
+ ext_len);
+ if (ret < 0)
+ goto end;
+
+ input += ret;
+
+ break;
+ }
+
+ case SSL_EXTENSION_SESSION_TICKET:
+ {
+ if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
+ /* This has to be verified later on by checking if a
+ certificate record has been sent by the server. */
+ ssl_state->flags |= SSL_AL_FLAG_SESSION_RESUMED;
+ }
+
+ input += ext_len;
+
+ break;
+ }
+
+ default:
+ {
+ input += ext_len;
+ break;
+ }
+ }
+
+ if (ja3) {
+ if (TLSDecodeValueIsGREASE(ext_type) != 1) {
+ rc = Ja3BufferAddValue(&ja3_extensions, ext_type);
+ if (rc != 0)
+ goto error;
+ }
+ }
+
+ processed_len += ext_len + 4;
+ }
+
+end:
+ if (ja3) {
+ rc = Ja3BufferAppendBuffer(&ssl_state->curr_connp->ja3_str,
+ &ja3_extensions);
+ if (rc == -1)
+ goto error;
+
+ if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
+ rc = Ja3BufferAppendBuffer(&ssl_state->curr_connp->ja3_str,
+ &ja3_elliptic_curves);
+ if (rc == -1)
+ goto error;
+
+ rc = Ja3BufferAppendBuffer(&ssl_state->curr_connp->ja3_str,
+ &ja3_elliptic_curves_pf);
+ if (rc == -1)
+ goto error;
+ }
+ }
+
+ return (input - initial_input);
+
+invalid_length:
+ SCLogDebug("TLS handshake invalid length");
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_HANDSHAKE_INVALID_LENGTH);
+
+error:
+ if (ja3_extensions != NULL)
+ Ja3BufferFree(&ja3_extensions);
+ if (ja3_elliptic_curves != NULL)
+ Ja3BufferFree(&ja3_elliptic_curves);
+ if (ja3_elliptic_curves_pf != NULL)
+ Ja3BufferFree(&ja3_elliptic_curves_pf);
+
+ return -1;
+}
+
+static int TLSDecodeHandshakeHello(SSLState *ssl_state,
+ const uint8_t * const input,
+ const uint32_t input_len)
+{
+ int ret;
+ uint32_t parsed = 0;
+
+ ret = TLSDecodeHSHelloVersion(ssl_state, input, input_len);
+ if (ret < 0)
+ goto end;
+
+ parsed += ret;
+
+ ret = TLSDecodeHSHelloRandom(ssl_state, input + parsed, input_len - parsed);
+ if (ret < 0)
+ goto end;
+
+ parsed += ret;
+
+ /* The session id field in the server hello record was removed in
+ TLSv1.3 draft1, but was readded in draft22. */
+ if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) ||
+ ((ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) &&
+ ((ssl_state->flags & SSL_AL_FLAG_LOG_WITHOUT_CERT) == 0))) {
+ ret = TLSDecodeHSHelloSessionID(ssl_state, input + parsed,
+ input_len - parsed);
+ if (ret < 0)
+ goto end;
+
+ parsed += ret;
+ }
+
+ ret = TLSDecodeHSHelloCipherSuites(ssl_state, input + parsed,
+ input_len - parsed);
+ if (ret < 0)
+ goto end;
+
+ parsed += ret;
+
+ /* The compression methods field in the server hello record was
+ removed in TLSv1.3 draft1, but was readded in draft22. */
+ if ((ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) ||
+ ((ssl_state->current_flags & SSL_AL_FLAG_STATE_SERVER_HELLO) &&
+ ((ssl_state->flags & SSL_AL_FLAG_LOG_WITHOUT_CERT) == 0))) {
+ ret = TLSDecodeHSHelloCompressionMethods(ssl_state, input + parsed,
+ input_len - parsed);
+ if (ret < 0)
+ goto end;
+
+ parsed += ret;
+ }
+
+ ret = TLSDecodeHSHelloExtensions(ssl_state, input + parsed,
+ input_len - parsed);
+ if (ret < 0)
+ goto end;
+
+ if (SC_ATOMIC_GET(ssl_config.enable_ja3) && ssl_state->curr_connp->ja3_hash == NULL) {
+ ssl_state->curr_connp->ja3_hash = Ja3GenerateHash(ssl_state->curr_connp->ja3_str);
+ }
+
+end:
+ return 0;
+}
+
+#ifdef DEBUG_VALIDATION
+static inline bool
+RecordAlreadyProcessed(const SSLStateConnp *curr_connp)
+{
+ return ((curr_connp->record_length + SSLV3_RECORD_HDR_LEN) <
+ curr_connp->bytes_processed);
+}
+#endif
+
+static inline int SSLv3ParseHandshakeTypeCertificate(SSLState *ssl_state, SSLStateConnp *connp,
+ const uint8_t *const initial_input, const uint32_t input_len)
+{
+ int rc = TlsDecodeHSCertificates(ssl_state, connp, initial_input, input_len);
+ SCLogDebug("rc %d", rc);
+ if (rc > 0) {
+ DEBUG_VALIDATE_BUG_ON(rc > (int)input_len);
+ SSLParserHSReset(connp);
+ } else if (rc < 0) {
+ SCLogDebug("error parsing cert, reset state");
+ SSLParserHSReset(connp);
+ /* fall through to still consume the cert bytes */
+ }
+ return input_len;
+}
+
+static int SupportedHandshakeType(const uint8_t type)
+{
+ switch (type) {
+ case SSLV3_HS_CLIENT_HELLO:
+ case SSLV3_HS_SERVER_HELLO:
+ case SSLV3_HS_SERVER_KEY_EXCHANGE:
+ case SSLV3_HS_CLIENT_KEY_EXCHANGE:
+ case SSLV3_HS_CERTIFICATE:
+ case SSLV3_HS_HELLO_REQUEST:
+ case SSLV3_HS_CERTIFICATE_REQUEST:
+ case SSLV3_HS_CERTIFICATE_VERIFY:
+ case SSLV3_HS_FINISHED:
+ case SSLV3_HS_CERTIFICATE_URL:
+ case SSLV3_HS_CERTIFICATE_STATUS:
+ case SSLV3_HS_NEW_SESSION_TICKET:
+ case SSLV3_HS_SERVER_HELLO_DONE:
+ return true;
+ break;
+
+ default:
+ return false;
+ break;
+ }
+}
+
+/**
+ * \retval parsed number of consumed bytes
+ * \retval < 0 error
+ */
+static int SSLv3ParseHandshakeType(SSLState *ssl_state, const uint8_t *input,
+ uint32_t input_len, uint8_t direction)
+{
+ const uint8_t *initial_input = input;
+ int rc;
+
+ if (input_len == 0) {
+ return 0;
+ }
+ DEBUG_VALIDATE_BUG_ON(RecordAlreadyProcessed(ssl_state->curr_connp));
+
+ switch (ssl_state->curr_connp->handshake_type) {
+ case SSLV3_HS_CLIENT_HELLO:
+ ssl_state->current_flags = SSL_AL_FLAG_STATE_CLIENT_HELLO;
+
+ rc = TLSDecodeHandshakeHello(ssl_state, input, input_len);
+ if (rc < 0)
+ return rc;
+ break;
+
+ case SSLV3_HS_SERVER_HELLO:
+ ssl_state->current_flags = SSL_AL_FLAG_STATE_SERVER_HELLO;
+
+ DEBUG_VALIDATE_BUG_ON(ssl_state->curr_connp->message_length != input_len);
+ rc = TLSDecodeHandshakeHello(ssl_state, input, input_len);
+ if (rc < 0)
+ return rc;
+ break;
+
+ case SSLV3_HS_SERVER_KEY_EXCHANGE:
+ ssl_state->current_flags = SSL_AL_FLAG_STATE_SERVER_KEYX;
+ break;
+
+ case SSLV3_HS_CLIENT_KEY_EXCHANGE:
+ ssl_state->current_flags = SSL_AL_FLAG_STATE_CLIENT_KEYX;
+ break;
+
+ case SSLV3_HS_CERTIFICATE:
+
+ rc = SSLv3ParseHandshakeTypeCertificate(ssl_state,
+ direction ? &ssl_state->server_connp : &ssl_state->client_connp, initial_input,
+ input_len);
+ if (rc < 0)
+ return rc;
+ break;
+
+ case SSLV3_HS_HELLO_REQUEST:
+ break;
+ case SSLV3_HS_CERTIFICATE_REQUEST:
+ if (direction) {
+ ssl_state->current_flags = SSL_AL_FLAG_NEED_CLIENT_CERT;
+ }
+ break;
+ case SSLV3_HS_CERTIFICATE_VERIFY:
+ case SSLV3_HS_FINISHED:
+ case SSLV3_HS_CERTIFICATE_URL:
+ case SSLV3_HS_CERTIFICATE_STATUS:
+ break;
+ case SSLV3_HS_NEW_SESSION_TICKET:
+ SCLogDebug("new session ticket");
+ break;
+ case SSLV3_HS_SERVER_HELLO_DONE:
+ break;
+ default:
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return -1;
+ }
+
+ ssl_state->flags |= ssl_state->current_flags;
+
+ SCLogDebug("message: length %u", ssl_state->curr_connp->message_length);
+ SCLogDebug("input_len %u ssl_state->curr_connp->bytes_processed %u", input_len, ssl_state->curr_connp->bytes_processed);
+
+ return input_len;
+}
+
+static int SSLv3ParseHandshakeProtocol(SSLState *ssl_state, const uint8_t *input,
+ uint32_t input_len, uint8_t direction)
+{
+ const uint8_t *initial_input = input;
+
+ if (input_len == 0 || ssl_state->curr_connp->bytes_processed ==
+ (ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN)) {
+ SCReturnInt(0);
+ }
+
+ while (input_len) {
+ SCLogDebug("input_len %u", input_len);
+
+ if (ssl_state->curr_connp->hs_buffer != NULL) {
+ SCLogDebug("partial handshake record in place");
+ const uint32_t need = ssl_state->curr_connp->hs_buffer_message_size -
+ ssl_state->curr_connp->hs_buffer_offset;
+ const uint32_t add = MIN(need, input_len);
+
+ /* grow buffer to next multiple of 4k that fits all data we have */
+ if (ssl_state->curr_connp->hs_buffer_offset + add >
+ ssl_state->curr_connp->hs_buffer_size) {
+ const uint32_t avail = ssl_state->curr_connp->hs_buffer_offset + add;
+ const uint32_t new_size = avail + (4096 - (avail % 4096));
+ SCLogDebug("new_size %u, avail %u", new_size, avail);
+ void *ptr = SCRealloc(ssl_state->curr_connp->hs_buffer, new_size);
+ if (ptr == NULL)
+ return -1;
+ ssl_state->curr_connp->hs_buffer = ptr;
+ ssl_state->curr_connp->hs_buffer_size = new_size;
+ }
+
+ SCLogDebug("ssl_state->curr_connp->hs_buffer_offset %u "
+ "ssl_state->curr_connp->hs_buffer_size %u",
+ ssl_state->curr_connp->hs_buffer_offset, ssl_state->curr_connp->hs_buffer_size);
+ SCLogDebug("to add %u total %u", add, ssl_state->curr_connp->hs_buffer_offset + add);
+
+ if (SafeMemcpy(ssl_state->curr_connp->hs_buffer,
+ ssl_state->curr_connp->hs_buffer_offset,
+ ssl_state->curr_connp->hs_buffer_size, input, 0, add, add) != 0) {
+ SCLogDebug("copy failed");
+ return -1;
+ }
+ ssl_state->curr_connp->hs_buffer_offset += add;
+
+ if (ssl_state->curr_connp->hs_buffer_message_size <=
+ ssl_state->curr_connp->hs_buffer_offset) {
+ DEBUG_VALIDATE_BUG_ON(ssl_state->curr_connp->hs_buffer_message_size !=
+ ssl_state->curr_connp->hs_buffer_offset);
+
+ ssl_state->curr_connp->handshake_type =
+ ssl_state->curr_connp->hs_buffer_message_type;
+ ssl_state->curr_connp->message_length =
+ ssl_state->curr_connp->hs_buffer_message_size;
+
+ SCLogDebug("got all data now: handshake_type %u message_length %u",
+ ssl_state->curr_connp->handshake_type,
+ ssl_state->curr_connp->message_length);
+
+ int retval = SSLv3ParseHandshakeType(ssl_state, ssl_state->curr_connp->hs_buffer,
+ ssl_state->curr_connp->hs_buffer_offset, direction);
+ if (retval < 0) {
+ SSLParserHSReset(ssl_state->curr_connp);
+ return (retval);
+ }
+ SCLogDebug("retval %d", retval);
+
+ /* data processed, reset buffer */
+ SCFree(ssl_state->curr_connp->hs_buffer);
+ ssl_state->curr_connp->hs_buffer = NULL;
+ ssl_state->curr_connp->hs_buffer_size = 0;
+ ssl_state->curr_connp->hs_buffer_message_size = 0;
+ ssl_state->curr_connp->hs_buffer_message_type = 0;
+ ssl_state->curr_connp->hs_buffer_offset = 0;
+ } else {
+ SCLogDebug("partial data");
+ }
+
+ input += add;
+ input_len -= add;
+ SCLogDebug("input_len %u", input_len);
+ SSLParserHSReset(ssl_state->curr_connp);
+ continue;
+ }
+
+ SCLogDebug("bytes_processed %u", ssl_state->curr_connp->bytes_processed);
+ SCLogDebug("input %p input_len %u", input, input_len);
+
+ if (input_len < 4) {
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ SCReturnInt(-1);
+ }
+
+ ssl_state->curr_connp->handshake_type = input[0];
+ ssl_state->curr_connp->message_length = input[1] << 16 | input[2] << 8 | input[3];
+ SCLogDebug("handshake_type %u message len %u input %p input_len %u",
+ ssl_state->curr_connp->handshake_type, ssl_state->curr_connp->message_length, input,
+ input_len);
+ input += 4;
+ input_len -= 4;
+
+ const uint32_t record_len = ssl_state->curr_connp->message_length;
+ /* see if we support this type. We check here to not use the fragment
+ * handling on things we don't support. */
+ const bool supported_type = SupportedHandshakeType(ssl_state->curr_connp->handshake_type);
+ SCLogDebug("supported_type %s handshake_type %u/%02x", supported_type ? "true" : "false",
+ ssl_state->curr_connp->handshake_type, ssl_state->curr_connp->handshake_type);
+ if (!supported_type) {
+ uint32_t avail_record_len = MIN(input_len, record_len);
+ input += avail_record_len;
+ input_len -= avail_record_len;
+
+ SSLParserHSReset(ssl_state->curr_connp);
+
+ if ((direction && (ssl_state->flags & SSL_AL_FLAG_SERVER_CHANGE_CIPHER_SPEC)) ||
+ (!direction && (ssl_state->flags & SSL_AL_FLAG_CLIENT_CHANGE_CIPHER_SPEC))) {
+ // after Change Cipher Spec we get Encrypted Handshake Messages
+ } else {
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_HANDSHAKE_MESSAGE);
+ }
+ continue;
+ }
+
+ /* if the message length exceeds our input_len, we have a tls fragment. */
+ if (record_len > input_len) {
+ const uint32_t avail = input_len;
+ const uint32_t size = avail + (4096 - (avail % 4096));
+ SCLogDebug("initial buffer size %u, based on input %u", size, avail);
+ ssl_state->curr_connp->hs_buffer = SCCalloc(1, size);
+ if (ssl_state->curr_connp->hs_buffer == NULL) {
+ return -1;
+ }
+ ssl_state->curr_connp->hs_buffer_size = size;
+ ssl_state->curr_connp->hs_buffer_message_size = record_len;
+ ssl_state->curr_connp->hs_buffer_message_type = ssl_state->curr_connp->handshake_type;
+
+ if (input_len > 0) {
+ if (SafeMemcpy(ssl_state->curr_connp->hs_buffer, 0,
+ ssl_state->curr_connp->hs_buffer_size, input, 0, input_len,
+ input_len) != 0) {
+ return -1;
+ }
+ ssl_state->curr_connp->hs_buffer_offset = input_len;
+ }
+ SCLogDebug("opened record buffer %p size %u offset %u type %u msg_size %u",
+ ssl_state->curr_connp->hs_buffer, ssl_state->curr_connp->hs_buffer_size,
+ ssl_state->curr_connp->hs_buffer_offset,
+ ssl_state->curr_connp->hs_buffer_message_type,
+ ssl_state->curr_connp->hs_buffer_message_size);
+ input += input_len;
+ SSLParserHSReset(ssl_state->curr_connp);
+ return (input - initial_input);
+
+ } else {
+ /* full record, parse it now */
+ int retval = SSLv3ParseHandshakeType(
+ ssl_state, input, ssl_state->curr_connp->message_length, direction);
+ if (retval < 0 || retval > (int)input_len) {
+ DEBUG_VALIDATE_BUG_ON(retval > (int)input_len);
+ return (retval);
+ }
+ SCLogDebug("retval %d input_len %u", retval, input_len);
+ input += retval;
+ input_len -= retval;
+
+ SSLParserHSReset(ssl_state->curr_connp);
+ }
+ SCLogDebug("input_len left %u", input_len);
+ }
+ return (input - initial_input);
+}
+
+/**
+ * \internal
+ * \brief TLS Heartbeat parser (see RFC 6520)
+ *
+ * \param sslstate Pointer to the SSL state.
+ * \param input Pointer to the received input data.
+ * \param input_len Length in bytes of the received data.
+ * \param direction 1 toclient, 0 toserver
+ *
+ * \retval The number of bytes parsed on success, 0 if nothing parsed, -1 on failure.
+ */
+static int SSLv3ParseHeartbeatProtocol(SSLState *ssl_state, const uint8_t *input,
+ uint32_t input_len, uint8_t direction)
+{
+ uint8_t hb_type;
+ uint16_t payload_len;
+ uint32_t padding_len;
+
+ /* expect at least 3 bytes: heartbeat type (1) + length (2) */
+ if (input_len < 3) {
+ return 0;
+ }
+
+ hb_type = *input++;
+
+ if (!(ssl_state->flags & SSL_AL_FLAG_CHANGE_CIPHER_SPEC)) {
+ if (!(hb_type == TLS_HB_REQUEST || hb_type == TLS_HB_RESPONSE)) {
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_HEARTBEAT);
+ return -1;
+ }
+ }
+
+ if ((ssl_state->flags & SSL_AL_FLAG_HB_INFLIGHT) == 0) {
+ ssl_state->flags |= SSL_AL_FLAG_HB_INFLIGHT;
+
+ if (direction) {
+ SCLogDebug("HeartBeat Record type sent in the toclient direction!");
+ ssl_state->flags |= SSL_AL_FLAG_HB_SERVER_INIT;
+ } else {
+ SCLogDebug("HeartBeat Record type sent in the toserver direction!");
+ ssl_state->flags |= SSL_AL_FLAG_HB_CLIENT_INIT;
+ }
+
+ /* if we reach this point, then we can assume that the HB request
+ is encrypted. If so, let's set the HB record length */
+ if (ssl_state->flags & SSL_AL_FLAG_CHANGE_CIPHER_SPEC) {
+ ssl_state->hb_record_len = ssl_state->curr_connp->record_length;
+ SCLogDebug("Encrypted HeartBeat Request In-flight. Storing len %u",
+ ssl_state->hb_record_len);
+ return (ssl_state->curr_connp->record_length - 3);
+ }
+
+ payload_len = (uint16_t)(*input << 8) | *(input + 1);
+
+ /* check that the requested payload length is really present in
+ the record (CVE-2014-0160) */
+ if ((uint32_t)(payload_len+3) > ssl_state->curr_connp->record_length) {
+ SCLogDebug("We have a short record in HeartBeat Request");
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_OVERFLOW_HEARTBEAT);
+ return -1;
+ }
+
+ /* check the padding length. It must be at least 16 bytes
+ (RFC 6520, section 4) */
+ padding_len = ssl_state->curr_connp->record_length - payload_len - 3;
+ if (padding_len < 16) {
+ SCLogDebug("We have a short record in HeartBeat Request");
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_HEARTBEAT);
+ return -1;
+ }
+
+ /* we don't have the payload */
+ if (input_len < payload_len + padding_len) {
+ return 0;
+ }
+
+ /* OpenSSL still seems to discard multiple in-flight
+ heartbeats although some tools send multiple at once */
+ } else if (direction == 1 && (ssl_state->flags & SSL_AL_FLAG_HB_INFLIGHT) &&
+ (ssl_state->flags & SSL_AL_FLAG_HB_SERVER_INIT)) {
+ SCLogDebug("Multiple in-flight server initiated HeartBeats");
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_HEARTBEAT);
+ return -1;
+
+ } else if (direction == 0 && (ssl_state->flags & SSL_AL_FLAG_HB_INFLIGHT) &&
+ (ssl_state->flags & SSL_AL_FLAG_HB_CLIENT_INIT)) {
+ SCLogDebug("Multiple in-flight client initiated HeartBeats");
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_HEARTBEAT);
+ return -1;
+
+ } else {
+ /* we have a HB record in the opposite direction of the request,
+ let's reset our flags */
+ ssl_state->flags &= ~SSL_AL_FLAG_HB_INFLIGHT;
+ ssl_state->flags &= ~SSL_AL_FLAG_HB_SERVER_INIT;
+ ssl_state->flags &= ~SSL_AL_FLAG_HB_CLIENT_INIT;
+
+ /* if we reach this point, then we can assume that the HB request
+ is encrypted. If so, let's set the HB record length */
+ if (ssl_state->flags & SSL_AL_FLAG_CHANGE_CIPHER_SPEC) {
+ /* check to see if the encrypted response is longer than the
+ encrypted request */
+ if (ssl_state->hb_record_len > 0 && ssl_state->hb_record_len <
+ ssl_state->curr_connp->record_length) {
+ SCLogDebug("My heart is bleeding.. OpenSSL HeartBleed response (%u)",
+ ssl_state->hb_record_len);
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_DATALEAK_HEARTBEAT_MISMATCH);
+ ssl_state->hb_record_len = 0;
+ return -1;
+ }
+ }
+
+ /* reset the HB record length in case we have a legit HB followed
+ by a bad one */
+ ssl_state->hb_record_len = 0;
+ }
+
+ /* skip the HeartBeat, 3 bytes were already parsed,
+ e.g |18 03 02| for TLS 1.2 */
+ return (ssl_state->curr_connp->record_length - 3);
+}
+
+static int SSLv3ParseRecord(uint8_t direction, SSLState *ssl_state,
+ const uint8_t *input, uint32_t input_len)
+{
+ const uint8_t *initial_input = input;
+
+ if (input_len == 0) {
+ return 0;
+ }
+
+ uint8_t skip_version = 0;
+
+ /* Only set SSL/TLS version here if it has not already been set in
+ client/server hello. */
+ if (direction == 0) {
+ if ((ssl_state->flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) &&
+ (ssl_state->client_connp.version != TLS_VERSION_UNKNOWN)) {
+ skip_version = 1;
+ }
+ } else {
+ if ((ssl_state->flags & SSL_AL_FLAG_STATE_SERVER_HELLO) &&
+ (ssl_state->server_connp.version != TLS_VERSION_UNKNOWN)) {
+ skip_version = 1;
+ }
+ }
+
+ switch (ssl_state->curr_connp->bytes_processed) {
+ case 0:
+ if (input_len >= 5) {
+ ssl_state->curr_connp->content_type = input[0];
+ if (!skip_version) {
+ ssl_state->curr_connp->version = (uint16_t)(input[1] << 8) | input[2];
+ }
+ ssl_state->curr_connp->record_length = input[3] << 8;
+ ssl_state->curr_connp->record_length |= input[4];
+ ssl_state->curr_connp->bytes_processed += SSLV3_RECORD_HDR_LEN;
+ return SSLV3_RECORD_HDR_LEN;
+ } else {
+ ssl_state->curr_connp->content_type = *(input++);
+ if (--input_len == 0)
+ break;
+ }
+
+ /* fall through */
+ case 1:
+ if (!skip_version) {
+ ssl_state->curr_connp->version = (uint16_t)(*(input++) << 8);
+ } else {
+ input++;
+ }
+ if (--input_len == 0)
+ break;
+
+ /* fall through */
+ case 2:
+ if (!skip_version) {
+ ssl_state->curr_connp->version |= *(input++);
+ } else {
+ input++;
+ }
+ if (--input_len == 0)
+ break;
+
+ /* fall through */
+ case 3:
+ ssl_state->curr_connp->record_length = *(input++) << 8;
+ if (--input_len == 0)
+ break;
+
+ /* fall through */
+ case 4:
+ ssl_state->curr_connp->record_length |= *(input++);
+ if (--input_len == 0)
+ break;
+
+ /* fall through */
+ }
+
+ ssl_state->curr_connp->bytes_processed += (input - initial_input);
+
+ return (input - initial_input);
+}
+
+static int SSLv2ParseRecord(uint8_t direction, SSLState *ssl_state,
+ const uint8_t *input, uint32_t input_len)
+{
+ const uint8_t *initial_input = input;
+
+ if (input_len == 0) {
+ return 0;
+ }
+
+ if (ssl_state->curr_connp->record_lengths_length == 2) {
+ switch (ssl_state->curr_connp->bytes_processed) {
+ case 0:
+ if (input_len >= ssl_state->curr_connp->record_lengths_length + 1) {
+ ssl_state->curr_connp->record_length = (0x7f & input[0]) << 8 | input[1];
+ ssl_state->curr_connp->content_type = input[2];
+ ssl_state->curr_connp->version = SSL_VERSION_2;
+ ssl_state->curr_connp->bytes_processed += 3;
+ return 3;
+ } else {
+ ssl_state->curr_connp->record_length = (0x7f & *(input++)) << 8;
+ if (--input_len == 0)
+ break;
+ }
+
+ /* fall through */
+ case 1:
+ ssl_state->curr_connp->record_length |= *(input++);
+ if (--input_len == 0)
+ break;
+
+ /* fall through */
+ case 2:
+ ssl_state->curr_connp->content_type = *(input++);
+ ssl_state->curr_connp->version = SSL_VERSION_2;
+ if (--input_len == 0)
+ break;
+
+ /* fall through */
+ }
+
+ } else {
+ switch (ssl_state->curr_connp->bytes_processed) {
+ case 0:
+ if (input_len >= ssl_state->curr_connp->record_lengths_length + 1) {
+ ssl_state->curr_connp->record_length = (0x3f & input[0]) << 8 | input[1];
+ ssl_state->curr_connp->content_type = input[3];
+ ssl_state->curr_connp->version = SSL_VERSION_2;
+ ssl_state->curr_connp->bytes_processed += 4;
+ return 4;
+ } else {
+ ssl_state->curr_connp->record_length = (0x3f & *(input++)) << 8;
+ if (--input_len == 0)
+ break;
+ }
+
+ /* fall through */
+ case 1:
+ ssl_state->curr_connp->record_length |= *(input++);
+ if (--input_len == 0)
+ break;
+
+ /* fall through */
+ case 2:
+ /* padding */
+ input++;
+ if (--input_len == 0)
+ break;
+
+ /* fall through */
+ case 3:
+ ssl_state->curr_connp->content_type = *(input++);
+ ssl_state->curr_connp->version = SSL_VERSION_2;
+ if (--input_len == 0)
+ break;
+
+ /* fall through */
+ }
+ }
+
+ ssl_state->curr_connp->bytes_processed += (input - initial_input);
+
+ return (input - initial_input);
+}
+
+static struct SSLDecoderResult SSLv2Decode(uint8_t direction, SSLState *ssl_state,
+ AppLayerParserState *pstate, const uint8_t *input, uint32_t input_len,
+ const StreamSlice stream_slice)
+{
+ const uint8_t *initial_input = input;
+
+ if (ssl_state->curr_connp->bytes_processed == 0) {
+ if (input[0] & 0x80) {
+ ssl_state->curr_connp->record_lengths_length = 2;
+ } else {
+ ssl_state->curr_connp->record_lengths_length = 3;
+ }
+
+ SCLogDebug("record start: ssl2.hdr frame");
+ AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input,
+ ssl_state->curr_connp->record_lengths_length + 1, direction, TLS_FRAME_SSLV2_HDR);
+ }
+
+ SCLogDebug("direction %u ssl_state->curr_connp->record_lengths_length + 1 %u, "
+ "ssl_state->curr_connp->bytes_processed %u",
+ direction, ssl_state->curr_connp->record_lengths_length + 1,
+ ssl_state->curr_connp->bytes_processed);
+ /* the +1 is because we read one extra byte inside SSLv2ParseRecord
+ to read the msg_type */
+ if (ssl_state->curr_connp->bytes_processed <
+ (ssl_state->curr_connp->record_lengths_length + 1)) {
+ const int retval = SSLv2ParseRecord(direction, ssl_state, input, input_len);
+ SCLogDebug("retval %d ssl_state->curr_connp->record_length %u", retval,
+ ssl_state->curr_connp->record_length);
+ if (retval < 0 || retval > (int)input_len) {
+ DEBUG_VALIDATE_BUG_ON(retval > (int)input_len);
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSLV2_HEADER);
+ return SSL_DECODER_ERROR(-1);
+ }
+
+ AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input,
+ ssl_state->curr_connp->record_lengths_length + ssl_state->curr_connp->record_length,
+ direction, TLS_FRAME_SSLV2_PDU);
+ SCLogDebug("record start: ssl2.pdu frame");
+
+ input += retval;
+ input_len -= retval;
+ }
+
+ /* if we don't have the full record, we return incomplete */
+ if (ssl_state->curr_connp->record_lengths_length + ssl_state->curr_connp->record_length >
+ input_len + ssl_state->curr_connp->bytes_processed) {
+ uint32_t needed = ssl_state->curr_connp->record_length;
+ SCLogDebug("record len %u input_len %u parsed %u: need %u bytes more data",
+ ssl_state->curr_connp->record_length, input_len, (uint32_t)(input - initial_input),
+ needed);
+ return SSL_DECODER_INCOMPLETE((input - initial_input), needed);
+ }
+
+ if (input_len == 0) {
+ return SSL_DECODER_OK((input - initial_input));
+ }
+
+ /* record_length should never be zero */
+ if (ssl_state->curr_connp->record_length == 0) {
+ SCLogDebug("SSLv2 record length is zero");
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSLV2_HEADER);
+ return SSL_DECODER_ERROR(-1);
+ }
+
+ /* record_lengths_length should never be zero */
+ if (ssl_state->curr_connp->record_lengths_length == 0) {
+ SCLogDebug("SSLv2 record lengths length is zero");
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSLV2_HEADER);
+ return SSL_DECODER_ERROR(-1);
+ }
+
+ switch (ssl_state->curr_connp->content_type) {
+ case SSLV2_MT_ERROR:
+ SCLogDebug("SSLV2_MT_ERROR msg_type received. Error encountered "
+ "in establishing the sslv2 session, may be version");
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_ERROR_MSG_ENCOUNTERED);
+
+ break;
+
+ case SSLV2_MT_CLIENT_HELLO:
+ if (input_len < 6) {
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return SSL_DECODER_ERROR(-1);
+ }
+
+ ssl_state->current_flags = SSL_AL_FLAG_STATE_CLIENT_HELLO;
+ ssl_state->current_flags |= SSL_AL_FLAG_SSL_CLIENT_HS;
+
+ const uint16_t version = (uint16_t)(input[0] << 8) | input[1];
+ SCLogDebug("SSLv2: version %04x", version);
+ ssl_state->curr_connp->version = version;
+ uint16_t session_id_length = (input[5]) | (uint16_t)(input[4] << 8);
+ input += 6;
+ input_len -= 6;
+ ssl_state->curr_connp->bytes_processed += 6;
+ if (session_id_length == 0) {
+ ssl_state->current_flags |= SSL_AL_FLAG_SSL_NO_SESSION_ID;
+ }
+ break;
+
+ case SSLV2_MT_CLIENT_MASTER_KEY:
+ if (!(ssl_state->flags & SSL_AL_FLAG_SSL_CLIENT_HS)) {
+ SCLogDebug("Client hello is not seen before master key "
+ "message!");
+ }
+ ssl_state->current_flags = SSL_AL_FLAG_SSL_CLIENT_MASTER_KEY;
+
+ break;
+
+ case SSLV2_MT_CLIENT_CERTIFICATE:
+ if (direction == 1) {
+ SCLogDebug("Incorrect SSL Record type sent in the toclient "
+ "direction!");
+ } else {
+ ssl_state->current_flags = SSL_AL_FLAG_STATE_CLIENT_KEYX;
+ }
+
+ /* fall through */
+ case SSLV2_MT_SERVER_VERIFY:
+ case SSLV2_MT_SERVER_FINISHED:
+ if (direction == 0 &&
+ !(ssl_state->curr_connp->content_type &
+ SSLV2_MT_CLIENT_CERTIFICATE)) {
+ SCLogDebug("Incorrect SSL Record type sent in the toserver "
+ "direction!");
+ }
+
+ /* fall through */
+ case SSLV2_MT_CLIENT_FINISHED:
+ case SSLV2_MT_REQUEST_CERTIFICATE:
+ /* both client hello and server hello must be seen */
+ if ((ssl_state->flags & SSL_AL_FLAG_SSL_CLIENT_HS) &&
+ (ssl_state->flags & SSL_AL_FLAG_SSL_SERVER_HS)) {
+
+ if (direction == 0) {
+ if (ssl_state->flags & SSL_AL_FLAG_SSL_NO_SESSION_ID) {
+ ssl_state->current_flags |= SSL_AL_FLAG_SSL_CLIENT_SSN_ENCRYPTED;
+ SCLogDebug("SSLv2 client side has started the encryption");
+ } else if (ssl_state->flags & SSL_AL_FLAG_SSL_CLIENT_MASTER_KEY) {
+ ssl_state->current_flags = SSL_AL_FLAG_SSL_CLIENT_SSN_ENCRYPTED;
+ SCLogDebug("SSLv2 client side has started the encryption");
+ }
+ } else {
+ ssl_state->current_flags = SSL_AL_FLAG_SSL_SERVER_SSN_ENCRYPTED;
+ SCLogDebug("SSLv2 Server side has started the encryption");
+ }
+
+ if ((ssl_state->flags & SSL_AL_FLAG_SSL_CLIENT_SSN_ENCRYPTED) &&
+ (ssl_state->flags & SSL_AL_FLAG_SSL_SERVER_SSN_ENCRYPTED))
+ {
+ if (ssl_config.encrypt_mode != SSL_CNF_ENC_HANDLE_FULL) {
+ AppLayerParserStateSetFlag(pstate,
+ APP_LAYER_PARSER_NO_INSPECTION);
+ }
+
+ if (ssl_config.encrypt_mode == SSL_CNF_ENC_HANDLE_BYPASS) {
+ AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_NO_REASSEMBLY);
+ AppLayerParserStateSetFlag(pstate, APP_LAYER_PARSER_BYPASS_READY);
+ }
+ SCLogDebug("SSLv2 No reassembly & inspection has been set");
+ }
+ }
+
+ break;
+
+ case SSLV2_MT_SERVER_HELLO:
+ ssl_state->current_flags = SSL_AL_FLAG_STATE_SERVER_HELLO;
+ ssl_state->current_flags |= SSL_AL_FLAG_SSL_SERVER_HS;
+
+ break;
+ }
+
+ ssl_state->flags |= ssl_state->current_flags;
+
+ if (input_len + ssl_state->curr_connp->bytes_processed >=
+ (ssl_state->curr_connp->record_length +
+ ssl_state->curr_connp->record_lengths_length)) {
+
+ /* looks like we have another record after this */
+ uint32_t diff = ssl_state->curr_connp->record_length +
+ ssl_state->curr_connp->record_lengths_length + -
+ ssl_state->curr_connp->bytes_processed;
+ input += diff;
+ SSLParserReset(ssl_state);
+
+ /* we still don't have the entire record for the one we are
+ currently parsing */
+ } else {
+ input += input_len;
+ ssl_state->curr_connp->bytes_processed += input_len;
+ }
+ return SSL_DECODER_OK((input - initial_input));
+}
+
+static struct SSLDecoderResult SSLv3Decode(uint8_t direction, SSLState *ssl_state,
+ AppLayerParserState *pstate, const uint8_t *input, const uint32_t input_len,
+ const StreamSlice stream_slice)
+{
+ uint32_t parsed = 0;
+ uint32_t record_len; /* slice of input_len for the current record */
+ const bool first_call = (ssl_state->curr_connp->bytes_processed == 0);
+
+ if (ssl_state->curr_connp->bytes_processed < SSLV3_RECORD_HDR_LEN) {
+ const uint16_t prev_version = ssl_state->curr_connp->version;
+
+ int retval = SSLv3ParseRecord(direction, ssl_state, input, input_len);
+ if (retval < 0 || retval > (int)input_len) {
+ DEBUG_VALIDATE_BUG_ON(retval > (int)input_len);
+ SCLogDebug("SSLv3ParseRecord returned %d", retval);
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_TLS_HEADER);
+ return SSL_DECODER_ERROR(-1);
+ }
+ parsed = retval;
+
+ SCLogDebug("%s input %p record_length %u", (direction == 0) ? "toserver" : "toclient",
+ input, ssl_state->curr_connp->record_length);
+
+ /* first the hdr frame at our first chance */
+ if (first_call) {
+ AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input, SSLV3_RECORD_HDR_LEN,
+ direction, TLS_FRAME_HDR);
+ }
+
+ /* parser is streaming for the initial header, then switches to incomplete
+ * API: so if we don't have the hdr yet, return consumed bytes and wait
+ * until we are called again with new data. */
+ if (ssl_state->curr_connp->bytes_processed < SSLV3_RECORD_HDR_LEN) {
+ SCLogDebug(
+ "incomplete header, return %u bytes consumed and wait for more data", parsed);
+ return SSL_DECODER_OK(parsed);
+ }
+
+ /* pdu frame needs record length, so only create it when hdr fully parsed. */
+ AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input,
+ ssl_state->curr_connp->record_length + retval, direction, TLS_FRAME_PDU);
+ record_len = MIN(input_len - parsed, ssl_state->curr_connp->record_length);
+ SCLogDebug(
+ "record_len %u (input_len %u, parsed %u, ssl_state->curr_connp->record_length %u)",
+ record_len, input_len, parsed, ssl_state->curr_connp->record_length);
+
+ bool unknown_record = false;
+ switch (ssl_state->curr_connp->content_type) {
+ case SSLV3_CHANGE_CIPHER_SPEC:
+ case SSLV3_ALERT_PROTOCOL:
+ case SSLV3_HANDSHAKE_PROTOCOL:
+ case SSLV3_APPLICATION_PROTOCOL:
+ case SSLV3_HEARTBEAT_PROTOCOL:
+ break;
+ default:
+ unknown_record = true;
+ break;
+ }
+
+ /* unknown record type. For TLS 1.0, 1.1 and 1.2 this is ok. For the rest it is fatal. Based
+ * on Wireshark logic. */
+ if (prev_version == TLS_VERSION_10 || prev_version == TLS_VERSION_11) {
+ if (unknown_record) {
+ SCLogDebug("unknown record, ignore it");
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_RECORD_TYPE);
+
+ ssl_state->curr_connp->bytes_processed = 0; // TODO review this reset logic
+ ssl_state->curr_connp->content_type = 0;
+ ssl_state->curr_connp->record_length = 0;
+ // restore last good version
+ ssl_state->curr_connp->version = prev_version;
+ return SSL_DECODER_OK(input_len); // consume everything
+ }
+ } else {
+ if (unknown_record) {
+ SCLogDebug("unknown record, fatal");
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_RECORD_TYPE);
+ return SSL_DECODER_ERROR(-1);
+ }
+ }
+
+ /* record_length should never be zero */
+ if (ssl_state->curr_connp->record_length == 0) {
+ SCLogDebug("SSLv3 Record length is 0");
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_RECORD_LENGTH);
+ return SSL_DECODER_ERROR(-1);
+ }
+
+ if (!TLSVersionValid(ssl_state->curr_connp->version)) {
+ SCLogDebug("ssl_state->curr_connp->version %04x", ssl_state->curr_connp->version);
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_RECORD_VERSION);
+ return SSL_DECODER_ERROR(-1);
+ }
+
+ if (ssl_state->curr_connp->bytes_processed == SSLV3_RECORD_HDR_LEN &&
+ ssl_state->curr_connp->record_length > SSLV3_RECORD_MAX_LEN) {
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_RECORD_LENGTH);
+ return SSL_DECODER_ERROR(-1);
+ }
+ DEBUG_VALIDATE_BUG_ON(ssl_state->curr_connp->bytes_processed > SSLV3_RECORD_HDR_LEN);
+ } else {
+ ValidateRecordState(ssl_state->curr_connp);
+
+ record_len = (ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN)- ssl_state->curr_connp->bytes_processed;
+ record_len = MIN(input_len, record_len);
+ }
+ SCLogDebug("record length %u processed %u got %u",
+ ssl_state->curr_connp->record_length, ssl_state->curr_connp->bytes_processed, record_len);
+
+ /* if we don't have the full record, we return incomplete */
+ if (ssl_state->curr_connp->record_length > input_len - parsed) {
+ /* no need to use incomplete api buffering for application
+ * records that we'll not use anyway. */
+ if (ssl_state->curr_connp->content_type == SSLV3_APPLICATION_PROTOCOL) {
+ SCLogDebug("application record");
+ } else {
+ uint32_t needed = ssl_state->curr_connp->record_length;
+ SCLogDebug("record len %u input_len %u parsed %u: need %u bytes more data",
+ ssl_state->curr_connp->record_length, input_len, parsed, needed);
+ DEBUG_VALIDATE_BUG_ON(needed > SSLV3_RECORD_MAX_LEN);
+ return SSL_DECODER_INCOMPLETE(parsed, needed);
+ }
+ }
+
+ if (record_len == 0) {
+ return SSL_DECODER_OK(parsed);
+ }
+
+ AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input + parsed,
+ ssl_state->curr_connp->record_length, direction, TLS_FRAME_DATA);
+
+ switch (ssl_state->curr_connp->content_type) {
+ /* we don't need any data from these types */
+ case SSLV3_CHANGE_CIPHER_SPEC:
+ ssl_state->flags |= SSL_AL_FLAG_CHANGE_CIPHER_SPEC;
+
+ if (direction) {
+ ssl_state->flags |= SSL_AL_FLAG_SERVER_CHANGE_CIPHER_SPEC;
+ } else {
+ ssl_state->flags |= SSL_AL_FLAG_CLIENT_CHANGE_CIPHER_SPEC;
+ }
+ break;
+
+ case SSLV3_ALERT_PROTOCOL:
+ AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input + parsed,
+ ssl_state->curr_connp->record_length, direction, TLS_FRAME_ALERT_DATA);
+ break;
+
+ case SSLV3_APPLICATION_PROTOCOL:
+ /* In TLSv1.3 early data (0-RTT) could be sent before the
+ handshake is complete (rfc8446, section 2.3). We should
+ therefore not mark the handshake as done before we have
+ seen the ServerHello record. */
+ if ((ssl_state->flags & SSL_AL_FLAG_EARLY_DATA) &&
+ ((ssl_state->flags & SSL_AL_FLAG_STATE_SERVER_HELLO) == 0))
+ break;
+
+ /* if we see (encrypted) application data, then this means the
+ handshake must be done */
+ ssl_state->flags |= SSL_AL_FLAG_HANDSHAKE_DONE;
+
+ if (ssl_config.encrypt_mode != SSL_CNF_ENC_HANDLE_FULL) {
+ SCLogDebug("setting APP_LAYER_PARSER_NO_INSPECTION_PAYLOAD");
+ AppLayerParserStateSetFlag(pstate,
+ APP_LAYER_PARSER_NO_INSPECTION_PAYLOAD);
+ }
+
+ /* Encrypted data, reassembly not asked, bypass asked, let's sacrifice
+ * heartbeat lke inspection to be able to be able to bypass the flow */
+ if (ssl_config.encrypt_mode == SSL_CNF_ENC_HANDLE_BYPASS) {
+ SCLogDebug("setting APP_LAYER_PARSER_NO_REASSEMBLY");
+ AppLayerParserStateSetFlag(pstate,
+ APP_LAYER_PARSER_NO_REASSEMBLY);
+ AppLayerParserStateSetFlag(pstate,
+ APP_LAYER_PARSER_NO_INSPECTION);
+ AppLayerParserStateSetFlag(pstate,
+ APP_LAYER_PARSER_BYPASS_READY);
+ }
+ break;
+
+ case SSLV3_HANDSHAKE_PROTOCOL: {
+ if (ssl_state->flags & SSL_AL_FLAG_CHANGE_CIPHER_SPEC) {
+ /* In TLSv1.3, ChangeCipherSpec is only used for middlebox
+ compatibility (rfc8446, appendix D.4). */
+ // Client hello flags is needed to have a valid version
+ if ((ssl_state->flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) &&
+ (ssl_state->client_connp.version > TLS_VERSION_12) &&
+ ((ssl_state->flags & SSL_AL_FLAG_STATE_SERVER_HELLO) == 0)) {
+ /* do nothing */
+ } else {
+ // if we started parsing this, we must stop
+ break;
+ }
+ }
+
+ if (ssl_state->curr_connp->record_length < 4) {
+ SSLParserReset(ssl_state);
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ SCLogDebug("record len < 4 => %u", ssl_state->curr_connp->record_length);
+ return SSL_DECODER_ERROR(-1);
+ }
+
+ int retval = SSLv3ParseHandshakeProtocol(ssl_state, input + parsed,
+ record_len, direction);
+ SCLogDebug("retval %d", retval);
+ if (retval < 0 || retval > (int)record_len) {
+ DEBUG_VALIDATE_BUG_ON(retval > (int)record_len);
+ SSLSetEvent(ssl_state, TLS_DECODER_EVENT_INVALID_HANDSHAKE_MESSAGE);
+ SCLogDebug("SSLv3ParseHandshakeProtocol returned %d", retval);
+ return SSL_DECODER_ERROR(-1);
+ }
+ ValidateRecordState(ssl_state->curr_connp);
+ break;
+ }
+ case SSLV3_HEARTBEAT_PROTOCOL: {
+ AppLayerFrameNewByPointer(ssl_state->f, &stream_slice, input + parsed,
+ ssl_state->curr_connp->record_length, direction, TLS_FRAME_HB_DATA);
+ int retval = SSLv3ParseHeartbeatProtocol(ssl_state, input + parsed,
+ record_len, direction);
+ if (retval < 0) {
+ SCLogDebug("SSLv3ParseHeartbeatProtocol returned %d", retval);
+ return SSL_DECODER_ERROR(-1);
+ }
+ break;
+ }
+ default:
+ // should be unreachable now that we check after header parsing
+ DEBUG_VALIDATE_BUG_ON(1);
+ SCLogDebug("unsupported record type");
+ return SSL_DECODER_ERROR(-1);
+ }
+
+ parsed += record_len;
+ ssl_state->curr_connp->bytes_processed += record_len;
+
+ if (ssl_state->curr_connp->bytes_processed >=
+ ssl_state->curr_connp->record_length + SSLV3_RECORD_HDR_LEN) {
+ SCLogDebug("record complete, trigger RAW");
+ AppLayerParserTriggerRawStreamReassembly(
+ ssl_state->f, direction == 0 ? STREAM_TOSERVER : STREAM_TOCLIENT);
+ SSLParserReset(ssl_state);
+ ValidateRecordState(ssl_state->curr_connp);
+ return SSL_DECODER_OK(parsed);
+
+ } else {
+ /* we still don't have the entire record for the one we are
+ currently parsing */
+ ValidateRecordState(ssl_state->curr_connp);
+ return SSL_DECODER_OK(parsed);
+ }
+}
+
+/**
+ * \internal
+ * \brief SSLv2, SSLv23, SSLv3, TLSv1.1, TLSv1.2, TLSv1.3 parser.
+ *
+ * On parsing error, this should be the only function that should reset
+ * the parser state, to avoid multiple functions in the chain resetting
+ * the parser state.
+ *
+ * \param direction 0 for toserver, 1 for toclient.
+ * \param alstate Pointer to the state.
+ * \param pstate Application layer parser state for this session.
+ * \param output Pointer to the list of parsed output elements.
+ *
+ * \todo On reaching an inconsistent state, check if the input has
+ * another new record, instead of just returning after the reset
+ *
+ * \retval >=0 On success.
+ */
+static AppLayerResult SSLDecode(Flow *f, uint8_t direction, void *alstate,
+ AppLayerParserState *pstate, StreamSlice stream_slice)
+{
+ SSLState *ssl_state = (SSLState *)alstate;
+ uint32_t counter = 0;
+ ssl_state->f = f;
+ const uint8_t *input = StreamSliceGetData(&stream_slice);
+ const uint8_t *init_input = input;
+ int32_t input_len = (int32_t)StreamSliceGetDataLen(&stream_slice);
+
+ if (input == NULL &&
+ ((direction == 0 && AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS)) ||
+ (direction == 1 &&
+ AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC)))) {
+ /* flag session as finished if APP_LAYER_PARSER_EOF is set */
+ ssl_state->flags |= SSL_AL_FLAG_STATE_FINISHED;
+ SCReturnStruct(APP_LAYER_OK);
+ } else if (input == NULL || input_len == 0) {
+ SCReturnStruct(APP_LAYER_ERROR);
+ }
+
+ if (direction == 0)
+ ssl_state->curr_connp = &ssl_state->client_connp;
+ else
+ ssl_state->curr_connp = &ssl_state->server_connp;
+
+ /* If entering on a new record, reset the current flags. */
+ if (ssl_state->curr_connp->bytes_processed == 0) {
+ ssl_state->current_flags = 0;
+ }
+
+ /* if we have more than one record */
+ uint32_t max_records = MAX((input_len / SSL_RECORD_MINIMUM_LENGTH),1);
+ while (input_len > 0) {
+ if (counter > max_records) {
+ SCLogDebug("Looks like we have looped quite a bit. Reset state "
+ "and get out of here");
+ SSLParserReset(ssl_state);
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_TOO_MANY_RECORDS_IN_PACKET);
+ return APP_LAYER_ERROR;
+ }
+
+ /* ssl_state->bytes_processed is zero for a fresh record or
+ positive to indicate a record currently being parsed */
+
+ if (ssl_state->curr_connp->bytes_processed == 0) {
+ if ((input[0] & 0x80) || (input[0] & 0x40)) {
+ /* only SSLv2, has one of the top 2 bits set */
+ ssl_state->curr_connp->version = SSL_VERSION_2;
+ SCLogDebug("SSLv2 detected");
+ } else if (ssl_state->curr_connp->version == SSL_VERSION_2) {
+ ssl_state->curr_connp->version = TLS_VERSION_UNKNOWN;
+ SCLogDebug("SSL/TLS version reset");
+ }
+ }
+ SCLogDebug("record %u: bytes_processed %u, version %02X, input_len %u", counter,
+ ssl_state->curr_connp->bytes_processed, ssl_state->curr_connp->version, input_len);
+
+ if (ssl_state->curr_connp->version == SSL_VERSION_2) {
+ if (ssl_state->curr_connp->bytes_processed == 0) {
+ SCLogDebug("New SSLv2 record parsing");
+ } else {
+ SCLogDebug("Continuing parsing SSLv2 record");
+ }
+ struct SSLDecoderResult r =
+ SSLv2Decode(direction, ssl_state, pstate, input, input_len, stream_slice);
+ if (r.retval < 0 || r.retval > input_len) {
+ DEBUG_VALIDATE_BUG_ON(r.retval > input_len);
+ SCLogDebug("Error parsing SSLv2. Resetting parser "
+ "state. Let's get outta here");
+ SSLParserReset(ssl_state);
+ SSLSetEvent(ssl_state,
+ TLS_DECODER_EVENT_INVALID_SSL_RECORD);
+ return APP_LAYER_ERROR;
+ } else if (r.needed) {
+ input += r.retval;
+ SCLogDebug("returning consumed %" PRIuMAX " needed %u",
+ (uintmax_t)(input - init_input), r.needed);
+ SCReturnStruct(APP_LAYER_INCOMPLETE(input - init_input, r.needed));
+ }
+ input_len -= r.retval;
+ input += r.retval;
+ SCLogDebug("SSLv2 decoder consumed %d bytes: %u left", r.retval, input_len);
+ } else {
+ if (ssl_state->curr_connp->bytes_processed == 0) {
+ SCLogDebug("New TLS record: record_length %u",
+ ssl_state->curr_connp->record_length);
+ } else {
+ SCLogDebug("Continuing parsing TLS record: record_length %u, bytes_processed %u",
+ ssl_state->curr_connp->record_length, ssl_state->curr_connp->bytes_processed);
+ }
+ struct SSLDecoderResult r =
+ SSLv3Decode(direction, ssl_state, pstate, input, input_len, stream_slice);
+ if (r.retval < 0 || r.retval > input_len) {
+ DEBUG_VALIDATE_BUG_ON(r.retval > input_len);
+ SCLogDebug("Error parsing TLS. Resetting parser "
+ "state. Let's get outta here");
+ SSLParserReset(ssl_state);
+ return APP_LAYER_ERROR;
+ } else if (r.needed) {
+ input += r.retval;
+ SCLogDebug("returning consumed %" PRIuMAX " needed %u",
+ (uintmax_t)(input - init_input), r.needed);
+ SCReturnStruct(APP_LAYER_INCOMPLETE(input - init_input, r.needed));
+ }
+ input_len -= r.retval;
+ input += r.retval;
+ SCLogDebug("TLS decoder consumed %d bytes: %u left", r.retval, input_len);
+
+ if (ssl_state->curr_connp->bytes_processed == SSLV3_RECORD_HDR_LEN
+ && ssl_state->curr_connp->record_length == 0) {
+ SCLogDebug("TLS empty record");
+ /* empty record */
+ SSLParserReset(ssl_state);
+ }
+ }
+ counter++;
+ } /* while (input_len) */
+
+ /* mark handshake as done if we have subject and issuer */
+ if ((ssl_state->flags & SSL_AL_FLAG_NEED_CLIENT_CERT) &&
+ ssl_state->client_connp.cert0_subject && ssl_state->client_connp.cert0_issuerdn) {
+ SCLogDebug("SSL_AL_FLAG_HANDSHAKE_DONE");
+ ssl_state->flags |= SSL_AL_FLAG_HANDSHAKE_DONE;
+ } else if ((ssl_state->flags & SSL_AL_FLAG_NEED_CLIENT_CERT) == 0 &&
+ ssl_state->server_connp.cert0_subject && ssl_state->server_connp.cert0_issuerdn) {
+ SCLogDebug("SSL_AL_FLAG_HANDSHAKE_DONE");
+ ssl_state->flags |= SSL_AL_FLAG_HANDSHAKE_DONE;
+ }
+
+ /* flag session as finished if APP_LAYER_PARSER_EOF is set */
+ if (AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS) &&
+ AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC)) {
+ SCLogDebug("SSL_AL_FLAG_STATE_FINISHED");
+ ssl_state->flags |= SSL_AL_FLAG_STATE_FINISHED;
+ }
+
+ return APP_LAYER_OK;
+}
+
+static AppLayerResult SSLParseClientRecord(Flow *f, void *alstate, AppLayerParserState *pstate,
+ StreamSlice stream_slice, void *local_data)
+{
+ return SSLDecode(f, 0 /* toserver */, alstate, pstate, stream_slice);
+}
+
+static AppLayerResult SSLParseServerRecord(Flow *f, void *alstate, AppLayerParserState *pstate,
+ StreamSlice stream_slice, void *local_data)
+{
+ return SSLDecode(f, 1 /* toclient */, alstate, pstate, stream_slice);
+}
+
+/**
+ * \internal
+ * \brief Function to allocate the SSL state memory.
+ */
+static void *SSLStateAlloc(void *orig_state, AppProto proto_orig)
+{
+ SSLState *ssl_state = SCMalloc(sizeof(SSLState));
+ if (unlikely(ssl_state == NULL))
+ return NULL;
+ memset(ssl_state, 0, sizeof(SSLState));
+ ssl_state->client_connp.cert_log_flag = 0;
+ ssl_state->server_connp.cert_log_flag = 0;
+ memset(ssl_state->client_connp.random, 0, TLS_RANDOM_LEN);
+ memset(ssl_state->server_connp.random, 0, TLS_RANDOM_LEN);
+ TAILQ_INIT(&ssl_state->server_connp.certs);
+ TAILQ_INIT(&ssl_state->client_connp.certs);
+
+ return (void *)ssl_state;
+}
+
+/**
+ * \internal
+ * \brief Function to free the SSL state memory.
+ */
+static void SSLStateFree(void *p)
+{
+ SSLState *ssl_state = (SSLState *)p;
+ SSLCertsChain *item;
+
+ if (ssl_state->client_connp.cert0_subject)
+ rs_cstring_free(ssl_state->client_connp.cert0_subject);
+ if (ssl_state->client_connp.cert0_issuerdn)
+ rs_cstring_free(ssl_state->client_connp.cert0_issuerdn);
+ if (ssl_state->client_connp.cert0_serial)
+ rs_cstring_free(ssl_state->client_connp.cert0_serial);
+ if (ssl_state->client_connp.cert0_fingerprint)
+ SCFree(ssl_state->client_connp.cert0_fingerprint);
+ if (ssl_state->client_connp.sni)
+ SCFree(ssl_state->client_connp.sni);
+ if (ssl_state->client_connp.session_id)
+ SCFree(ssl_state->client_connp.session_id);
+ if (ssl_state->client_connp.hs_buffer)
+ SCFree(ssl_state->client_connp.hs_buffer);
+
+ if (ssl_state->server_connp.cert0_subject)
+ rs_cstring_free(ssl_state->server_connp.cert0_subject);
+ if (ssl_state->server_connp.cert0_issuerdn)
+ rs_cstring_free(ssl_state->server_connp.cert0_issuerdn);
+ if (ssl_state->server_connp.cert0_serial)
+ rs_cstring_free(ssl_state->server_connp.cert0_serial);
+ if (ssl_state->server_connp.cert0_fingerprint)
+ SCFree(ssl_state->server_connp.cert0_fingerprint);
+ if (ssl_state->server_connp.sni)
+ SCFree(ssl_state->server_connp.sni);
+ if (ssl_state->server_connp.session_id)
+ SCFree(ssl_state->server_connp.session_id);
+
+ if (ssl_state->client_connp.ja3_str)
+ Ja3BufferFree(&ssl_state->client_connp.ja3_str);
+ if (ssl_state->client_connp.ja3_hash)
+ SCFree(ssl_state->client_connp.ja3_hash);
+ if (ssl_state->server_connp.ja3_str)
+ Ja3BufferFree(&ssl_state->server_connp.ja3_str);
+ if (ssl_state->server_connp.ja3_hash)
+ SCFree(ssl_state->server_connp.ja3_hash);
+ if (ssl_state->server_connp.hs_buffer)
+ SCFree(ssl_state->server_connp.hs_buffer);
+
+ AppLayerDecoderEventsFreeEvents(&ssl_state->tx_data.events);
+
+ if (ssl_state->tx_data.de_state != NULL) {
+ DetectEngineStateFree(ssl_state->tx_data.de_state);
+ }
+
+ /* Free certificate chain */
+ if (ssl_state->server_connp.certs_buffer)
+ SCFree(ssl_state->server_connp.certs_buffer);
+ while ((item = TAILQ_FIRST(&ssl_state->server_connp.certs))) {
+ TAILQ_REMOVE(&ssl_state->server_connp.certs, item, next);
+ SCFree(item);
+ }
+ TAILQ_INIT(&ssl_state->server_connp.certs);
+ /* Free certificate chain */
+ if (ssl_state->client_connp.certs_buffer)
+ SCFree(ssl_state->client_connp.certs_buffer);
+ while ((item = TAILQ_FIRST(&ssl_state->client_connp.certs))) {
+ TAILQ_REMOVE(&ssl_state->client_connp.certs, item, next);
+ SCFree(item);
+ }
+ TAILQ_INIT(&ssl_state->client_connp.certs);
+
+ SCFree(ssl_state);
+
+ return;
+}
+
+static void SSLStateTransactionFree(void *state, uint64_t tx_id)
+{
+ /* do nothing */
+}
+
+static AppProto SSLProbingParser(Flow *f, uint8_t direction,
+ const uint8_t *input, uint32_t ilen, uint8_t *rdir)
+{
+ /* probably a rst/fin sending an eof */
+ if (ilen < 3)
+ return ALPROTO_UNKNOWN;
+
+ /* for now just the 3 byte header ones */
+ /* \todo Detect the 2 byte ones */
+ if ((input[0] & 0x80) && (input[2] == 0x01)) {
+ return ALPROTO_TLS;
+ }
+
+ return ALPROTO_FAILED;
+}
+
+static int SSLStateGetFrameIdByName(const char *frame_name)
+{
+ int id = SCMapEnumNameToValue(frame_name, tls_frame_table);
+ if (id < 0) {
+ return -1;
+ }
+ return id;
+}
+
+static const char *SSLStateGetFrameNameById(const uint8_t frame_id)
+{
+ const char *name = SCMapEnumValueToName(frame_id, tls_frame_table);
+ return name;
+}
+
+static int SSLStateGetEventInfo(const char *event_name,
+ int *event_id, AppLayerEventType *event_type)
+{
+ *event_id = SCMapEnumNameToValue(event_name, tls_decoder_event_table);
+ if (*event_id == -1) {
+ SCLogError("event \"%s\" not present in "
+ "ssl's enum map table.",
+ event_name);
+ /* yes this is fatal */
+ return -1;
+ }
+
+ *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
+
+ return 0;
+}
+
+static int SSLStateGetEventInfoById(int event_id, const char **event_name,
+ AppLayerEventType *event_type)
+{
+ *event_name = SCMapEnumValueToName(event_id, tls_decoder_event_table);
+ if (*event_name == NULL) {
+ SCLogError("event \"%d\" not present in "
+ "ssl's enum map table.",
+ event_id);
+ /* yes this is fatal */
+ return -1;
+ }
+
+ *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
+
+ return 0;
+}
+
+static int SSLRegisterPatternsForProtocolDetection(void)
+{
+ if (AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP, ALPROTO_TLS, "|01 00 02|", 5, 2,
+ STREAM_TOSERVER, SSLProbingParser, 0, 3) < 0) {
+ return -1;
+ }
+
+ /** SSLv3 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|01 03 00|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 00|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+
+ /** TLSv1 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|01 03 01|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 01|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+
+ /** TLSv1.1 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|01 03 02|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 02|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+
+ /** TLSv1.2 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|01 03 03|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 03|", 3, 0, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+
+ /***** toclient direction *****/
+
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|15 03 00|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 00|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|17 03 00|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+
+ /** TLSv1 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|15 03 01|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 01|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|17 03 01|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+
+ /** TLSv1.1 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|15 03 02|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 02|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|17 03 02|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+
+ /** TLSv1.2 */
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|15 03 03|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|16 03 03|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|17 03 03|", 3, 0, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+
+ /* Subsection - SSLv2 style record by client, but informing the server
+ * the max version it supports.
+ * Updated by Anoop Saldanha. Disabled it for now. We'll get back to
+ * it after some tests */
+#if 0
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|01 03 00|", 5, 2, STREAM_TOSERVER) < 0)
+ {
+ return -1;
+ }
+ if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_TLS,
+ "|00 02|", 7, 5, STREAM_TOCLIENT) < 0)
+ {
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * \brief Function to register the SSL protocol parser and other functions
+ */
+void RegisterSSLParsers(void)
+{
+ const char *proto_name = "tls";
+
+ SC_ATOMIC_INIT(ssl_config.enable_ja3);
+
+ /** SSLv2 and SSLv23*/
+ if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
+ AppLayerProtoDetectRegisterProtocol(ALPROTO_TLS, proto_name);
+
+ if (SSLRegisterPatternsForProtocolDetection() < 0)
+ return;
+
+ if (RunmodeIsUnittests()) {
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "443",
+ ALPROTO_TLS,
+ 0, 3,
+ STREAM_TOSERVER,
+ SSLProbingParser, NULL);
+ } else {
+ if (AppLayerProtoDetectPPParseConfPorts("tcp", IPPROTO_TCP,
+ proto_name, ALPROTO_TLS,
+ 0, 3,
+ SSLProbingParser, NULL) == 0) {
+ SCLogConfig("no TLS config found, "
+ "enabling TLS detection on port 443.");
+ AppLayerProtoDetectPPRegister(IPPROTO_TCP,
+ "443",
+ ALPROTO_TLS,
+ 0, 3,
+ STREAM_TOSERVER,
+ SSLProbingParser, NULL);
+ }
+ }
+ } else {
+ SCLogConfig("Protocol detection and parser disabled for %s protocol",
+ proto_name);
+ return;
+ }
+
+ if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOSERVER,
+ SSLParseClientRecord);
+
+ AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOCLIENT,
+ SSLParseServerRecord);
+
+ AppLayerParserRegisterGetFrameFuncs(
+ IPPROTO_TCP, ALPROTO_TLS, SSLStateGetFrameIdByName, SSLStateGetFrameNameById);
+ AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_TLS, SSLStateGetEventInfo);
+ AppLayerParserRegisterGetEventInfoById(IPPROTO_TCP, ALPROTO_TLS, SSLStateGetEventInfoById);
+
+ AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_TLS, SSLStateAlloc, SSLStateFree);
+
+ AppLayerParserRegisterParserAcceptableDataDirection(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOSERVER);
+
+ AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_TLS, SSLStateTransactionFree);
+
+ AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_TLS, SSLGetTx);
+ AppLayerParserRegisterTxDataFunc(IPPROTO_TCP, ALPROTO_TLS, SSLGetTxData);
+ AppLayerParserRegisterStateDataFunc(IPPROTO_TCP, ALPROTO_TLS, SSLGetStateData);
+
+ AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_TLS, SSLGetTxCnt);
+
+ AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_TLS, SSLGetAlstateProgress);
+
+ AppLayerParserRegisterStateProgressCompletionStatus(
+ ALPROTO_TLS, TLS_STATE_FINISHED, TLS_STATE_FINISHED);
+
+ ConfNode *enc_handle = ConfGetNode("app-layer.protocols.tls.encryption-handling");
+ if (enc_handle != NULL && enc_handle->val != NULL) {
+ SCLogDebug("have app-layer.protocols.tls.encryption-handling = %s", enc_handle->val);
+ if (strcmp(enc_handle->val, "full") == 0) {
+ ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_FULL;
+ } else if (strcmp(enc_handle->val, "bypass") == 0) {
+ ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_BYPASS;
+ } else if (strcmp(enc_handle->val, "default") == 0) {
+ ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_DEFAULT;
+ } else {
+ ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_DEFAULT;
+ }
+ } else {
+ /* Get the value of no reassembly option from the config file */
+ if (ConfGetNode("app-layer.protocols.tls.no-reassemble") == NULL) {
+ int value = 0;
+ if (ConfGetBool("tls.no-reassemble", &value) == 1 && value == 1)
+ ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_BYPASS;
+ } else {
+ int value = 0;
+ if (ConfGetBool("app-layer.protocols.tls.no-reassemble", &value) == 1 && value == 1)
+ ssl_config.encrypt_mode = SSL_CNF_ENC_HANDLE_BYPASS;
+ }
+ }
+ SCLogDebug("ssl_config.encrypt_mode %u", ssl_config.encrypt_mode);
+
+ /* Check if we should generate JA3 fingerprints */
+ int enable_ja3 = SSL_CONFIG_DEFAULT_JA3;
+ const char *strval = NULL;
+ if (ConfGet("app-layer.protocols.tls.ja3-fingerprints", &strval) != 1) {
+ enable_ja3 = SSL_CONFIG_DEFAULT_JA3;
+ } else if (strcmp(strval, "auto") == 0) {
+ enable_ja3 = SSL_CONFIG_DEFAULT_JA3;
+ } else if (ConfValIsFalse(strval)) {
+ enable_ja3 = 0;
+ ssl_config.disable_ja3 = true;
+ } else if (ConfValIsTrue(strval)) {
+ enable_ja3 = true;
+ }
+ SC_ATOMIC_SET(ssl_config.enable_ja3, enable_ja3);
+
+ if (g_disable_hashing) {
+ if (SC_ATOMIC_GET(ssl_config.enable_ja3)) {
+ SCLogWarning("MD5 calculation has been disabled, disabling JA3");
+ SC_ATOMIC_SET(ssl_config.enable_ja3, 0);
+ }
+ } else {
+ if (RunmodeIsUnittests()) {
+ SC_ATOMIC_SET(ssl_config.enable_ja3, 1);
+ }
+ }
+ } else {
+ SCLogConfig("Parsed disabled for %s protocol. Protocol detection"
+ "still on.", proto_name);
+ }
+
+ return;
+}
+
+/**
+ * \brief if not explicitly disabled in config, enable ja3 support
+ *
+ * Implemented using atomic to allow rule reloads to do this at
+ * runtime.
+ */
+void SSLEnableJA3(void)
+{
+ if (g_disable_hashing || ssl_config.disable_ja3) {
+ return;
+ }
+ if (SC_ATOMIC_GET(ssl_config.enable_ja3)) {
+ return;
+ }
+ SC_ATOMIC_SET(ssl_config.enable_ja3, 1);
+}
+
+bool SSLJA3IsEnabled(void)
+{
+ if (SC_ATOMIC_GET(ssl_config.enable_ja3)) {
+ return true;
+ }
+ return false;
+}