ssl: improve interaction with heuristics subdissectors
authorPeter Wu <peter@lekensteyn.nl>
Mon, 11 Jan 2016 20:47:03 +0000 (21:47 +0100)
committerAnders Broman <a.broman58@gmail.com>
Thu, 14 Jan 2016 05:39:18 +0000 (05:39 +0000)
Do not call heuristics dissectors when the SSL application data protocol
is known (via STARTTLS or via an earlier packet in session).

When the protocol is *not* known, first try heuristics on the initial
payload. If a match is found, it can then override the protocol that
would otherwise be used due to a port number match.

The HTTP2 dissector is adjusted to take advantage of that such that
HTTP2 on non-standard ports still get detected as HTTP2. Also save
dissector registration to avoid the http2 dissector from showing up
as "(null)". Now HTTP2 is really shown as "http2" and not "http" in
the "Application Data Protocol"!

The CredSSP dissector is untested and not modified as I don't know if
the whole stream will be SSL.

Tested with fix-ssl.pcap and a http2 capture (from bug 11331) wrapped
in SSL (without ALPN).

Change-Id: I134e2d4ac22287bc0a5aeadb1e38cb4059fa108b
Reviewed-on: https://code.wireshark.org/review/13179
Reviewed-by: Anders Broman <a.broman58@gmail.com>
epan/dissectors/packet-http2.c
epan/dissectors/packet-ssl.c

index adc5fa3f4f8796020c0eed3d15a2a210271f71e4..3a9171a998f446e3eed39fe39eb2bd8079d1c626 100644 (file)
@@ -1439,6 +1439,17 @@ dissect_http2_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *da
     return (TRUE);
 }
 
+static gboolean
+dissect_http2_heur_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
+{
+    dissector_handle_t *app_handle = (dissector_handle_t *) data;
+    if (dissect_http2_heur(tvb, pinfo, tree, NULL)) {
+        *app_handle = http2_handle;
+        return TRUE;
+    }
+    return FALSE;
+}
+
 void
 proto_register_http2(void)
 {
@@ -1852,7 +1863,7 @@ proto_register_http2(void)
 
     prefs_register_obsolete_preference(http2_module, "heuristic_http2");
 
-    register_dissector("http2", dissect_http2, proto_http2);
+    http2_handle = register_dissector("http2", dissect_http2, proto_http2);
 
     http2_tap = register_tap("http2");
 }
@@ -1879,10 +1890,9 @@ proto_reg_handoff_http2(void)
 {
     data_handle = find_dissector("data");
 
-    http2_handle = create_dissector_handle(dissect_http2, proto_http2);
     dissector_add_for_decode_as("tcp.port", http2_handle);
 
-    heur_dissector_add("ssl", dissect_http2_heur, "HTTP2 over SSL", "http2_ssl", proto_http2, HEURISTIC_ENABLE);
+    heur_dissector_add("ssl", dissect_http2_heur_ssl, "HTTP2 over SSL", "http2_ssl", proto_http2, HEURISTIC_ENABLE);
     heur_dissector_add("http", dissect_http2_heur, "HTTP2 over TCP", "http2_tcp", proto_http2, HEURISTIC_ENABLE);
 
     stats_tree_register("http2", "http2", "HTTP2", 0, http2_stats_tree_packet, http2_stats_tree_init, NULL);
index 98e1e38b2a72d17c32e1421ab78b59b3cf3275ab..a0480ef79d58044680811cabdf091dd82a7d0034 100644 (file)
@@ -914,14 +914,15 @@ decrypt_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, guint32 offset,
 
 static void
 process_ssl_payload(tvbuff_t *tvb, int offset, packet_info *pinfo,
-                    proto_tree *tree, SslSession *session);
+                    proto_tree *tree, SslSession *session,
+                    dissector_handle_t app_handle_port);
 
 static void
 desegment_ssl(tvbuff_t *tvb, packet_info *pinfo, int offset,
               guint32 seq, guint32 nxtseq,
               SslSession *session,
               proto_tree *root_tree, proto_tree *tree,
-              SslFlow *flow)
+              SslFlow *flow, dissector_handle_t app_handle_port)
 {
     fragment_head *ipfd_head;
     gboolean       must_desegment;
@@ -1029,7 +1030,7 @@ again:
          * contain a continuation of a higher-level PDU.
          * Call the normal subdissector.
          */
-        process_ssl_payload(tvb, offset, pinfo, tree, session);
+        process_ssl_payload(tvb, offset, pinfo, tree, session, app_handle_port);
         called_dissector = TRUE;
 
         /* Did the subdissector ask us to desegment some more data
@@ -1083,7 +1084,7 @@ again:
             add_new_data_source(pinfo, next_tvb, "Reassembled SSL");
 
             /* call subdissector */
-            process_ssl_payload(next_tvb, 0, pinfo, tree, session);
+            process_ssl_payload(next_tvb, 0, pinfo, tree, session, app_handle_port);
             called_dissector = TRUE;
 
             /*
@@ -1339,7 +1340,8 @@ export_pdu_packet(tvbuff_t *tvb, packet_info *pinfo, guint tag, const gchar *nam
 
 static void
 process_ssl_payload(tvbuff_t *tvb, int offset, packet_info *pinfo,
-                    proto_tree *tree, SslSession *session)
+                    proto_tree *tree, SslSession *session,
+                    dissector_handle_t app_handle_port)
 {
     tvbuff_t *next_tvb;
     heur_dtbl_entry_t *hdtbl_entry;
@@ -1347,36 +1349,54 @@ process_ssl_payload(tvbuff_t *tvb, int offset, packet_info *pinfo,
 
     next_tvb = tvb_new_subset_remaining(tvb, offset);
 
-    if (session->app_handle) {
-        ssl_debug_printf("%s: found handle %p (%s)\n", G_STRFUNC,
-                         (void *)session->app_handle,
-                         dissector_handle_get_dissector_name(session->app_handle));
-
+    /* If the appdata proto is not yet known (no STARTTLS), try heuristics
+     * first, then ports-based dissectors. Port 443 is too overloaded... */
+    if (!session->app_handle) {
+        /* The heuristics dissector should set the app_handle if it wants to be
+         * called in the future. */
         if (dissector_try_heuristic(ssl_heur_subdissector_list, next_tvb,
-                                    pinfo, proto_tree_get_root(tree), &hdtbl_entry, NULL)) {
+                                    pinfo, proto_tree_get_root(tree), &hdtbl_entry,
+                                    &session->app_handle)) {
+            ssl_debug_printf("%s: found heuristics dissector %s, app_handle is %p (%s)\n",
+                             G_STRFUNC, hdtbl_entry->short_name,
+                             (void *)session->app_handle,
+                             dissector_handle_get_dissector_name(session->app_handle));
             if (have_tap_listener(exported_pdu_tap)) {
                 export_pdu_packet(next_tvb, pinfo, EXP_PDU_TAG_HEUR_PROTO_NAME, hdtbl_entry->short_name);
             }
+            return;
+        }
+        if (app_handle_port) {
+            /* Heuristics failed, just try the port-based dissector. */
+            session->app_handle = app_handle_port;
         } else {
-            if (have_tap_listener(exported_pdu_tap)) {
-                export_pdu_packet(next_tvb, pinfo, EXP_PDU_TAG_PROTO_NAME,
-                                  dissector_handle_get_dissector_name(session->app_handle));
-            }
-            saved_match_port = pinfo->match_uint;
-            if (ssl_packet_from_server(session, ssl_associations, pinfo)) {
-                pinfo->match_uint = pinfo->srcport;
-            } else {
-                pinfo->match_uint = pinfo->destport;
-            }
-            call_dissector(session->app_handle, next_tvb, pinfo, proto_tree_get_root(tree));
-            pinfo->match_uint = saved_match_port;
+            /* No heuristics, no port-based proto, unknown protocol. */
+            return;
         }
     }
+
+    ssl_debug_printf("%s: found handle %p (%s)\n", G_STRFUNC,
+                     (void *)session->app_handle,
+                     dissector_handle_get_dissector_name(session->app_handle));
+
+    if (have_tap_listener(exported_pdu_tap)) {
+        export_pdu_packet(next_tvb, pinfo, EXP_PDU_TAG_PROTO_NAME,
+                          dissector_handle_get_dissector_name(session->app_handle));
+    }
+    saved_match_port = pinfo->match_uint;
+    if (ssl_packet_from_server(session, ssl_associations, pinfo)) {
+        pinfo->match_uint = pinfo->srcport;
+    } else {
+        pinfo->match_uint = pinfo->destport;
+    }
+    call_dissector(session->app_handle, next_tvb, pinfo, proto_tree_get_root(tree));
+    pinfo->match_uint = saved_match_port;
 }
 
 static void
 dissect_ssl_payload(tvbuff_t *tvb, packet_info *pinfo, int offset,
-                    proto_tree *tree, SslSession *session)
+                    proto_tree *tree, SslSession *session,
+                    dissector_handle_t app_handle_port)
 {
     gboolean     save_fragmented;
     guint16      save_can_desegment;
@@ -1407,7 +1427,7 @@ dissect_ssl_payload(tvbuff_t *tvb, packet_info *pinfo, int offset,
         pinfo->can_desegment = 2;
         desegment_ssl(next_tvb, pinfo, 0, appl_data->seq, appl_data->nxtseq,
                       session, proto_tree_get_root(tree), tree,
-                      appl_data->flow);
+                      appl_data->flow, app_handle_port);
     } else if (session->app_handle) {
         /* No - just call the subdissector.
            Mark this as fragmented, so if somebody throws an exception,
@@ -1416,7 +1436,7 @@ dissect_ssl_payload(tvbuff_t *tvb, packet_info *pinfo, int offset,
         save_fragmented = pinfo->fragmented;
         pinfo->fragmented = TRUE;
 
-        process_ssl_payload(next_tvb, 0, pinfo, tree, session);
+        process_ssl_payload(next_tvb, 0, pinfo, tree, session, app_handle_port);
         pinfo->fragmented = save_fragmented;
     }
 
@@ -1683,6 +1703,9 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
         break;
     }
     case SSL_ID_APP_DATA:
+    {
+        dissector_handle_t app_handle;
+
         if (ssl){
             decrypt_ssl3_record(tvb, pinfo, offset,
                 record_length, content_type, ssl, TRUE);
@@ -1697,29 +1720,37 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo,
 
         /* app_handle discovery is done here instead of dissect_ssl_payload()
          * because the protocol name needs to be displayed below. */
-        if (!session->app_handle) {
+        app_handle = session->app_handle;
+        if (!app_handle) {
             /* Unknown protocol handle, ssl_starttls_ack was not called before.
-             * Try to find an appropriate dissection handle and cache it. */
-            dissector_handle_t handle;
-            handle = dissector_get_uint_handle(ssl_associations, pinfo->srcport);
-            handle = handle ? handle : dissector_get_uint_handle(ssl_associations, pinfo->destport);
-            if (handle) session->app_handle = handle;
+             * Try to find a port-based protocol and use it if there is no
+             * heuristics dissector (see process_ssl_payload). */
+            app_handle = dissector_get_uint_handle(ssl_associations, pinfo->srcport);
+            if (!app_handle) app_handle = dissector_get_uint_handle(ssl_associations, pinfo->destport);
         }
 
         proto_item_set_text(ssl_record_tree,
            "%s Record Layer: %s Protocol: %s",
             val_to_str_const(session->version, ssl_version_short_names, "SSL"),
             val_to_str_const(content_type, ssl_31_content_type, "unknown"),
-            session->app_handle
-            ? dissector_handle_get_dissector_name(session->app_handle)
+            app_handle ? dissector_handle_get_dissector_name(app_handle)
             : "Application Data");
 
         proto_tree_add_item(ssl_record_tree, hf_ssl_record_appdata, tvb,
                        offset, record_length, ENC_NA);
 
-        dissect_ssl_payload(tvb, pinfo, offset, tree, session);
+        dissect_ssl_payload(tvb, pinfo, offset, tree, session, app_handle);
+
+        /* Set app proto again in case the heuristics found a different proto. */
+        if (session->app_handle != app_handle)
+            proto_item_set_text(ssl_record_tree,
+               "%s Record Layer: %s Protocol: %s",
+                val_to_str_const(session->version, ssl_version_short_names, "SSL"),
+                val_to_str_const(content_type, ssl_31_content_type, "unknown"),
+                dissector_handle_get_dissector_name(session->app_handle));
 
         break;
+    }
     case SSL_ID_HEARTBEAT:
       {
         tvbuff_t *decrypted;