Have a dissector table for SSL/TLS/DTLS ALPN protocol IDs.
authorGuy Harris <guy@alum.mit.edu>
Sun, 27 May 2018 01:42:41 +0000 (18:42 -0700)
committerGuy Harris <guy@alum.mit.edu>
Sun, 27 May 2018 01:43:27 +0000 (01:43 +0000)
Have dissectors register with their protocol ID string in that table,
rather than having a table in epan/dissectors/packet-ssl-utils.c that
has to be updated for new protocols.

Have a table of protocol ID string prefixes, to handle the case of
protocols such as SPDY and HTTP2 drafts, where multiple protocol IDs are
used for different versions.

Change-Id: I363d04895a88e779fbbca7dc8e1f31aa1970a31a
Reviewed-on: https://code.wireshark.org/review/27836
Reviewed-by: Guy Harris <guy@alum.mit.edu>
epan/dissectors/packet-dtls.c
epan/dissectors/packet-http.c
epan/dissectors/packet-http2.c
epan/dissectors/packet-ssl-utils.c
epan/dissectors/packet-ssl-utils.h
epan/dissectors/packet-ssl.c
epan/dissectors/packet-stun.c
epan/dissectors/packet-turnchannel.c

index 8735f1abe14b75fa11652bb947e54871be92bc1a..b7bfdbe2a1f8b6bf4f492fe2ddce66ad8cd598cc 100644 (file)
@@ -1955,6 +1955,10 @@ proto_register_dtls(void)
 
   dtls_associations = register_dissector_table("dtls.port", "DTLS Port", proto_dtls, FT_UINT16, BASE_DEC);
 
+  ssl_common_register_dtls_alpn_dissector_table("dtls.handshake.extensions_alpn_str",
+        "DTLS Application-Layer Protocol Negotiation (ALPN) Protocol IDs",
+        proto_dtls);
+
   /* Required function calls to register the header fields and
    * subtrees used */
   proto_register_field_array(proto_dtls, hf, array_length(hf));
index 85bba25eff545e475597f7b9c91c4702583751eb..afe8c1fd8f0a07f335716c3a5f319b5bba9d73d0 100644 (file)
@@ -4098,6 +4098,12 @@ proto_reg_handoff_http(void)
        ssdp_handle = create_dissector_handle(dissect_ssdp, proto_ssdp);
        dissector_add_uint_with_preference("udp.port", UDP_PORT_SSDP, ssdp_handle);
 
+       /*
+        * SSL/TLS Application-Layer Protocol Negotiation (ALPN) protocol
+        * ID.
+        */
+       dissector_add_string("ssl.handshake.extensions_alpn_str", "http/1.1", http_ssl_handle);
+
        ntlmssp_handle = find_dissector_add_dependency("ntlmssp", proto_http);
        gssapi_handle = find_dissector_add_dependency("gssapi", proto_http);
        sstp_handle = find_dissector_add_dependency("sstp", proto_http);
index d0ffdc39b49eb08bc81978dfa6281f48081ab91b..fe3e7c3880c534b612ed472f0aab64ff61bec40a 100644 (file)
@@ -3326,6 +3326,12 @@ proto_reg_handoff_http2(void)
 
     dissector_add_for_decode_as_with_preference("tcp.port", http2_handle);
 
+    /*
+     * SSL/TLS Application-Layer Protocol Negotiation (ALPN) protocol
+     * ID.
+     */
+    dissector_add_string("ssl.handshake.extensions_alpn_str", "h2", http2_handle);
+
     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);
 
index a693a709a2e6e8c80493a1c66e70d8220072c14c..273a8a1d8b7a80a92a98f5c2bc58df6f0e7223e1 100644 (file)
@@ -1402,39 +1402,26 @@ static const bytes_string ct_logids[] = {
 };
 
 /*
- * http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
+ * Application-Layer Protocol Negotiation (ALPN) dissector tables.
  */
-/* string_string is inappropriate as it compares strings while
- * "byte strings MUST NOT be truncated" (RFC 7301) */
-typedef struct ssl_alpn_protocol {
-    const char      *proto_name;
-    gboolean         match_exact;
-    const char      *dissector_name;
-} ssl_alpn_protocol_t;
+static dissector_table_t ssl_alpn_dissector_table;
+static dissector_table_t dtls_alpn_dissector_table;
 
 /*
- * For SSL/TLS; the dissectors should handle running atop a byte-stream
- * protocol such as TCP.
+ * Special cases for prefix matching of the ALPN, if the ALPN includes
+ * a version number for a draft or protocol revision.
  */
-static const ssl_alpn_protocol_t ssl_alpn_protocols[] = {
-    { "http/1.1",           TRUE,   "http" },
+typedef struct ssl_alpn_prefix_match_protocol {
+    const char      *proto_prefix;
+    const char      *dissector_name;
+} ssl_alpn_prefix_match_protocol_t;
+
+static const ssl_alpn_prefix_match_protocol_t ssl_alpn_prefix_match_protocols[] = {
     /* SPDY moves so fast, just 1, 2 and 3 are registered with IANA but there
      * already exists 3.1 as of this writing... match the prefix. */
-    { "spdy/",              FALSE,  "spdy" },
-    { "stun.turn",          TRUE,   "turnchannel-tcp" }, /* RFC 7443 */
-    { "stun.nat-discovery", TRUE,   "stun-tcp" },        /* RFC 7443 */
+    { "spdy/",              "spdy" },
     /* draft-ietf-httpbis-http2-16 */
-    { "h2-",                FALSE,  "http2" }, /* draft versions */
-    { "h2",                 TRUE,   "http2" }, /* final version */
-};
-
-/*
- * For DTLS; the dissectors should handle running atop a datagram
- * protocol such as UDP.
- */
-static const ssl_alpn_protocol_t dtls_alpn_protocols[] = {
-    { "stun.turn",          TRUE,   "turnchannel" }, /* RFC 7443 */
-    { "stun.nat-discovery", TRUE,   "stun-udp" },    /* RFC 7443 */
+    { "h2-",                "http2" }, /* draft versions */
 };
 
 const value_string quic_transport_parameter_id[] = {
@@ -5893,9 +5880,6 @@ ssl_dissect_hnd_hello_ext_alpn(ssl_common_dissect_t *hf, tvbuff_t *tvb,
     proto_item *ti;
     guint32     next_offset, alpn_length, name_length;
     guint8     *proto_name = NULL;
-    guint32     proto_name_length = 0;
-    const ssl_alpn_protocol_t *alpn_protocols;
-    size_t      n_alpn_protocols;
 
     /* ProtocolName protocol_name_list<2..2^16-1> */
     if (!ssl_add_vector(hf, tvb, pinfo, tree, offset, offset_end, &alpn_length,
@@ -5923,9 +5907,10 @@ ssl_dissect_hnd_hello_ext_alpn(ssl_common_dissect_t *hf, tvbuff_t *tvb,
                             tvb, offset, name_length, ENC_ASCII|ENC_NA);
         /* Remember first ALPN ProtocolName entry for server. */
         if (hnd_type == SSL_HND_SERVER_HELLO || hnd_type == SSL_HND_ENCRYPTED_EXTENSIONS) {
-            proto_name_length = name_length;
+            /* '\0'-terminated string for dissector table match and prefix
+             * comparison purposes. */
             proto_name = tvb_get_string_enc(wmem_packet_scope(), tvb, offset,
-                                            proto_name_length, ENC_ASCII);
+                                            name_length, ENC_ASCII);
         }
         offset += name_length;
     }
@@ -5933,31 +5918,38 @@ ssl_dissect_hnd_hello_ext_alpn(ssl_common_dissect_t *hf, tvbuff_t *tvb,
     /* If ALPN is given in ServerHello, then ProtocolNameList MUST contain
      * exactly one "ProtocolName". */
     if (proto_name) {
-        alpn_protocols = is_dtls ? dtls_alpn_protocols : ssl_alpn_protocols;
-        n_alpn_protocols = is_dtls ? G_N_ELEMENTS(dtls_alpn_protocols) : G_N_ELEMENTS(ssl_alpn_protocols);
-        /* '\0'-terminated string for prefix/full string comparison purposes. */
-        for (size_t i = 0; i < n_alpn_protocols; i++) {
-            const ssl_alpn_protocol_t *alpn_proto = &alpn_protocols[i];
-
-            if ((alpn_proto->match_exact &&
-                        proto_name_length == strlen(alpn_proto->proto_name) &&
-                        !strcmp(proto_name, alpn_proto->proto_name)) ||
-                (!alpn_proto->match_exact && g_str_has_prefix(proto_name, alpn_proto->proto_name))) {
-
-                dissector_handle_t handle;
-                /* ProtocolName match, so set the App data dissector handle.
-                 * This may override protocols given via the UAT dialog, but
-                 * since the ALPN hint is precise, do it anyway. */
-                handle = ssl_find_appdata_dissector(alpn_proto->dissector_name);
-                ssl_debug_printf("%s: changing handle %p to %p (%s)", G_STRFUNC,
-                                 (void *)session->app_handle,
-                                 (void *)handle, alpn_proto->dissector_name);
-                /* if dissector is disabled, do not overwrite previous one */
-                if (handle)
-                    session->app_handle = handle;
-                break;
+        dissector_handle_t handle;
+
+        if (is_dtls) {
+            handle = dissector_get_string_handle(dtls_alpn_dissector_table,
+                                                 proto_name);
+        } else {
+            handle = dissector_get_string_handle(ssl_alpn_dissector_table,
+                                                 proto_name);
+            if (handle == NULL) {
+                /* Try prefix matching */
+                for (size_t i = 0; i < G_N_ELEMENTS(ssl_alpn_prefix_match_protocols); i++) {
+                    const ssl_alpn_prefix_match_protocol_t *alpn_proto = &ssl_alpn_prefix_match_protocols[i];
+
+                    /* string_string is inappropriate as it compares strings
+                     * while "byte strings MUST NOT be truncated" (RFC 7301) */
+                    if (g_str_has_prefix(proto_name, alpn_proto->proto_prefix)) {
+                        handle = find_dissector(alpn_proto->dissector_name);
+                        break;
+                    }
+                }
             }
         }
+        if (handle != NULL) {
+            /* ProtocolName match, so set the App data dissector handle.
+             * This may override protocols given via the UAT dialog, but
+             * since the ALPN hint is precise, do it anyway. */
+            ssl_debug_printf("%s: changing handle %p to %p (%s)", G_STRFUNC,
+                             (void *)session->app_handle,
+                             (void *)handle,
+                             dissector_handle_get_dissector_name(handle));
+            session->app_handle = handle;
+        }
     }
 
     return offset;
@@ -8754,6 +8746,22 @@ tls13_dissect_hnd_key_update(ssl_common_dissect_t *hf, tvbuff_t *tvb,
     proto_tree_add_item(tree, hf->hf.hs_key_update_request_update, tvb, offset, 1, ENC_NA);
 }
 
+void
+ssl_common_register_ssl_alpn_dissector_table(const char *name,
+    const char *ui_name, const int proto)
+{
+    ssl_alpn_dissector_table = register_dissector_table(name, ui_name,
+        proto, FT_STRING, FALSE);
+}
+
+void
+ssl_common_register_dtls_alpn_dissector_table(const char *name,
+    const char *ui_name, const int proto)
+{
+    dtls_alpn_dissector_table = register_dissector_table(name, ui_name,
+        proto, FT_STRING, FALSE);
+}
+
 void
 ssl_common_register_options(module_t *module, ssl_common_options_t *options)
 {
index 24daf2749ba824e90092254ca8d767aee1951b6f..2595849cf994a91d15428e067a4aec0bac701a4c 100644 (file)
@@ -1986,6 +1986,14 @@ ssl_common_dissect_t name = {   \
     }
 /* }}} */
 
+extern void
+ssl_common_register_ssl_alpn_dissector_table(const char *name,
+    const char *ui_name, const int proto);
+
+extern void
+ssl_common_register_dtls_alpn_dissector_table(const char *name,
+    const char *ui_name, const int proto);
+
 extern void
 ssl_common_register_options(module_t *module, ssl_common_options_t *options);
 
index 17140639385d9dac009d54e7d813c0b0c8336d00..ec98259155722d720932a9bfee1f7dc7395f8b3f 100644 (file)
@@ -4647,6 +4647,10 @@ proto_register_ssl(void)
     /* heuristic dissectors for any premable e.g. CredSSP before RDP */
     ssl_heur_subdissector_list = register_heur_dissector_list("ssl", proto_ssl);
 
+    ssl_common_register_ssl_alpn_dissector_table("ssl.handshake.extensions_alpn_str",
+        "SSL/TLS Application-Layer Protocol Negotiation (ALPN) Protocol IDs",
+        proto_ssl);
+
     ssl_handle = register_dissector("ssl", dissect_ssl, proto_ssl);
 
     register_init_routine(ssl_init);
index 46e5a382343ec0eac62f4ad6f833b7f2da72c7c1..6a8734cf007191c3edd21cf56f951049a4215e89 100644 (file)
@@ -1731,6 +1731,13 @@ proto_reg_handoff_stun(void)
     dissector_add_uint_with_preference("tcp.port", TCP_PORT_STUN, stun_tcp_handle);
     dissector_add_uint_with_preference("udp.port", UDP_PORT_STUN, stun_udp_handle);
 
+    /*
+     * SSL/TLS and DTLS Application-Layer Protocol Negotiation (ALPN)
+     * protocol ID.
+     */
+    dissector_add_string("ssl.handshake.extensions_alpn_str", "stun.nat-discovery", stun_tcp_handle);
+    dissector_add_string("dtls.handshake.extensions_alpn_str", "stun.nat-discovery", stun_udp_handle);
+
     heur_dissector_add("udp", dissect_stun_heur, "STUN over UDP", "stun_udp", proto_stun, HEURISTIC_ENABLE);
 
     data_handle = find_dissector("data");
index 339db978f33175f7f2979c5ef3322ae27914a1f2..e3c2f364f45b26fb6b271cdf628370db61f92eb1 100644 (file)
@@ -202,6 +202,13 @@ proto_reg_handoff_turnchannel(void)
        dissector_add_for_decode_as_with_preference("tcp.port", turnchannel_tcp_handle);
        dissector_add_for_decode_as_with_preference("udp.port", turnchannel_udp_handle);
 
+       /*
+        * SSL/TLS and DTLS Application-Layer Protocol Negotiation (ALPN)
+        * protocol ID.
+        */
+       dissector_add_string("ssl.handshake.extensions_alpn_str", "stun.turn", turnchannel_tcp_handle);
+       dissector_add_string("dtls.handshake.extensions_alpn_str", "stun.turn", turnchannel_udp_handle);
+
        /* TURN negotiation is handled through STUN2 dissector (packet-stun.c),
           so only it should be able to determine if a packet is a TURN packet */
        heur_dissector_add("stun", dissect_turnchannel_heur, "TURN Channel over STUN", "turnchannel_stun", proto_turnchannel, HEURISTIC_ENABLE);