Add decryption for resumed TLS sessions with a session ticket.
https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5963
svn path=/trunk/; revision=54860
{
ssl_dissect_hnd_hello_ext(&dissect_dtls_hf, tvb, tree, offset,
length -
- (offset - start_offset), TRUE);
+ (offset - start_offset), TRUE, ssl);
}
}
}
{
offset = ssl_dissect_hnd_hello_ext(&dissect_dtls_hf, tvb, tree, offset,
length -
- (offset - start_offset), FALSE);
+ (offset - start_offset), FALSE, ssl);
}
}
return offset;
{ SSL_HND_HELLO_EXT_HEARTBEAT, "Heartbeat" }, /* RFC 6520 */
{ SSL_HND_HELLO_EXT_ALPN, "Application Layer Protocol Negotiation" }, /* draft-ietf-tls-applayerprotoneg-01 */
{ SSL_HND_HELLO_EXT_STATUS_REQUEST_V2, "status_request_v2" }, /* RFC 6961 */
- { 35, "SessionTicket TLS" }, /* RFC 4507 */
+ { SSL_HND_HELLO_EXT_SESSION_TICKET, "SessionTicket TLS" }, /* RFC 4507 */
{ SSL_HND_HELLO_EXT_NPN, "next_protocol_negotiation"}, /* http://technotes.googlecode.com/git/nextprotoneg.html */
{ SSL_HND_HELLO_EXT_RENEG_INFO, "renegotiation_info" }, /* RFC 5746 */
{ 0, NULL }
ssl_session->session_id.data = ssl_session->_session_id;
ssl_session->client_random.data = ssl_session->_client_random;
ssl_session->server_random.data = ssl_session->_server_random;
+ ssl_session->session_ticket.data = ssl_session->_session_ticket;
+ ssl_session->session_ticket.data_len = 0;
ssl_session->master_secret.data_len = 48;
ssl_session->server_data_for_iv.data_len = 0;
ssl_session->server_data_for_iv.data = ssl_session->_server_data_for_iv;
return TRUE;
}
+/* store master secret into session data cache */
+void
+ssl_save_session_ticket(SslDecryptSession* ssl, GHashTable *session_hash)
+{
+ /* allocate stringinfo chunks for session id and master secret data*/
+ StringInfo* session_ticket;
+ StringInfo* master_secret;
+
+ if (ssl->session_ticket.data_len == 0) {
+ ssl_debug_printf("ssl_save_session_ticket - session ticket is empty!\n");
+ return;
+ }
+
+ session_ticket = (StringInfo *)wmem_alloc0(wmem_file_scope(), sizeof(StringInfo) + ssl->session_ticket.data_len);
+ master_secret = (StringInfo *)wmem_alloc0(wmem_file_scope(), 48 + sizeof(StringInfo));
+
+ master_secret->data = ((guchar*)master_secret+sizeof(StringInfo));
+
+ /* ssl_hash() depends on session_id->data being aligned for guint access
+ * so be careful in changing how it is allocated.
+ */
+ session_ticket->data = ((guchar*)session_ticket+sizeof(StringInfo));
+
+ ssl_data_set(session_ticket, ssl->session_ticket.data, ssl->session_ticket.data_len);
+ ssl_data_set(master_secret, ssl->master_secret.data, ssl->master_secret.data_len);
+ g_hash_table_insert(session_hash, session_ticket, master_secret);
+ ssl_print_string("ssl_save_session_ticket stored session_ticket", session_ticket);
+ ssl_print_string("ssl_save_session_ticket stored master secret", master_secret);
+}
+
+gboolean
+ssl_restore_session_ticket(SslDecryptSession* ssl, GHashTable *session_hash)
+{
+ StringInfo* ms;
+
+ if (ssl->session_ticket.data_len == 0) {
+ ssl_debug_printf("ssl_restore_session_ticket Cannot restore using an empty session ticket\n");
+ return FALSE;
+ }
+
+ ms = (StringInfo *)g_hash_table_lookup(session_hash, &ssl->session_ticket);
+
+ if (!ms) {
+ ssl_debug_printf("ssl_restore_session_ticket can't find stored session ticket\n");
+ return FALSE;
+ }
+ ssl_data_set(&ssl->master_secret, ms->data, ms->data_len);
+ ssl->state |= SSL_MASTER_SECRET;
+ ssl_debug_printf("ssl_restore_session_ticket master key retrieved\n");
+ return TRUE;
+}
+
int
ssl_is_valid_content_type(guint8 type)
{
return offset;
}
+static gint
+ssl_dissect_hnd_hello_ext_session_ticket(ssl_common_dissect_t *hf, tvbuff_t *tvb,
+ proto_tree *tree, guint32 offset, guint32 ext_len, gboolean is_client, SslDecryptSession *ssl)
+{
+ if(is_client && ssl && ext_len != 0)
+ {
+ /*save the ticket on the ssl opaque so that we can use it as key on server hello */
+ tvb_memcpy(tvb,ssl->session_ticket.data, offset, ext_len);
+ ssl->session_ticket.data_len = ext_len;
+ }
+ proto_tree_add_bytes_format(tree, hf->hf.hs_ext_data,
+ tvb, offset, ext_len, NULL,
+ "Data (%u byte%s)",
+ ext_len, plurality(ext_len, "", "s"));
+ return offset + ext_len;
+}
+
void
ssl_dissect_hnd_cert_url(ssl_common_dissect_t *hf, tvbuff_t *tvb, proto_tree *tree, guint32 offset)
{
gint
ssl_dissect_hnd_hello_ext(ssl_common_dissect_t *hf, tvbuff_t *tvb, proto_tree *tree,
- guint32 offset, guint32 left, gboolean is_client)
+ guint32 offset, guint32 left, gboolean is_client, SslDecryptSession *ssl)
{
guint16 extension_length;
guint16 ext_type;
guint16 ext_len;
proto_item *pi;
proto_tree *ext_tree;
-
+
if (left < 2)
return offset;
tvb, offset, 1, ENC_BIG_ENDIAN);
offset += ext_len;
break;
+ case SSL_HND_HELLO_EXT_SESSION_TICKET:
+ offset = ssl_dissect_hnd_hello_ext_session_ticket(hf, tvb, ext_tree, offset, ext_len, is_client, ssl);
+ break;
default:
proto_tree_add_bytes_format(ext_tree, hf->hf.hs_ext_data,
tvb, offset, ext_len, NULL,
#define SSL_HND_HELLO_EXT_HEARTBEAT 0x000f
#define SSL_HND_HELLO_EXT_ALPN 0x0010
#define SSL_HND_HELLO_EXT_STATUS_REQUEST_V2 0x0011
+#define SSL_HND_HELLO_EXT_SESSION_TICKET 0x0023
#define SSL_HND_HELLO_EXT_RENEG_INFO 0xff01
#define SSL_HND_HELLO_EXT_NPN 0x3374
#define SSL_HND_CERT_URL_TYPE_INDIVIDUAL_CERT 1
typedef struct _SslDecryptSession {
guchar _master_secret[48];
guchar _session_id[256];
+ guchar _session_ticket[1024];
guchar _client_random[32];
guchar _server_random[32];
StringInfo session_id;
+ StringInfo session_ticket;
StringInfo server_random;
StringInfo client_random;
StringInfo master_secret;
extern gboolean
ssl_restore_session(SslDecryptSession* ssl, GHashTable *session_hash);
+extern void
+ssl_save_session_ticket(SslDecryptSession* ssl, GHashTable *session_hash);
+
+extern gboolean
+ssl_restore_session_ticket(SslDecryptSession* ssl, GHashTable *session_hash);
+
extern gint
ssl_is_valid_content_type(guint8 type);
extern gint
ssl_dissect_hnd_hello_ext(ssl_common_dissect_t *hf, tvbuff_t *tvb, proto_tree *tree,
- guint32 offset, guint32 left, gboolean is_client);
+ guint32 offset, guint32 left, gboolean is_client, SslDecryptSession *ssl);
extern gint
ssl_dissect_hash_alg_list(ssl_common_dissect_t *hf, tvbuff_t *tvb, proto_tree *tree,
static void dissect_ssl3_hnd_new_ses_ticket(tvbuff_t *tvb,
proto_tree *tree,
- guint32 offset, guint32 length);
+ guint32 offset, guint32 length,
+ SslDecryptSession *ssl);
static void dissect_ssl3_hnd_cert(tvbuff_t *tvb,
proto_tree *tree, guint32 offset, packet_info *pinfo);
break;
case SSL_HND_NEWSESSION_TICKET:
- dissect_ssl3_hnd_new_ses_ticket(tvb, ssl_hand_tree, offset, length);
+ dissect_ssl3_hnd_new_ses_ticket(tvb, ssl_hand_tree, offset, length, ssl);
break;
case SSL_HND_CERTIFICATE:
ssl_debug_printf(" found master secret in keylog file\n");
}
}
+ /* if the session_ids match, then there is a chance that we need to restore a session_ticket */
+ if(ssl->session_ticket.data_len != 0)
+ {
+ ssl_restore_session_ticket(ssl, ssl_session_hash);
+ }
} else {
tvb_memcpy(tvb,ssl->session_id.data, offset+33, session_id_length);
ssl->session_id.data_len = session_id_length;
ssl_set_server(ssl, &pinfo->dst, pinfo->ptype, pinfo->destport);
ssl_find_private_key(ssl, ssl_key_hash, ssl_associations, pinfo);
}
-
if (tree || ssl)
{
/* show the client version */
proto_tree_add_item(tree, hf_ssl_handshake_client_version, tvb,
offset, 2, ENC_BIG_ENDIAN);
offset += 2;
-
/* show the fields in common with server hello */
offset += dissect_ssl3_hnd_hello_common(tvb, tree, offset, ssl, 0);
-
/* tell the user how many cipher suites there are */
cipher_suite_length = tvb_get_ntohs(tvb, offset);
- if (!tree)
- return;
+
+ /* even if there's no tree, we'll have to dissect the whole record to get to the extensions.
+ * we will continue with tree==NULL */
+
proto_tree_add_uint(tree, hf_ssl_handshake_cipher_suites_len,
tvb, offset, 2, cipher_suite_length);
offset += 2; /* skip opaque length */
-
if (cipher_suite_length > 0)
{
tvb_ensure_bytes_exist(tvb, offset, cipher_suite_length);
cipher_suite_length -= 2;
}
}
-
/* tell the user how many compression methods there are */
compression_methods_length = tvb_get_guint8(tvb, offset);
proto_tree_add_uint(tree, hf_ssl_handshake_comp_methods_len,
tvb, offset, 1, compression_methods_length);
offset += 1;
-
if (compression_methods_length > 0)
{
tvb_ensure_bytes_exist(tvb, offset, compression_methods_length);
compression_methods_length--;
}
}
-
if (length > offset - start_offset)
{
ssl_dissect_hnd_hello_ext(&dissect_ssl3_hf, tvb, tree, offset,
- length - (offset - start_offset), TRUE);
+ length - (offset - start_offset), TRUE, ssl);
}
}
}
if (length > offset - start_offset)
{
ssl_dissect_hnd_hello_ext(&dissect_ssl3_hf, tvb, tree, offset,
- length - (offset - start_offset), FALSE);
+ length - (offset - start_offset), FALSE, ssl);
}
}
}
static void
dissect_ssl3_hnd_new_ses_ticket(tvbuff_t *tvb, proto_tree *tree,
- guint32 offset, guint32 length)
+ guint32 offset, guint32 length, SslDecryptSession *ssl)
{
guint nst_len;
proto_item *ti;
proto_tree *subtree;
-
+ guint16 session_ticket_length = 0;
nst_len = tvb_get_ntohs(tvb, offset+4);
if (6 + nst_len != length) {
tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
+
+ session_ticket_length = tvb_get_ntohs(tvb, offset);
proto_tree_add_uint(subtree, hf_ssl_handshake_session_ticket_len,
tvb, offset, 2, nst_len);
+ offset += 2;
+
+ /* save the session ticket to cache */
+ if(ssl){
+ tvb_memcpy(tvb,ssl->session_ticket.data, offset, session_ticket_length);
+ ssl->session_ticket.data_len = session_ticket_length;
+ ssl_save_session_ticket(ssl, ssl_session_hash);
+ }
+
/* Content depends on implementation, so just show data! */
proto_tree_add_item(subtree, hf_ssl_handshake_session_ticket,
- tvb, offset + 2, nst_len, ENC_NA);
+ tvb, offset, nst_len, ENC_NA);
}
static void