diff options
author | João Valverde <joao.valverde@tecnico.ulisboa.pt> | 2016-01-28 02:08:06 +0000 |
---|---|---|
committer | João Valverde <j@v6e.pt> | 2016-05-01 23:32:41 +0000 |
commit | f69b3b1f0e6aa2edc3fe51e9e0fd3a28ba2f4306 (patch) | |
tree | 55de7a45c4a2e0d774649520264c8c90427e1586 | |
parent | 3270dfac43da861c714df76513456b46765ff47f (diff) |
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>
-rw-r--r-- | epan/dissectors/packet-imap.c | 66 | ||||
-rw-r--r-- | epan/dissectors/packet-ssl-utils.c | 24 | ||||
-rw-r--r-- | epan/dissectors/packet-ssl-utils.h | 12 |
3 files changed, 97 insertions, 5 deletions
diff --git a/epan/dissectors/packet-imap.c b/epan/dissectors/packet-imap.c index b9cde04dca..8379bc5618 100644 --- a/epan/dissectors/packet-imap.c +++ b/epan/dissectors/packet-imap.c @@ -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 diff --git a/epan/dissectors/packet-ssl-utils.c b/epan/dissectors/packet-ssl-utils.c index 8801e9d2ed..2ed8f89a60 100644 --- a/epan/dissectors/packet-ssl-utils.c +++ b/epan/dissectors/packet-ssl-utils.c @@ -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) diff --git a/epan/dissectors/packet-ssl-utils.h b/epan/dissectors/packet-ssl-utils.h index 272dcb3f8b..f010f22c16 100644 --- a/epan/dissectors/packet-ssl-utils.h +++ b/epan/dissectors/packet-ssl-utils.h @@ -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 |