|
|
|
|
/* Copyright (C) 2007-2024 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"
|
|
|
|
|
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
#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)
|
mpm: run engines as few times as possible
In various scenarios buffers would be checked my MPM more than
once. This was because the buffers would be inspected for a
certain progress value or higher.
For example, for each packet in a file upload, the engine would
not just rerun the 'http client body' MPM on the new data, it
would also rerun the method, uri, headers, cookie, etc MPMs.
This was obviously inefficent, so this patch changes the logic.
The patch only runs the MPM engines when the progress is exactly
the intended progress. If the progress is beyond the desired
value, it is run once. A tracker is added to the app layer API,
where the completed MPMs are tracked.
Implemented for HTTP, TLS and SSH.
10 years ago
|
|
|
{
|
|
|
|
|
SSLState *ssl_state = (SSLState *)vtx;
|
|
|
|
|
return &ssl_state->tx_data;
|
mpm: run engines as few times as possible
In various scenarios buffers would be checked my MPM more than
once. This was because the buffers would be inspected for a
certain progress value or higher.
For example, for each packet in a file upload, the engine would
not just rerun the 'http client body' MPM on the new data, it
would also rerun the method, uri, headers, cookie, etc MPMs.
This was obviously inefficent, so this patch changes the logic.
The patch only runs the MPM engines when the progress is exactly
the intended progress. If the progress is beyond the desired
value, it is run once. A tracker is added to the app layer API,
where the completed MPMs are tracked.
Implemented for HTTP, TLS and SSH.
10 years ago
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 preferred) 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 = SCCalloc(1, sizeof(SSLState));
|
|
|
|
|
if (unlikely(ssl_state == NULL))
|
|
|
|
|
return NULL;
|
|
|
|
|
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 */
|
|
|
|
|
}
|
|
|
|
|
|
proto-detect: improve midstream support
When Suricata picks up a flow it assumes the first packet is
toserver. In a perfect world without packet loss and where all
sessions neatly start after Suricata itself started, this would be
true. However, in reality we have to account for packet loss and
Suricata starting to get packets for flows already active be for
Suricata is (re)started.
The protocol records on the wire would often be able to tell us more
though. For example in SMB1 and SMB2 records there is a flag that
indicates whether the record is a request or a response. This patch
is enabling the procotol detection engine to utilize this information
to 'reverse' the flow.
There are three ways in which this is supported in this patch:
1. patterns for detection are registered per direction. If the proto
was not recognized in the traffic direction, and midstream is
enabled, the pattern set for the opposing direction is also
evaluated. If that matches, the flow is considered to be in the
wrong direction and is reversed.
2. probing parsers now have a way to feed back their understanding
of the flow direction. They are now passed the direction as
Suricata sees the traffic when calling the probing parsers. The
parser can then see if its own observation matches that, and
pass back it's own view to the caller.
3. a new pattern + probing parser set up: probing parsers can now
be registered with a pattern, so that when the pattern matches
the probing parser is called as well. The probing parser can
then provide the protocol detection engine with the direction
of the traffic.
The process of reversing takes a multi step approach as well:
a. reverse the current packets direction
b. reverse most of the flows direction sensitive flags
c. tag the flow as 'reversed'. This is because the 5 tuple is
*not* reversed, since it is immutable after the flows creation.
Most of the currently registered parsers benefit already:
- HTTP/SMTP/FTP/TLS patterns are registered per direction already
so they will benefit from the pattern midstream logic in (1)
above.
- the Rust based SMB parser uses a mix of pattern + probing parser
as described in (3) above.
- the NFS detection is purely done by probing parser and is updated
to consider the direction in that parser.
Other protocols, such as DNS, are still to do.
Ticket: #2572
8 years ago
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
static int SSLRegisterPatternsForProtocolDetection(void)
|
|
|
|
|
{
|
|
|
|
|
if (AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP, ALPROTO_TLS, "|01 00 02|", 5, 2,
|
|
|
|
|
STREAM_TOSERVER, SSLProbingParser, 0, 3) < 0) {
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
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;
|
|
|
|
|
}
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
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;
|
|
|
|
|
}
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
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;
|
|
|
|
|
}
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
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;
|
|
|
|
|
}
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
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*/
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
|
|
|
|
|
AppLayerProtoDetectRegisterProtocol(ALPROTO_TLS, proto_name);
|
|
|
|
|
|
|
|
|
|
if (SSLRegisterPatternsForProtocolDetection() < 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (RunmodeIsUnittests()) {
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
|
|
|
|
|
AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOSERVER,
|
|
|
|
|
SSLParseClientRecord);
|
|
|
|
|
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_TLS, STREAM_TOCLIENT,
|
|
|
|
|
SSLParseServerRecord);
|
|
|
|
|
|
|
|
|
|
AppLayerParserRegisterGetFrameFuncs(
|
|
|
|
|
IPPROTO_TCP, ALPROTO_TLS, SSLStateGetFrameIdByName, SSLStateGetFrameNameById);
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
AppLayerParserRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_TLS, SSLStateGetEventInfo);
|
|
|
|
|
AppLayerParserRegisterGetEventInfoById(IPPROTO_TCP, ALPROTO_TLS, SSLStateGetEventInfoById);
|
|
|
|
|
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_TLS, SSLStateAlloc, SSLStateFree);
|
|
|
|
|
|
App layer API rewritten. The main files in question are:
app-layer.[ch], app-layer-detect-proto.[ch] and app-layer-parser.[ch].
Things addressed in this commit:
- Brings out a proper separation between protocol detection phase and the
parser phase.
- The dns app layer now is registered such that we don't use "dnstcp" and
"dnsudp" in the rules. A user who previously wrote a rule like this -
"alert dnstcp....." or
"alert dnsudp....."
would now have to use,
alert dns (ipproto:tcp;) or
alert udp (app-layer-protocol:dns;) or
alert ip (ipproto:udp; app-layer-protocol:dns;)
The same rules extend to other another such protocol, dcerpc.
- The app layer parser api now takes in the ipproto while registering
callbacks.
- The app inspection/detection engine also takes an ipproto.
- All app layer parser functions now take direction as STREAM_TOSERVER or
STREAM_TOCLIENT, as opposed to 0 or 1, which was taken by some of the
functions.
- FlowInitialize() and FlowRecycle() now resets proto to 0. This is
needed by unittests, which would try to clean the flow, and that would
call the api, AppLayerParserCleanupParserState(), which would try to
clean the app state, but the app layer now needs an ipproto to figure
out which api to internally call to clean the state, and if the ipproto
is 0, it would return without trying to clean the state.
- A lot of unittests are now updated where if they are using a flow and
they need to use the app layer, we would set a flow ipproto.
- The "app-layer" section in the yaml conf has also been updated as well.
13 years ago
|
|
|
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("Parser 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;
|
|
|
|
|
}
|