(D)TLS: defer RSA private key lookup until it is really needed

RSA private keys can only be used for decrypting TLS sessions with a
full handshake that use the RSA key exchange. However currently the RSA
private key is always looked up even if it cannot be used (for example,
due to an (EC)DHE cipher or due to a resumed session).

Defer lookup of these private keys and make some more code conditional
on the availability of GnuTLS at compile time since future changes
switch to GnuTLS for RSA decryption.

Change-Id: I31dfd6cdfbd733818c798b1fb0e895cf5a987c5a
Reviewed-on: https://code.wireshark.org/review/30831
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Peter Wu 2018-11-29 13:01:30 +01:00 committed by Anders Broman
parent d68b7bc505
commit 7cc07bf748
4 changed files with 104 additions and 67 deletions

View File

@ -55,9 +55,11 @@
void proto_register_dtls(void);
#ifdef HAVE_LIBGNUTLS
/* DTLS User Access Table */
static ssldecrypt_assoc_t *dtlskeylist_uats = NULL;
static guint ndtlsdecrypt = 0;
#endif
/* we need to remember the top tree so that subdissectors we call are created
* at the root and not deep down inside the DTLS decode
@ -145,8 +147,12 @@ static expert_field ei_dtls_msg_len_diff_fragment = EI_INIT;
static expert_field ei_dtls_heartbeat_payload_length = EI_INIT;
static ssl_master_key_map_t dtls_master_key_map;
static GHashTable *dtls_key_hash = NULL;
static wmem_stack_t *key_list_stack = NULL;
#ifdef HAVE_LIBGNUTLS
static GHashTable *dtls_key_hash = NULL;
static wmem_stack_t *key_list_stack = NULL;
static uat_t *dtlsdecrypt_uat = NULL;
static const gchar *dtls_keys_list = NULL;
#endif
static reassembly_table dtls_reassembly_table;
static dissector_table_t dtls_associations = NULL;
static dissector_handle_t dtls_handle = NULL;
@ -155,8 +161,6 @@ static StringInfo dtls_decrypted_data = {NULL, 0};
static gint dtls_decrypted_data_avail = 0;
static FILE *dtls_keylog_file = NULL;
static uat_t *dtlsdecrypt_uat = NULL;
static const gchar *dtls_keys_list = NULL;
static ssl_common_options_t dtls_options = { NULL, NULL};
static const gchar *dtls_debug_file_name = NULL;
@ -209,14 +213,17 @@ dtls_init(void)
static void
dtls_cleanup(void)
{
#ifdef HAVE_LIBGNUTLS
if (key_list_stack != NULL) {
wmem_destroy_stack(key_list_stack);
key_list_stack = NULL;
}
#endif
ssl_common_cleanup(&dtls_master_key_map, &dtls_keylog_file,
&dtls_decrypted_data, &dtls_compressed_data);
}
#ifdef HAVE_LIBGNUTLS
/* parse dtls related preferences (private keys and ports association strings) */
static void
dtls_parse_uat(void)
@ -240,8 +247,8 @@ dtls_parse_uat(void)
}
/* parse private keys string, load available keys and put them in key hash*/
dtls_key_hash = g_hash_table_new_full(ssl_private_key_hash,
ssl_private_key_equal, g_free, rsa_private_key_free);
dtls_key_hash = g_hash_table_new_full(tls_private_key_hash,
tls_private_key_equal, g_free, tls_private_key_free);
ssl_set_debug(dtls_debug_file_name);
@ -263,14 +270,12 @@ dtls_parse_uat(void)
dissector_add_for_decode_as("udp.port", dtls_handle);
}
#if defined(HAVE_LIBGNUTLS)
static void
dtls_reset_uat(void)
{
g_hash_table_destroy(dtls_key_hash);
dtls_key_hash = NULL;
}
#endif
static void
dtls_parse_old_keys(void)
@ -301,6 +306,7 @@ dtls_parse_old_keys(void)
g_strfreev(old_keys);
}
}
#endif /* HAVE_LIBGNUTLS */
/*
* DTLS Dissection Routines
@ -1305,7 +1311,7 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
case SSL_HND_CERTIFICATE:
ssl_dissect_hnd_cert(&dissect_dtls_hf, sub_tvb, ssl_hand_tree, 0, length,
pinfo, session, ssl, dtls_key_hash, is_from_server, TRUE);
pinfo, session, ssl, is_from_server, TRUE);
break;
case SSL_HND_SERVER_KEY_EXCHG:
@ -1335,6 +1341,9 @@ dissect_dtls_handshake(tvbuff_t *tvb, packet_info *pinfo,
/* try to find master key from pre-master key */
if (!ssl_generate_pre_master_secret(ssl, length, sub_tvb, 0,
dtls_options.psk,
#ifdef HAVE_LIBGNUTLS
dtls_key_hash,
#endif
&dtls_master_key_map)) {
ssl_debug_printf("dissect_dtls_handshake can't generate pre master secret\n");
}
@ -1980,17 +1989,17 @@ proto_register_dtls(void)
"RSA keys list",
"A table of RSA keys for DTLS decryption",
dtlsdecrypt_uat);
#endif /* HAVE_LIBGNUTLS */
prefs_register_filename_preference(dtls_module, "debug_file", "DTLS debug file",
"redirect dtls debug to file name; leave empty to disable debug, "
"use \"" SSL_DEBUG_USE_STDERR "\" to redirect output to stderr\n",
&dtls_debug_file_name, TRUE);
prefs_register_string_preference(dtls_module, "keys_list", "RSA keys list (deprecated)",
"Semicolon-separated list of private RSA keys used for DTLS decryption. "
"Used by versions of Wireshark prior to 1.6",
&dtls_keys_list);
#endif /* HAVE_LIBGNUTLS */
prefs_register_filename_preference(dtls_module, "debug_file", "DTLS debug file",
"redirect dtls debug to file name; leave empty to disable debug, "
"use \"" SSL_DEBUG_USE_STDERR "\" to redirect output to stderr\n",
&dtls_debug_file_name, TRUE);
ssl_common_register_options(dtls_module, &dtls_options);
}
@ -2018,9 +2027,10 @@ proto_reg_handoff_dtls(void)
{
static gboolean initialized = FALSE;
/* add now dissector to default ports.*/
#ifdef HAVE_LIBGNUTLS
dtls_parse_uat();
dtls_parse_old_keys();
#endif
exported_pdu_tap = find_tap_id(EXPORT_PDU_TAP_NAME_LAYER_7);
if (initialized == FALSE) {

View File

@ -3044,6 +3044,9 @@ gboolean
ssl_generate_pre_master_secret(SslDecryptSession *ssl_session,
guint32 length, tvbuff_t *tvb, guint32 offset,
const gchar *ssl_psk,
#ifdef HAVE_LIBGNUTLS
GHashTable *key_hash,
#endif
const ssl_master_key_map_t *mk_map)
{
/* check for required session data */
@ -3159,10 +3162,13 @@ ssl_generate_pre_master_secret(SslDecryptSession *ssl_session,
tvb_memcpy(tvb, encrypted_pre_master.data, offset+skip, encrlen);
#ifdef HAVE_LIBGNUTLS
if (ssl_session->private_key) {
/* try to decrypt encrypted pre-master with RSA key */
if (ssl_decrypt_pre_master_secret(ssl_session,
&encrypted_pre_master, ssl_session->private_key))
/* Try to lookup an appropriate RSA private key to decrypt the Encrypted Pre-Master Secret. */
gcry_sexp_t pk = NULL;
if (ssl_session->cert_key_id) {
pk = (gcry_sexp_t)g_hash_table_lookup(key_hash, ssl_session->cert_key_id);
}
if (pk) {
if (ssl_decrypt_pre_master_secret(ssl_session, &encrypted_pre_master, pk))
return TRUE;
ssl_debug_printf("%s: can't decrypt pre-master secret\n",
@ -3592,8 +3598,8 @@ end:
#ifdef HAVE_LIBGNUTLS
/* Decrypt RSA pre-master secret using RSA private key. {{{ */
static gboolean
ssl_decrypt_pre_master_secret(SslDecryptSession*ssl_session,
StringInfo* encrypted_pre_master, gcry_sexp_t pk)
ssl_decrypt_pre_master_secret(SslDecryptSession *ssl_session,
StringInfo *encrypted_pre_master, gcry_sexp_t pk)
{
size_t i;
char *err;
@ -4254,11 +4260,11 @@ skip_mac:
#if defined(HAVE_LIBGNUTLS)
#ifdef HAVE_LIBGNUTLS
/* RSA private key file processing {{{ */
static void
ssl_find_private_key_by_pubkey(SslDecryptSession *ssl, GHashTable *key_hash,
ssl_find_private_key_by_pubkey(SslDecryptSession *ssl,
gnutls_datum_t *subjectPublicKeyInfo)
{
gnutls_pubkey_t pubkey = NULL;
@ -4293,16 +4299,21 @@ ssl_find_private_key_by_pubkey(SslDecryptSession *ssl, GHashTable *key_hash,
goto end;
}
ssl_print_data("lookup(KeyID)", key_id, key_id_len);
ssl->private_key = (gcry_sexp_t)g_hash_table_lookup(key_hash, key_id);
ssl_debug_printf("%s: lookup result: %p\n", G_STRFUNC, (void *) ssl->private_key);
if (key_id_len != sizeof(key_id)) {
ssl_debug_printf("%s: expected Key ID size %zu, got %zu\n",
G_STRFUNC, sizeof(key_id), key_id_len);
goto end;
}
ssl_print_data("Certificate.KeyID", key_id, key_id_len);
ssl->cert_key_id = (guint8 *)wmem_memdup(wmem_file_scope(), key_id, key_id_len);
end:
gnutls_pubkey_deinit(pubkey);
}
/* RSA private key file processing }}} */
#endif /* ! defined(HAVE_LIBGNUTLS) */
#endif /* HAVE_LIBGNUTLS */
/*--- Start of dissector-related code below ---*/
@ -4371,8 +4382,8 @@ static void ssl_reset_session(SslSession *session, SslDecryptSession *ssl, gbool
clear_flags |= SSL_SERVER_EXTENDED_MASTER_SECRET | SSL_NEW_SESSION_TICKET;
ssl->server_random.data_len = 0;
ssl->pre_master_secret.data_len = 0;
#if defined(HAVE_LIBGNUTLS)
ssl->private_key = NULL;
#ifdef HAVE_LIBGNUTLS
ssl->cert_key_id = NULL;
#endif
ssl->psk.data_len = 0;
}
@ -4496,15 +4507,16 @@ ssl_hash (gconstpointer v)
return hash;
}
#ifdef HAVE_LIBGNUTLS
gboolean
ssl_private_key_equal (gconstpointer v, gconstpointer v2)
tls_private_key_equal (gconstpointer v, gconstpointer v2)
{
/* key ID length (SHA-1 hash, per GNUTLS_KEYID_USE_SHA1) */
return !memcmp(v, v2, 20);
}
guint
ssl_private_key_hash (gconstpointer v)
tls_private_key_hash (gconstpointer v)
{
guint l, hash = 0;
const guint8 *cur = (const guint8 *)v;
@ -4516,6 +4528,12 @@ ssl_private_key_hash (gconstpointer v)
return hash;
}
void
tls_private_key_free(gpointer data)
{
rsa_private_key_free(data);
}
#endif
/* Functions for TLS/DTLS sessions and RSA private keys hashtables. }}} */
/* Handling of association between tls/dtls ports and clear text protocol. {{{ */
@ -4809,12 +4827,6 @@ end:
g_free(key_id);
}
/* }}} */
#else
void
ssl_parse_key_list(const ssldecrypt_assoc_t *uats _U_, GHashTable *key_hash _U_, const char* dissector_table_name _U_, dissector_handle_t main_handle _U_, gboolean tcp _U_)
{
report_failure("Can't load private key files, support is not compiled in.");
}
#endif
@ -7862,7 +7874,7 @@ void
ssl_dissect_hnd_cert(ssl_common_dissect_t *hf, tvbuff_t *tvb, proto_tree *tree,
guint32 offset, guint32 offset_end, packet_info *pinfo,
SslSession *session, SslDecryptSession *ssl _U_,
GHashTable *key_hash _U_, gboolean is_from_server, gboolean is_dtls)
gboolean is_from_server, gboolean is_dtls)
{
/* opaque ASN.1Cert<1..2^24-1>;
*
@ -7990,7 +8002,7 @@ ssl_dissect_hnd_cert(ssl_common_dissect_t *hf, tvbuff_t *tvb, proto_tree *tree,
dissect_x509af_Certificate(FALSE, tvb, offset, &asn1_ctx, subtree, hf->hf.hs_certificate);
#if defined(HAVE_LIBGNUTLS)
if (is_from_server && ssl && certificate_index == 0) {
ssl_find_private_key_by_pubkey(ssl, key_hash, &subjectPublicKeyInfo);
ssl_find_private_key_by_pubkey(ssl, &subjectPublicKeyInfo);
/* Only attempt to get the RSA modulus for the first cert. */
asn1_ctx.private_data = NULL;
}
@ -8265,7 +8277,7 @@ void
ssl_dissect_hnd_compress_certificate(ssl_common_dissect_t *hf, tvbuff_t *tvb, proto_tree *tree,
guint32 offset, guint32 offset_end, packet_info *pinfo,
SslSession *session _U_, SslDecryptSession *ssl _U_,
GHashTable *key_hash _U_, gboolean is_from_server _U_, gboolean is_dtls _U_)
gboolean is_from_server _U_, gboolean is_dtls _U_)
{
guint32 compressed_certificate_message_length;
/*

View File

@ -434,7 +434,7 @@ typedef struct _SslDecryptSession {
SslDecoder *server_new;
SslDecoder *client_new;
#if defined(HAVE_LIBGNUTLS)
gcry_sexp_t private_key;
guint8 *cert_key_id; /**< SHA-1 Key ID of public key in certificate. */
#endif
StringInfo psk;
StringInfo app_data_segment;
@ -569,6 +569,9 @@ gboolean
ssl_generate_pre_master_secret(SslDecryptSession *ssl_session,
guint32 length, tvbuff_t *tvb, guint32 offset,
const gchar *ssl_psk,
#ifdef HAVE_LIBGNUTLS
GHashTable *key_hash,
#endif
const ssl_master_key_map_t *mk_map);
/** Expand the pre_master_secret to generate all the session information
@ -607,19 +610,19 @@ tls13_cipher *
tls13_cipher_create(const char *label_prefix, int cipher_algo, int cipher_mode, int hash_algo, const StringInfo *secret, const gchar **error);
/* Common part bitween SSL and DTLS dissectors */
/* Common part between TLS and DTLS dissectors */
/* Hash Functions for RSA private keys table */
#ifdef HAVE_LIBGNUTLS
extern gboolean
ssl_private_key_equal (gconstpointer v, gconstpointer v2);
tls_private_key_equal (gconstpointer v, gconstpointer v2);
extern guint
ssl_private_key_hash (gconstpointer v);
tls_private_key_hash (gconstpointer v);
/* private key table entries have a scope 'larger' then packet capture,
* so we can't rely on wmem_file_scope function */
extern void
ssl_private_key_free(gpointer key);
tls_private_key_free(gpointer key);
#endif /* HAVE_LIBGNUTLS */
/* handling of association between tls/dtls ports and clear text protocol */
@ -657,9 +660,11 @@ extern void
ssl_load_keyfile(const gchar *ssl_keylog_filename, FILE **keylog_file,
const ssl_master_key_map_t *mk_map);
#ifdef HAVE_LIBGNUTLS
/* parse ssl related preferences (private keys and ports association strings) */
extern void
ssl_parse_key_list(const ssldecrypt_assoc_t * uats, GHashTable *key_hash, const char* dissector_table_name, dissector_handle_t main_handle, gboolean tcp);
#endif
/* store master secret into session data cache */
extern void
@ -1043,7 +1048,7 @@ extern void
ssl_dissect_hnd_cert(ssl_common_dissect_t *hf, tvbuff_t *tvb, proto_tree *tree,
guint32 offset, guint32 offset_end, packet_info *pinfo,
SslSession *session, SslDecryptSession *ssl,
GHashTable *key_hash, gboolean is_from_server, gboolean is_dtls);
gboolean is_from_server, gboolean is_dtls);
extern void
ssl_dissect_hnd_cert_req(ssl_common_dissect_t *hf, tvbuff_t *tvb, packet_info *pinfo,
@ -1099,7 +1104,7 @@ extern void
ssl_dissect_hnd_compress_certificate(ssl_common_dissect_t *hf, tvbuff_t *tvb, proto_tree *tree,
guint32 offset, guint32 offset_end, packet_info *pinfo,
SslSession *session _U_, SslDecryptSession *ssl _U_,
GHashTable *key_hash _U_, gboolean is_from_server _U_, gboolean is_dtls _U_);
gboolean is_from_server _U_, gboolean is_dtls _U_);
/* {{{ */
#define SSL_COMMON_LIST_T(name) \
ssl_common_dissect_t name = { \

View File

@ -98,8 +98,10 @@
void proto_register_tls(void);
#ifdef HAVE_LIBGNUTLS
static ssldecrypt_assoc_t *tlskeylist_uats = NULL;
static guint ntlsdecrypt = 0;
#endif
static gboolean tls_desegment = TRUE;
static gboolean tls_desegment_app_data = TRUE;
@ -239,17 +241,18 @@ static ssl_master_key_map_t ssl_master_key_map;
GHashTable *ssl_session_hash;
GHashTable *ssl_crandom_hash;
#ifdef HAVE_LIBGNUTLS
static GHashTable *ssl_key_hash = NULL;
static wmem_stack_t *key_list_stack = NULL;
static uat_t *ssldecrypt_uat = NULL;
static const gchar *ssl_keys_list = NULL;
#endif
static dissector_table_t ssl_associations = NULL;
static dissector_handle_t tls_handle = NULL;
static StringInfo ssl_compressed_data = {NULL, 0};
static StringInfo ssl_decrypted_data = {NULL, 0};
static gint ssl_decrypted_data_avail = 0;
static FILE *ssl_keylog_file = NULL;
static uat_t *ssldecrypt_uat = NULL;
static const gchar *ssl_keys_list = NULL;
static ssl_common_options_t ssl_options = { NULL, NULL};
/* List of dissectors to call for TLS data */
@ -292,10 +295,12 @@ ssl_init(void)
static void
ssl_cleanup(void)
{
#ifdef HAVE_LIBGNUTLS
if (key_list_stack != NULL) {
wmem_destroy_stack(key_list_stack);
key_list_stack = NULL;
}
#endif
ssl_common_cleanup(&ssl_master_key_map, &ssl_keylog_file,
&ssl_decrypted_data, &ssl_compressed_data);
@ -305,6 +310,7 @@ ssl_cleanup(void)
ssl_crandom_hash = NULL;
}
#ifdef HAVE_LIBGNUTLS
/* parse ssl related preferences (private keys and ports association strings) */
static void
ssl_parse_uat(void)
@ -330,8 +336,8 @@ ssl_parse_uat(void)
}
}
/* parse private keys string, load available keys and put them in key hash*/
ssl_key_hash = g_hash_table_new_full(ssl_private_key_hash,
ssl_private_key_equal, g_free, rsa_private_key_free);
ssl_key_hash = g_hash_table_new_full(tls_private_key_hash,
tls_private_key_equal, g_free, tls_private_key_free);
if (ntlsdecrypt > 0) {
@ -348,14 +354,12 @@ ssl_parse_uat(void)
ssl_debug_flush();
}
#if defined(HAVE_LIBGNUTLS)
static void
ssl_reset_uat(void)
{
g_hash_table_destroy(ssl_key_hash);
ssl_key_hash = NULL;
}
#endif
static void
ssl_parse_old_keys(void)
@ -387,6 +391,7 @@ ssl_parse_old_keys(void)
g_strfreev(old_keys);
}
}
#endif /* HAVE_LIBGNUTLS */
static gboolean
@ -2327,7 +2332,7 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
case SSL_HND_CERTIFICATE:
ssl_dissect_hnd_cert(&dissect_ssl3_hf, tvb, ssl_hand_tree,
offset, offset + length, pinfo, session, ssl, ssl_key_hash, is_from_server, FALSE);
offset, offset + length, pinfo, session, ssl, is_from_server, FALSE);
break;
case SSL_HND_SERVER_KEY_EXCHG:
@ -2358,6 +2363,9 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
/* try to find master key from pre-master key */
if (!ssl_generate_pre_master_secret(ssl, length, tvb, offset,
ssl_options.psk,
#ifdef HAVE_LIBGNUTLS
ssl_key_hash,
#endif
&ssl_master_key_map)) {
ssl_debug_printf("dissect_ssl3_handshake can't generate pre master secret\n");
}
@ -2394,7 +2402,7 @@ dissect_ssl3_handshake(tvbuff_t *tvb, packet_info *pinfo,
case SSL_HND_COMPRESSED_CERTIFICATE:
ssl_dissect_hnd_compress_certificate(&dissect_ssl3_hf, tvb, ssl_hand_tree,
offset, offset + length, pinfo, session,
ssl, ssl_key_hash, is_from_server, FALSE);
ssl, is_from_server, FALSE);
break;
case SSL_HND_ENCRYPTED_EXTS:
@ -3628,7 +3636,7 @@ tls13_exporter(packet_info *pinfo, gboolean is_early,
/* UAT */
#if defined(HAVE_LIBGNUTLS)
#ifdef HAVE_LIBGNUTLS
static void
ssldecrypt_free_cb(void *r)
{
@ -3687,7 +3695,7 @@ ssldecrypt_uat_fld_protocol_chk_cb(void* r _U_, const char* p, guint len _U_, co
*err = NULL;
return TRUE;
}
#endif
#endif /* HAVE_LIBGNUTLS */
static void
ssl_src_prompt(packet_info *pinfo, gchar *result)
@ -4125,17 +4133,17 @@ proto_register_tls(void)
"RSA keys list",
"A table of RSA keys for TLS decryption",
ssldecrypt_uat);
#endif /* HAVE_LIBGNUTLS */
prefs_register_filename_preference(ssl_module, "debug_file", "TLS debug file",
"Redirect TLS debug to the file specified. Leave empty to disable debugging "
"or use \"" SSL_DEBUG_USE_STDERR "\" to redirect output to stderr.",
&ssl_debug_file_name, TRUE);
prefs_register_string_preference(ssl_module, "keys_list", "RSA keys list (deprecated)",
"Semicolon-separated list of private RSA keys used for TLS decryption. "
"Used by versions of Wireshark prior to 1.6",
&ssl_keys_list);
#endif /* HAVE_LIBGNUTLS */
prefs_register_filename_preference(ssl_module, "debug_file", "TLS debug file",
"Redirect TLS debug to the file specified. Leave empty to disable debugging "
"or use \"" SSL_DEBUG_USE_STDERR "\" to redirect output to stderr.",
&ssl_debug_file_name, TRUE);
prefs_register_bool_preference(ssl_module,
"desegment_ssl_records",
@ -4203,9 +4211,11 @@ void
proto_reg_handoff_ssl(void)
{
#ifdef HAVE_LIBGNUTLS
/* parse key list */
ssl_parse_uat();
ssl_parse_old_keys();
#endif
/*
* XXX the port preferences should probably be removed in favor of Decode