IMAP: Add heuristic check for TLS

If the IMAP TCP stream doesn't include the STARTTLS command/response
the IMAP dissector will try to dissect TLS ciphertext as IMAP protocol
plaintext.

Add heuristic check for SSLv3/TLS and if the heuristic matches register
dissect_ssl() as the dissector for that IMAP session.

Change-Id: If84eca22315193a306e93e66c608de6634e6cd85
Reviewed-on: https://code.wireshark.org/review/13570
Petri-Dish: João Valverde <j@v6e.pt>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: João Valverde <j@v6e.pt>
This commit is contained in:
João Valverde 2016-01-28 02:08:06 +00:00 committed by João Valverde
parent 3270dfac43
commit f69b3b1f0e
3 changed files with 97 additions and 5 deletions

View File

@ -52,14 +52,38 @@ static gint ett_imap_reqresp = -1;
static dissector_handle_t imap_handle;
static dissector_handle_t ssl_handle;
static gboolean imap_ssl_heuristic = TRUE;
#define TCP_PORT_IMAP 143
#define TCP_PORT_SSL_IMAP 993
#define MAX_BUFFER 1024
#define IMAP_HEUR_LEN 5
typedef struct imap_state {
gboolean ssl_requested;
gint ssl_heur_tries_left;
} imap_state_t;
/* Heuristic to detect plaintext or TLS ciphertext IMAP */
static gboolean
check_imap_heur(tvbuff_t *tvb)
{
const gchar *s;
gint i;
if (!tvb_bytes_exist(tvb, 0, IMAP_HEUR_LEN)) {
return TRUE;
}
s = (const gchar *)tvb_get_ptr(tvb, 0, IMAP_HEUR_LEN);
for (i = 0; i < IMAP_HEUR_LEN; i++) {
if (!g_ascii_isprint(s[i])) {
return FALSE;
}
}
return TRUE;
}
static int
dissect_imap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
@ -92,9 +116,40 @@ dissect_imap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_
if (!session_state) {
session_state = wmem_new0(wmem_file_scope(), imap_state_t);
session_state->ssl_requested = FALSE;
if (imap_ssl_heuristic)
session_state->ssl_heur_tries_left = 2;
else
session_state->ssl_heur_tries_left = -1; /* Disabled */
conversation_add_proto_data(conversation, proto_imap, session_state);
}
if (imap_ssl_heuristic && session_state->ssl_heur_tries_left < 0) {
/* Preference changed to enabled */
session_state->ssl_heur_tries_left = 2;
}
else if (!imap_ssl_heuristic && session_state->ssl_heur_tries_left >= 0) {
/* Preference changed to disabled */
session_state->ssl_heur_tries_left = -1;
}
/*
* It is possible the IMAP session is already running over TLS and the
* STARTTLS request/response happened before the capture began. Don't assume
* we have plaintext without performing some heuristic checks first.
* We have three cases:
* 1. capture includes STARTTLS command: no need for heuristics
* 2. capture starts with STARTTLS OK response: next frame will be TLS (need to retry heuristic)
* 3. capture start after STARTTLS negotiation: current frame is TLS
*/
if (session_state->ssl_heur_tries_left > 0) {
session_state->ssl_heur_tries_left--;
if (!check_imap_heur(tvb)) {
ssl_starttls_post_ack(ssl_handle, pinfo, imap_handle);
session_state->ssl_heur_tries_left = 0;
return call_dissector(ssl_handle, tvb, pinfo, tree);
}
}
tokenbuf = (guchar *)wmem_alloc0(wmem_packet_scope(), MAX_BUFFER);
command_token = (guchar *)wmem_alloc0(wmem_packet_scope(), MAX_BUFFER);
commandlen = 0;
@ -260,6 +315,9 @@ dissect_imap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_
if (!is_request && strncmp(tokenbuf, "ok", tokenlen) == 0) {
/* STARTTLS accepted, next reply will be TLS. */
ssl_starttls_ack(ssl_handle, pinfo, imap_handle);
if (session_state->ssl_heur_tries_left > 0) {
session_state->ssl_heur_tries_left = 0;
}
}
session_state->ssl_requested = FALSE;
}
@ -348,12 +406,20 @@ proto_register_imap(void)
&ett_imap_reqresp,
};
module_t *imap_module;
proto_imap = proto_register_protocol("Internet Message Access Protocol", "IMAP", "imap");
imap_handle = register_dissector("imap", dissect_imap, proto_imap);
proto_register_field_array(proto_imap, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
imap_module = prefs_register_protocol(proto_imap, NULL);
prefs_register_bool_preference(imap_module, "ssl_heuristic",
"Use heuristic detection for TLS",
"Whether to use heuristics for post-STARTTLS detection of encrypted IMAP conversations",
&imap_ssl_heuristic);
}
void

View File

@ -4100,10 +4100,9 @@ ssl_get_session(conversation_t *conversation, dissector_handle_t ssl_handle)
return ssl_session;
}
/* ssl_starttls_ack: mark future frames as encrypted. {{{ */
guint32
ssl_starttls_ack(dissector_handle_t ssl_handle, packet_info *pinfo,
dissector_handle_t app_handle)
static guint32
ssl_starttls(dissector_handle_t ssl_handle, packet_info *pinfo,
dissector_handle_t app_handle, guint32 last_nontls_frame)
{
conversation_t *conversation;
SslSession *session;
@ -4135,10 +4134,25 @@ ssl_starttls_ack(dissector_handle_t ssl_handle, packet_info *pinfo,
/* The SSL dissector should be called first for this conversation. */
conversation_set_dissector(conversation, ssl_handle);
/* SSL starts after this frame. */
session->last_nontls_frame = pinfo->num;
session->last_nontls_frame = last_nontls_frame;
return 0;
} /* }}} */
/* ssl_starttls_ack: mark future frames as encrypted. {{{ */
guint32
ssl_starttls_ack(dissector_handle_t ssl_handle, packet_info *pinfo,
dissector_handle_t app_handle)
{
return ssl_starttls(ssl_handle, pinfo, app_handle, pinfo->num);
}
guint32
ssl_starttls_post_ack(dissector_handle_t ssl_handle, packet_info *pinfo,
dissector_handle_t app_handle)
{
return ssl_starttls(ssl_handle, pinfo, app_handle, pinfo->num - 1);
}
/* Functions for TLS/DTLS sessions and RSA private keys hashtables. {{{ */
static gint
ssl_equal (gconstpointer v, gconstpointer v2)

View File

@ -465,6 +465,18 @@ extern guint32
ssl_starttls_ack(dissector_handle_t ssl_handle, packet_info *pinfo,
dissector_handle_t app_handle);
/** Marks this packet as belonging to an SSL conversation started with STARTTLS.
* @param ssl_handle The dissector handle for SSL or DTLS.
* @param pinfo Packet Info.
* @param app_handle Dissector handle for the protocol inside the decrypted
* Application Data record.
* @return 0 for the first STARTTLS acknowledgement (success) or if ssl_handle
* is NULL. >0 if STARTTLS was started before.
*/
extern guint32
ssl_starttls_post_ack(dissector_handle_t ssl_handle, packet_info *pinfo,
dissector_handle_t app_handle);
/** set the data and len for the stringInfo buffer. buf should be big enough to
* contain the provided data
@param buf the buffer to update