diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
commit | a0aa2307322cd47bbf416810ac0292925e03be87 (patch) | |
tree | 37076262a026c4b48c8a0e84f44ff9187556ca35 /src/app-layer-ssl.c | |
parent | Initial commit. (diff) | |
download | suricata-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.c | 3108 |
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; +} |