Have a dissector table for SSL/TLS/DTLS ALPN protocol IDs.

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>
This commit is contained in:
Guy Harris 2018-05-26 18:42:41 -07:00
parent 53a373693c
commit 5b30d5c767
8 changed files with 102 additions and 52 deletions

View 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));

View 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);

View 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);

View 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];
dissector_handle_t handle;
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))) {
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];
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;
/* 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)
{

View 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);

View 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);

View 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");

View 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);