From 6c0a2875decfc0e9bbdeb2773012496f2e120e14 Mon Sep 17 00:00:00 2001 From: Michal Ruprich Date: Mon, 7 Jul 2025 14:54:10 +0200 Subject: [PATCH] Resolves: RHEL-55554 - NFS packets decrypted via TLS are displayed as RPC --- wireshark-0019-nfs-over-tls.patch | 1074 +++++++++++++++++++++++++++++ wireshark.spec | 6 +- 2 files changed, 1079 insertions(+), 1 deletion(-) create mode 100644 wireshark-0019-nfs-over-tls.patch diff --git a/wireshark-0019-nfs-over-tls.patch b/wireshark-0019-nfs-over-tls.patch new file mode 100644 index 0000000..80cb1d4 --- /dev/null +++ b/wireshark-0019-nfs-over-tls.patch @@ -0,0 +1,1074 @@ +diff --git a/epan/dissectors/packet-rpc.c b/epan/dissectors/packet-rpc.c +index 79ec7bd..f452fcb 100644 +--- a/epan/dissectors/packet-rpc.c ++++ b/epan/dissectors/packet-rpc.c +@@ -29,6 +29,7 @@ + + #include "packet-rpc.h" + #include "packet-tcp.h" ++#include "packet-tls.h" + #include "packet-nfs.h" + #include "packet-dcerpc.h" + #include "packet-gssapi.h" +@@ -59,6 +60,10 @@ void proto_reg_handoff_rpc(void); + + #define RPC_TCP_PORT 111 + ++#define RPC_UDP 0 ++#define RPC_TCP 1 ++#define RPC_TLS 2 ++ + /* desegmentation of RPC over TCP */ + static gboolean rpc_desegment = TRUE; + +@@ -98,6 +103,7 @@ const value_string rpc_auth_flavor[] = { + { AUTH_DES, "AUTH_DES" }, + { AUTH_RSA, "AUTH_RSA/Gluster" }, + { RPCSEC_GSS, "RPCSEC_GSS" }, ++ { AUTH_TLS, "AUTH_TLS" }, + { AUTH_GSSAPI, "AUTH_GSSAPI" }, + { RPCSEC_GSS_KRB5, "RPCSEC_GSS_KRB5" }, + { RPCSEC_GSS_KRB5I, "RPCSEC_GSS_KRB5I" }, +@@ -271,11 +277,13 @@ static gint ett_gss_context = -1; + static gint ett_gss_wrap = -1; + + static expert_field ei_rpc_cannot_dissect = EI_INIT; ++static expert_field ei_rpc_segment_needed = EI_INIT; + + static dissector_handle_t rpc_tcp_handle; + static dissector_handle_t rpc_handle; + static dissector_handle_t gssapi_handle; + static dissector_handle_t data_handle; ++static dissector_handle_t rpc_tls_handle; + + static dissector_table_t subdissector_call_table; + static dissector_table_t subdissector_reply_table; +@@ -305,7 +313,7 @@ static const fragment_items rpc_frag_items = { + GHashTable *rpc_progs = NULL; + + typedef gboolean (*rec_dissector_t)(tvbuff_t *, packet_info *, proto_tree *, +- tvbuff_t *, fragment_head *, gboolean, guint32, gboolean); ++ tvbuff_t *, fragment_head *, int, guint32, gboolean, gboolean); + + static void show_rpc_fraginfo(tvbuff_t *tvb, tvbuff_t *frag_tvb, proto_tree *tree, + guint32 rpc_rm, fragment_head *ipfd_head, packet_info *pinfo); +@@ -2198,8 +2206,8 @@ looks_like_rpc_reply(tvbuff_t *tvb, packet_info *pinfo, int offset) + + static gboolean + dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, +- tvbuff_t *frag_tvb, fragment_head *ipfd_head, gboolean is_tcp, +- guint32 rpc_rm, gboolean first_pdu) ++ tvbuff_t *frag_tvb, fragment_head *ipfd_head, int tp_type, ++ guint32 rpc_rm, gboolean first_pdu, gboolean can_defragment) + { + guint32 msg_type; + rpc_call_info_value *rpc_call; +@@ -2211,6 +2219,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + unsigned int prog = 0; + unsigned int vers = 0; + unsigned int proc = 0; ++ unsigned int cred_flavor = 0; + flavor_t flavor = FLAVOR_UNKNOWN; + unsigned int gss_proc = 0; + unsigned int gss_svc = 0; +@@ -2237,7 +2246,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + + proto_item *pitem = NULL; + proto_tree *ptree = NULL; +- int offset = (is_tcp && tvb == frag_tvb) ? 4 : 0; ++ int offset = (tp_type != RPC_UDP && tvb == frag_tvb) ? 4 : 0; + + rpc_proc_info_key key; + conversation_t* conversation; +@@ -2285,7 +2294,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + return FALSE; + } + +- if (is_tcp) { ++ if (tp_type != RPC_UDP) { + /* + * This is RPC-over-TCP; check if this is the last + * fragment. +@@ -2298,7 +2307,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + * the beginning of an RPC message, + * and let them do fragment reassembly. + */ +- if (rpc_defragment) ++ if (can_defragment) + return TRUE; + } + } +@@ -2309,7 +2318,7 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + ENC_NA); + rpc_tree = proto_item_add_subtree(rpc_item, ett_rpc); + +- if (is_tcp) { ++ if (tp_type != RPC_UDP) { + show_rpc_fraginfo(tvb, frag_tvb, rpc_tree, rpc_rm, + ipfd_head, pinfo); + } +@@ -2378,7 +2387,8 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + + /* Check for RPCSEC_GSS and AUTH_GSSAPI */ + if (tvb_bytes_exist(tvb, offset+16, 4)) { +- switch (tvb_get_ntohl(tvb, offset+16)) { ++ cred_flavor = tvb_get_ntohl(tvb, offset+16); ++ switch (cred_flavor) { + + case RPCSEC_GSS: + /* +@@ -2467,8 +2477,10 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + + /* Make the dissector for this conversation the non-heuristic + RPC dissector. */ +- conversation_set_dissector(conversation, +- (pinfo->ptype == PT_TCP) ? rpc_tcp_handle : rpc_handle); ++ if (tp_type != RPC_TLS && cred_flavor != AUTH_TLS) { ++ conversation_set_dissector(conversation, ++ (pinfo->ptype == PT_TCP) ? rpc_tcp_handle : rpc_handle); ++ } + + /* look up the request */ + rpc_call = (rpc_call_info_value *)wmem_tree_lookup32(rpc_conv_info->xids, xid); +@@ -3033,15 +3045,15 @@ dissect_rpc_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + static gboolean + dissect_rpc_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) + { +- return dissect_rpc_message(tvb, pinfo, tree, NULL, NULL, FALSE, 0, +- TRUE); ++ return dissect_rpc_message(tvb, pinfo, tree, NULL, NULL, RPC_UDP, 0, ++ TRUE, FALSE); + } + + static int + dissect_rpc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) + { +- if (!dissect_rpc_message(tvb, pinfo, tree, NULL, NULL, FALSE, 0, +- TRUE)) { ++ if (!dissect_rpc_message(tvb, pinfo, tree, NULL, NULL, RPC_UDP, 0, ++ TRUE, FALSE)) { + if (tvb_reported_length(tvb) != 0) + dissect_rpc_continuation(tvb, pinfo, tree); + } +@@ -3172,7 +3184,8 @@ show_rpc_fraginfo(tvbuff_t *tvb, tvbuff_t *frag_tvb, proto_tree *tree, + static gboolean + call_message_dissector(tvbuff_t *tvb, tvbuff_t *rec_tvb, packet_info *pinfo, + proto_tree *tree, tvbuff_t *frag_tvb, rec_dissector_t dissector, +- fragment_head *ipfd_head, guint32 rpc_rm, gboolean first_pdu) ++ fragment_head *ipfd_head, int tp_type, guint32 rpc_rm, ++ gboolean first_pdu, gboolean can_defragment) + { + const char *saved_proto; + volatile gboolean rpc_succeeded; +@@ -3181,7 +3194,8 @@ call_message_dissector(tvbuff_t *tvb, tvbuff_t *rec_tvb, packet_info *pinfo, + rpc_succeeded = FALSE; + TRY { + rpc_succeeded = (*dissector)(rec_tvb, pinfo, tree, +- frag_tvb, ipfd_head, TRUE, rpc_rm, first_pdu); ++ frag_tvb, ipfd_head, tp_type, rpc_rm, first_pdu, ++ can_defragment); + } + CATCH_NONFATAL_ERRORS { + /* +@@ -3209,28 +3223,30 @@ call_message_dissector(tvbuff_t *tvb, tvbuff_t *rec_tvb, packet_info *pinfo, + } + + static int +-dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo, +- proto_tree *tree, rec_dissector_t dissector, gboolean is_heur, +- int proto, int ett, gboolean first_pdu, struct tcpinfo *tcpinfo) ++dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, ++ rec_dissector_t dissector, gboolean is_heur, int proto, int ett, ++ gboolean first_pdu, struct tcpinfo *tcpinfo, struct tlsinfo *tlsinfo) + { + guint32 seq; + guint32 rpc_rm; + guint32 len; +- gint32 seglen; ++ int tp_type; + gint tvb_len, tvb_reported_len; + tvbuff_t *frag_tvb; + conversation_t *conversation = NULL; + gboolean rpc_succeeded; + gboolean save_fragmented; ++ gboolean can_defragment; + rpc_fragment_key old_rfk, *rfk, *new_rfk; + fragment_head *ipfd_head; + tvbuff_t *rec_tvb; + +- if (pinfo == NULL || tcpinfo == NULL) { ++ if (pinfo == NULL || (tcpinfo == NULL && tlsinfo == NULL)) { + return 0; + } + +- seq = tcpinfo->seq + offset; ++ seq = (tcpinfo != NULL ? tcpinfo->seq : tlsinfo->seq) + offset; ++ tp_type = (tcpinfo != NULL ? RPC_TCP : RPC_TLS); + + /* + * Get the record mark. +@@ -3258,94 +3274,93 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo, + if (len > max_rpc_tcp_pdu_size) + return 0; /* pretend it's not valid */ + +- if (rpc_desegment) { +- seglen = tvb_reported_length_remaining(tvb, offset + 4); ++ can_defragment = rpc_defragment; ++ len += 4; /* include record mark */ ++ tvb_reported_len = tvb_reported_length_remaining(tvb, offset); + +- if ((gint)len > seglen && pinfo->can_desegment) { +- /* +- * This frame doesn't have all of the +- * data for this message, but we can do +- * reassembly on it. +- * +- * If this is a heuristic dissector, check +- * whether it looks like the beginning of +- * an RPC call or reply. If not, then, just +- * return 0 - we don't want to try to get +- * more data, as that's too likely to cause +- * us to misidentify this as valid. Otherwise, +- * mark this conversation as being for RPC and +- * try to get more data. +- * +- * If this isn't a heuristic dissector, +- * we've already identified this conversation +- * as containing data for this protocol, as we +- * saw valid data in previous frames. Try to +- * get more data. +- */ +- if (is_heur) { +- guint32 msg_type; ++ if ((gint)len > tvb_reported_len) { ++ /* This frame doesn't have all the data for this message. ++ * ++ * If this is a heuristic dissector, check whether it looks ++ * like the beginning of an RPC call or reply. ++ */ ++ if (is_heur) { ++ guint32 msg_type; + +- if (!tvb_bytes_exist(tvb, offset + 4, 8)) { +- /* +- * Captured data in packet isn't +- * enough to let us tell. +- */ +- return 0; +- } ++ if (!tvb_bytes_exist(tvb, offset + 4, 8)) { ++ /* ++ * Captured data in packet isn't ++ * enough to let us tell. ++ */ ++ return 0; ++ } + +- /* both directions need at least this */ +- msg_type = tvb_get_ntohl(tvb, offset + 4 + 4); ++ /* both directions need at least this */ ++ msg_type = tvb_get_ntohl(tvb, offset + 4 + 4); + +- switch (msg_type) { ++ switch (msg_type) { + +- case RPC_CALL: +- /* Check for RPC call. */ +- if (looks_like_rpc_call(tvb, +- offset + 4) == NULL) { +- /* Doesn't look like a call. */ +- return 0; +- } +- break; +- +- case RPC_REPLY: +- /* Check for RPC reply. */ +- if (looks_like_rpc_reply(tvb, pinfo, +- offset + 4) == NULL) { +- /* Doesn't look like a reply. */ +- return 0; +- } +- break; ++ case RPC_CALL: ++ /* Check for RPC call. */ ++ if (looks_like_rpc_call(tvb, ++ offset + 4) == NULL) { ++ /* Doesn't look like a call. */ ++ return 0; ++ } ++ break; + +- default: +- /* The putative message type field +- contains neither RPC_CALL nor +- RPC_REPLY, so it's not an RPC +- call or reply. */ ++ case RPC_REPLY: ++ /* Check for RPC reply. */ ++ if (looks_like_rpc_reply(tvb, pinfo, ++ offset + 4) == NULL) { ++ /* Doesn't look like a reply. */ + return 0; + } ++ break; + +- /* Get this conversation, creating it if +- it doesn't already exist, and make the +- dissector for it the non-heuristic RPC +- dissector for RPC-over-TCP. */ +- conversation = find_or_create_conversation(pinfo); +- conversation_set_dissector(conversation, +- rpc_tcp_handle); ++ default: ++ /* The putative message type field ++ contains neither RPC_CALL nor ++ RPC_REPLY, so it's not an RPC ++ call or reply. */ ++ return 0; + } + +- /* Try to get more data. */ ++ /* Get this conversation, creating it if ++ it doesn't already exist, and make the ++ dissector for it the non-heuristic RPC ++ dissector for RPC-over-TCP. */ ++ conversation = find_or_create_conversation(pinfo); ++ if (tcpinfo != NULL) { ++ conversation_set_dissector(conversation, rpc_tcp_handle); ++ } ++ } ++ ++ /* OK, we think it is RPC. Can we desegment? */ ++ if (rpc_desegment && pinfo->can_desegment) { ++ /* Yes, so tell the TCP dissector how much ++ * more data we need. */ ++ + pinfo->desegment_offset = offset; +- pinfo->desegment_len = len - seglen; ++ pinfo->desegment_len = len - tvb_reported_len; + return -((gint32) pinfo->desegment_len); ++ } else { ++ /* No. */ ++ expert_add_info(pinfo, NULL, &ei_rpc_segment_needed); ++ can_defragment = FALSE; + } ++ } else if (tvb_reported_len > (gint)len) { ++ tvb_reported_len = len; + } +- len += 4; /* include record mark */ ++ + tvb_len = tvb_captured_length_remaining(tvb, offset); +- tvb_reported_len = tvb_reported_length_remaining(tvb, offset); +- if (tvb_len > (gint)len) ++ if (tvb_len < (gint)len) { ++ /* We don't have all the data for this fragment. */ ++ can_defragment = FALSE; ++ }if (tvb_len > (gint)len) { + tvb_len = len; +- if (tvb_reported_len > (gint)len) +- tvb_reported_len = len; ++ } ++ + frag_tvb = tvb_new_subset_length_caplen(tvb, offset, tvb_len, + tvb_reported_len); + +@@ -3353,15 +3368,12 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo, + * If we're not defragmenting, just hand this to the + * disssector. + * +- * We defragment only if we should (rpc_defragment true) *and* +- * we can (tvb_len == tvb_reported_len, so that we have all the +- * data in the fragment). ++ * We defragment only if we should (rpc_defragment true) *and* we ++ * can (tvb_len <= len, so that we have all the data in the fragment). + */ +- if (!rpc_defragment || tvb_len != tvb_reported_len) { ++ if (!can_defragment) { + /* +- * This is the first fragment we've seen, and it's also +- * the last fragment; that means the record wasn't +- * fragmented. Hand the dissector the tvbuff for the ++ * We can't defragment. Hand the dissector the tvbuff for the + * fragment as the tvbuff for the record. + */ + rec_tvb = frag_tvb; +@@ -3374,7 +3386,8 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo, + save_fragmented = pinfo->fragmented; + pinfo->fragmented = TRUE; + rpc_succeeded = call_message_dissector(tvb, rec_tvb, pinfo, +- tree, frag_tvb, dissector, ipfd_head, rpc_rm, first_pdu); ++ tree, frag_tvb, dissector, ipfd_head, tp_type, rpc_rm, ++ first_pdu, can_defragment); + pinfo->fragmented = save_fragmented; + if (!rpc_succeeded) + return 0; /* not RPC */ +@@ -3421,7 +3434,7 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo, + * an exception. + */ + if (!(*dissector)(frag_tvb, pinfo, tree, frag_tvb, +- NULL, TRUE, rpc_rm, first_pdu)) ++ NULL, tp_type, rpc_rm, first_pdu, can_defragment)) + return 0; /* not valid */ + + /* +@@ -3606,7 +3619,8 @@ dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo, + * dissector. + */ + if (!call_message_dissector(tvb, rec_tvb, pinfo, tree, +- frag_tvb, dissector, ipfd_head, rpc_rm, first_pdu)) ++ frag_tvb, dissector, ipfd_head, tp_type, rpc_rm, ++ first_pdu, can_defragment)) + return 0; /* not RPC */ + return len; + } /* end of dissect_rpc_fragment() */ +@@ -3762,8 +3776,8 @@ find_rpc_over_tcp_reply_start(tvbuff_t *tvb, int offset) + static int + find_and_dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, rec_dissector_t dissector, +- gboolean is_heur, +- int proto, int ett, struct tcpinfo* tcpinfo) ++ gboolean is_heur, int proto, int ett, ++ struct tcpinfo *tcpinfo, struct tlsinfo *tlsinfo) + { + + int offReply; +@@ -3778,7 +3792,7 @@ find_and_dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo, + len = dissect_rpc_fragment(tvb, offReply, + pinfo, tree, + dissector, is_heur, proto, ett, +- TRUE /* force first-pdu state */, tcpinfo); ++ TRUE /* force first-pdu state */, tcpinfo, tlsinfo); + + /* misses are reported as-is */ + if (len == 0) +@@ -3809,7 +3823,7 @@ find_and_dissect_rpc_fragment(tvbuff_t *tvb, int offset, packet_info *pinfo, + */ + static gboolean + dissect_rpc_tcp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, +- gboolean is_heur, struct tcpinfo* tcpinfo) ++ gboolean is_heur, struct tcpinfo *tcpinfo, struct tlsinfo *tlsinfo) + { + int offset = 0; + gboolean saw_rpc = FALSE; +@@ -3822,7 +3836,7 @@ dissect_rpc_tcp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + */ + len = dissect_rpc_fragment(tvb, offset, pinfo, tree, + dissect_rpc_message, is_heur, proto_rpc, ett_rpc, +- first_pdu, tcpinfo); ++ first_pdu, tcpinfo, tlsinfo); + + if ((len == 0) && first_pdu && rpc_find_fragment_start) { + /* +@@ -3831,7 +3845,7 @@ dissect_rpc_tcp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + */ + len = find_and_dissect_rpc_fragment(tvb, offset, pinfo, tree, + dissect_rpc_message, is_heur, proto_rpc, ett_rpc, +- tcpinfo); ++ tcpinfo, tlsinfo); + } + + first_pdu = FALSE; +@@ -3889,7 +3903,7 @@ dissect_rpc_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void * + { + struct tcpinfo* tcpinfo = (struct tcpinfo *)data; + +- return dissect_rpc_tcp_common(tvb, pinfo, tree, TRUE, tcpinfo); ++ return dissect_rpc_tcp_common(tvb, pinfo, tree, TRUE, tcpinfo, NULL); + } + + static int +@@ -3897,12 +3911,42 @@ dissect_rpc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) + { + struct tcpinfo* tcpinfo = (struct tcpinfo *)data; + +- if (!dissect_rpc_tcp_common(tvb, pinfo, tree, FALSE, tcpinfo)) ++ if (!dissect_rpc_tcp_common(tvb, pinfo, tree, FALSE, tcpinfo, NULL)) + dissect_rpc_continuation(tvb, pinfo, tree); + + return tvb_reported_length(tvb); + } + ++static gboolean ++dissect_rpc_tls_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) ++{ ++ struct tlsinfo *tlsinfo = (struct tlsinfo *)data; ++ ++ if (dissect_rpc_tcp_common(tvb, pinfo, tree, TRUE, NULL, tlsinfo)) { ++ if (tlsinfo != NULL) { ++ *(tlsinfo->app_handle) = rpc_tls_handle; ++ } ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++static int ++dissect_rpc_tls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) ++{ ++ struct tlsinfo* tlsinfo = (struct tlsinfo *)data; ++ ++ col_set_writable(pinfo->cinfo, COL_PROTOCOL, TRUE); ++ if (dissect_rpc_tcp_common(tvb, pinfo, tree, FALSE, NULL, tlsinfo)) { ++ col_set_writable(pinfo->cinfo, COL_PROTOCOL, FALSE); ++ col_set_writable(pinfo->cinfo, COL_INFO, FALSE); ++ } else { ++ dissect_rpc_continuation(tvb, pinfo, tree); ++ } ++ ++ return tvb_reported_length(tvb); ++} ++ + /* Tap statistics */ + typedef enum + { +@@ -4319,6 +4363,7 @@ proto_register_rpc(void) + }; + static ei_register_info ei[] = { + { &ei_rpc_cannot_dissect, { "rpc.cannot_dissect", PI_UNDECODED, PI_WARN, "Cannot dissect", EXPFILL }}, ++ { &ei_rpc_segment_needed, { "rpc.segment_needed", PI_REASSEMBLE, PI_NOTE, "Need another TCP segment but cannot desegment or not enabled (check RPC and TCP preferences)", EXPFILL }}, + }; + + module_t *rpc_module; +@@ -4388,6 +4433,7 @@ proto_register_rpc(void) + + rpc_handle = register_dissector("rpc", dissect_rpc, proto_rpc); + rpc_tcp_handle = register_dissector("rpc-tcp", dissect_rpc_tcp, proto_rpc); ++ rpc_tls_handle = register_dissector("rpc-with-tls", dissect_rpc_tls, proto_rpc); + rpc_tap = register_tap("rpc"); + + register_srt_table(proto_rpc, NULL, 1, rpcstat_packet, rpcstat_init, rpcstat_param); +@@ -4425,9 +4471,11 @@ proto_reg_handoff_rpc(void) + */ + dissector_add_uint_with_preference("tcp.port", RPC_TCP_PORT, rpc_tcp_handle); + dissector_add_uint_with_preference("udp.port", RPC_TCP_PORT, rpc_handle); ++ dissector_add_string("tls.alpn", "sunrpc", rpc_tls_handle); + + heur_dissector_add("tcp", dissect_rpc_tcp_heur, "RPC over TCP", "rpc_tcp", proto_rpc, HEURISTIC_ENABLE); + heur_dissector_add("udp", dissect_rpc_heur, "RPC over UDP", "rpc_udp", proto_rpc, HEURISTIC_ENABLE); ++ heur_dissector_add("tls", dissect_rpc_tls_heur, "RPC with TLS", "rpc_tls", proto_rpc, HEURISTIC_ENABLE); + gssapi_handle = find_dissector_add_dependency("gssapi", proto_rpc); + spnego_krb5_wrap_handle = find_dissector_add_dependency("spnego-krb5-wrap", proto_rpc); + data_handle = find_dissector("data"); +diff --git a/epan/dissectors/packet-rpc.h b/epan/dissectors/packet-rpc.h +index a9a2908..b695394 100644 +--- a/epan/dissectors/packet-rpc.h ++++ b/epan/dissectors/packet-rpc.h +@@ -30,6 +30,7 @@ extern "C" { + #define AUTH_KRB4 4 + #define AUTH_RSA 5 + #define RPCSEC_GSS 6 ++#define AUTH_TLS 7 + #define AUTH_GSSAPI 300001 + /* Pseudo-flavors used for security mechanisms while using + * RPCSEC_GSS +diff --git a/epan/dissectors/packet-tls.c b/epan/dissectors/packet-tls.c +index 2f86795..0bef431 100644 +--- a/epan/dissectors/packet-tls.c ++++ b/epan/dissectors/packet-tls.c +@@ -493,12 +493,14 @@ static gint dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, + SslSession *session, gint is_from_server, + gboolean *need_desegmentation, + SslDecryptSession *conv_data, +- guint8 curr_layer_num_ssl); ++ guint8 curr_layer_num_ssl, ++ struct tlsinfo *tlsinfo); + + /* alert message dissector */ + static void dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo, + proto_tree *tree, guint32 offset, +- guint32 record_length, const SslSession *session); ++ guint32 record_length, const SslSession *session, ++ struct tlsinfo *tlsinfo); + + /* handshake protocol dissector */ + static void dissect_tls_handshake(tvbuff_t *tvb, packet_info *pinfo, +@@ -567,6 +569,17 @@ static gint ssl_looks_like_valid_v2_handshake(tvbuff_t *tvb, + const guint32 offset, + const guint32 record_length); + ++static void ++process_ssl_payload(tvbuff_t *tvb, int offset, packet_info *pinfo, ++ proto_tree *tree, SslSession *session, ++ dissector_handle_t app_handle_port, ++ struct tlsinfo *tlsinfo); ++static guint32 ++tls_msp_fragment_id(struct tcp_multisegment_pdu *msp); ++ ++static void ++print_tls_fragment_tree(fragment_head *ipfd_head, proto_tree *tree, proto_tree *tls_tree, packet_info *pinfo, tvbuff_t *next_tvb); ++ + /********************************************************************* + * + * Main dissector +@@ -584,9 +597,11 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) + proto_tree *ssl_tree; + guint32 offset; + gboolean need_desegmentation; +- SslDecryptSession *ssl_session; ++ SslDecryptSession *ssl_session, *ssl_session_save; + SslSession *session; + gint is_from_server; ++ struct tcpinfo *tcpinfo; ++ struct tlsinfo tlsinfo; + /* + * A single packet may contain multiple TLS records. Two possible scenarios: + * +@@ -610,6 +625,8 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) + offset = 0; + ssl_session = NULL; + ++ memset(&tlsinfo, 0, sizeof(tlsinfo)); ++ tcpinfo = (struct tcpinfo*)data; + + if (tvb_captured_length(tvb) > 4) { + const guint8 *tmp = tvb_get_ptr(tvb, 0, 4); +@@ -643,7 +660,7 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) + * in addition to conv_version + */ + conversation = find_or_create_conversation(pinfo); +- ssl_session = ssl_get_session(conversation, tls_handle); ++ ssl_session_save = ssl_session = ssl_get_session(conversation, tls_handle); + session = &ssl_session->session; + is_from_server = ssl_packet_from_server(session, ssl_associations, pinfo); + +@@ -746,7 +763,7 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) + offset, session, is_from_server, + &need_desegmentation, + ssl_session, +- curr_layer_num_ssl); ++ curr_layer_num_ssl, &tlsinfo); + } + break; + +@@ -786,7 +803,7 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) + offset, session, is_from_server, + &need_desegmentation, + ssl_session, +- curr_layer_num_ssl); ++ curr_layer_num_ssl, &tlsinfo); + } + else + { +@@ -809,6 +826,104 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) + } + } + ++ guint ret = tvb_captured_length(tvb); ++ ++ /* Check for needing to reassemble at end of stream */ ++ ssl_session = ssl_session_save; ++ if (ssl_session) { ++ SslDecoder *decoder; ++ /* retrieve decoder for this packet direction. Retrieve it ++ * here because the decoder could have been created while ++ * processing the records (e.g., an Early Data HTTP request ++ * and response, with no Content-Length.) */ ++ if (is_from_server != 0) { ++ decoder = ssl_session->server; ++ } ++ else { ++ decoder = ssl_session->client; ++ } ++ ++ if (decoder && decoder->flow && decoder->flow->flags & TCP_FLOW_REASSEMBLE_UNTIL_FIN) { ++ /* We want to reassemble at the end of the stream. Are we ++ * there? */ ++ /* There might be more than one record, and we don't want to tell ++ * the application dissector that we got a TCP FIN if there are ++ * more app data records to come. We add the FIN here. ++ * XXX: We could have some logic to do so in dissect_ssl3_record() ++ * when we're on the last record. Note that the last record could ++ * be an alert, or this could be a FIN with no data, so we'd still ++ * have to check here anyway.) ++ */ ++ if (tcpinfo) { ++ tlsinfo.end_of_stream |= (tcpinfo->flags & TH_FIN); ++ } ++ if (!tlsinfo.end_of_stream) { ++ /* No. Tell the TCP dissector that we want to desegment ++ * at FIN, so that it will call the TLS dissector at FIN ++ * even if there is no TCP payload. ++ * ++ * However, tell it that we've already dissected all the ++ * the data in the packet, so that we avoid getting it ++ * later and trying to decrypt the records again. ++ * (XXX: An alternative would be checking for already decrypted ++ * records before trying to decrypt on the first pass.) ++ */ ++ pinfo->desegment_offset = tvb_captured_length(tvb); ++ pinfo->desegment_len = DESEGMENT_UNTIL_FIN; ++ } else { ++ ssl_debug_printf(" desegmenting at end of stream (FIN)\n"); ++ struct tcp_multisegment_pdu *msp; ++ msp = (struct tcp_multisegment_pdu *)wmem_tree_lookup32_le(decoder->flow->multisegment_pdus, decoder->flow->byte_seq); ++ if (msp) { ++ fragment_head *ipfd_head; ++ ipfd_head = fragment_add(&ssl_reassembly_table, tvb, offset, ++ pinfo, tls_msp_fragment_id(msp), msp, ++ decoder->flow->byte_seq - msp->seq, ++ 0, FALSE); ++ if (ipfd_head && ipfd_head->reassembled_in == pinfo->num) { ++ tvbuff_t *next_tvb; ++ ++ /* create a new TVB structure for desegmented data */ ++ next_tvb = tvb_new_chain(tvb, ipfd_head->tvb_data); ++ ++ /* add desegmented data to the data source list */ ++ add_new_data_source(pinfo, next_tvb, "Reassembled TLS"); ++ ++ /* Show details of the reassembly */ ++ print_tls_fragment_tree(ipfd_head, tree, ssl_tree, pinfo, next_tvb); ++ ++ /* ++ * Supply the sequence number of the first of the ++ * reassembled bytes. ++ */ ++ tlsinfo.seq = msp->seq; ++ ++ /* indicate that this is reassembled data */ ++ tlsinfo.is_reassembled = TRUE; ++ ++ /* call subdissector */ ++ process_ssl_payload(next_tvb, 0, pinfo, tree, session, session->app_handle, &tlsinfo); ++ ++ if (ret == 0) { ++ /* XXX: Workaround for #15159. Ordinarily we ++ * return the number of bytes dissected, but zero ++ * indicates the dissector rejecting the data. If ++ * we are dissecting at FIN, but there were no new ++ * records added, we want to indicate that the ++ * dissector accepted the zero length payload so ++ * that the TLS (and, e.g. HTTP) layers don't get ++ * removed. So artificially return 1 instead. ++ * (The TCP dissector will ignore the number.) ++ */ ++ ret = 1; ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ + col_set_fence(pinfo->cinfo, COL_INFO); + + ssl_debug_flush(); +@@ -816,7 +931,7 @@ dissect_ssl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) + /* Make data available to ssl_follow_tap_listener */ + tap_queue_packet(tls_tap, pinfo, p_get_proto_data(wmem_file_scope(), pinfo, proto_tls, curr_layer_num_ssl)); + +- return tvb_captured_length(tvb); ++ return ret; + } + + /* +@@ -1166,9 +1281,28 @@ decrypt_tls13_early_data(tvbuff_t *tvb, packet_info *pinfo, guint32 offset, + #endif + + static void +-process_ssl_payload(tvbuff_t *tvb, int offset, packet_info *pinfo, +- proto_tree *tree, SslSession *session, +- dissector_handle_t app_handle_port); ++print_tls_fragment_tree(fragment_head *ipfd_head, proto_tree *tree, proto_tree *tls_tree, packet_info *pinfo, tvbuff_t *next_tvb) ++{ ++ proto_item *tls_tree_item, *frag_tree_item; ++ ++ /* ++ * The subdissector thought it was completely ++ * desegmented (although the stuff at the ++ * end may, in turn, require desegmentation), ++ * so we show a tree with all segments. ++ */ ++ show_fragment_tree(ipfd_head, &ssl_segment_items, ++ tree, pinfo, next_tvb, &frag_tree_item); ++ /* ++ * The toplevel fragment subtree is now ++ * behind all desegmented data; move it ++ * right behind the TLS tree. ++ */ ++ tls_tree_item = proto_tree_get_parent(tls_tree); ++ if (frag_tree_item && tls_tree_item) { ++ proto_tree_move_item(tree, tls_tree_item, frag_tree_item); ++ } ++} + + static guint32 + tls_msp_fragment_id(struct tcp_multisegment_pdu *msp) +@@ -1191,7 +1325,8 @@ 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, dissector_handle_t app_handle_port) ++ SslFlow *flow, dissector_handle_t app_handle_port, ++ struct tlsinfo *tlsinfo) + { + fragment_head *ipfd_head; + gboolean must_desegment; +@@ -1202,8 +1337,6 @@ desegment_ssl(tvbuff_t *tvb, packet_info *pinfo, int offset, + guint32 deseg_seq; + gint nbytes; + proto_item *item; +- proto_item *frag_tree_item; +- proto_item *ssl_tree_item; + struct tcp_multisegment_pdu *msp; + + again: +@@ -1300,7 +1433,15 @@ again: + * contain a continuation of a higher-level PDU. + * Call the normal subdissector. + */ +- process_ssl_payload(tvb, offset, pinfo, tree, session, app_handle_port); ++ ++ /* ++ * Supply the sequence number of this segment. We set this here ++ * because this segment could be after another in the same packet, ++ * in which case seq was incremented at the end of the loop. ++ */ ++ tlsinfo->seq = seq; ++ ++ process_ssl_payload(tvb, offset, pinfo, tree, session, app_handle_port, tlsinfo); + called_dissector = TRUE; + + /* Did the subdissector ask us to desegment some more data +@@ -1343,6 +1484,8 @@ again: + * This is *not* the last segment. It is part of a PDU in the same + * frame, so no another PDU can follow this one. + * Do not reassemble TLS yet, it will be done in the final segment. ++ * (If we are reassembling at FIN, we will do that in dissect_ssl() ++ * after iterating through all the records.) + * Clear the Info column and avoid displaying [TLS segment of a + * reassembled PDU], the payload dissector will typically set it. + * (This is needed here for the second pass.) +@@ -1375,8 +1518,17 @@ again: + /* add desegmented data to the data source list */ + add_new_data_source(pinfo, next_tvb, "Reassembled SSL"); + ++ /* ++ * Supply the sequence number of the first of the ++ * reassembled bytes. ++ */ ++ tlsinfo->seq = msp->seq; ++ ++ /* indicate that this is reassembled data */ ++ tlsinfo->is_reassembled = TRUE; ++ + /* call subdissector */ +- process_ssl_payload(next_tvb, 0, pinfo, tree, session, app_handle_port); ++ process_ssl_payload(next_tvb, 0, pinfo, tree, session, app_handle_port, tlsinfo); + called_dissector = TRUE; + + /* +@@ -1413,6 +1565,15 @@ again: + */ + msp->nxtpdu = seq + tvb_reported_length_remaining(tvb, offset) + 1; + msp->flags |= MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT; ++ } else if (pinfo->desegment_len == DESEGMENT_UNTIL_FIN) { ++ /* This is not the first segment, and we thought reassembly ++ * would be done now, but now we know we desegment at FIN. ++ * E.g., a HTTP response where the headers were split ++ * across segments (so previous ONE_MORE_SEGMENT) and ++ * also no Content-Length (so now DESEGMENT_UNTIL_FIN). ++ */ ++ flow->flags |= TCP_FLOW_REASSEMBLE_UNTIL_FIN; ++ msp->nxtpdu = nxtseq + 0x40000000; + } else { + msp->nxtpdu = seq + tvb_reported_length_remaining(tvb, offset) + pinfo->desegment_len; + } +@@ -1429,23 +1590,8 @@ again: + nbytes = tvb_reported_length_remaining(tvb, offset); + ssl_proto_tree_add_segment_data(tree, tvb, offset, nbytes, NULL); + +- /* +- * The subdissector thought it was completely +- * desegmented (although the stuff at the +- * end may, in turn, require desegmentation), +- * so we show a tree with all segments. +- */ +- show_fragment_tree(ipfd_head, &ssl_segment_items, +- root_tree, pinfo, next_tvb, &frag_tree_item); +- /* +- * The toplevel fragment subtree is now +- * behind all desegmented data; move it +- * right behind the TCP tree. +- */ +- ssl_tree_item = proto_tree_get_parent(tree); +- if (frag_tree_item && ssl_tree_item) { +- proto_tree_move_item(root_tree, ssl_tree_item, frag_tree_item); +- } ++ /* Show details of the reassembly */ ++ print_tls_fragment_tree(ipfd_head, root_tree, tree, pinfo, next_tvb); + + /* Did the subdissector ask us to desegment + * some more data? This means that the data +@@ -1537,6 +1683,13 @@ again: + msp = pdu_store_sequencenumber_of_next_pdu(pinfo, + deseg_seq, nxtseq+1, flow->multisegment_pdus); + msp->flags |= MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT; ++ } else if (pinfo->desegment_len == DESEGMENT_UNTIL_FIN) { ++ /* Set nxtseq very large so that reassembly won't happen ++ * until we force it at the end of the stream in dissect_ssl() ++ * outside this function. ++ */ ++ msp = pdu_store_sequencenumber_of_next_pdu(pinfo, ++ deseg_seq, nxtseq+0x40000000, flow->multisegment_pdus); + } else { + msp = pdu_store_sequencenumber_of_next_pdu(pinfo, + deseg_seq, nxtseq+pinfo->desegment_len, flow->multisegment_pdus); +@@ -1628,22 +1781,25 @@ export_pdu_packet(tvbuff_t *tvb, packet_info *pinfo, guint8 tag, const gchar *na + static void + process_ssl_payload(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, SslSession *session, +- dissector_handle_t app_handle_port) ++ dissector_handle_t app_handle_port, ++ struct tlsinfo *tlsinfo) + { + tvbuff_t *next_tvb; + heur_dtbl_entry_t *hdtbl_entry; + guint16 saved_match_port; + ++ tlsinfo->app_handle = &session->app_handle; ++ + next_tvb = tvb_new_subset_remaining(tvb, offset); + + /* 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. */ ++ /* The heuristics dissector should set the app_handle via tlsinfo ++ * 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, +- &session->app_handle)) { ++ tlsinfo)) { + ssl_debug_printf("%s: found heuristics dissector %s, app_handle is %p (%s)\n", + G_STRFUNC, hdtbl_entry->short_name, + (void *)session->app_handle, +@@ -1681,7 +1837,7 @@ process_ssl_payload(tvbuff_t *tvb, int offset, packet_info *pinfo, + } else { + pinfo->match_uint = pinfo->destport; + } +- call_dissector(session->app_handle, next_tvb, pinfo, proto_tree_get_root(tree)); ++ call_dissector_with_data(session->app_handle, next_tvb, pinfo, proto_tree_get_root(tree), tlsinfo); + pinfo->match_uint = saved_match_port; + } + +@@ -1689,11 +1845,14 @@ static void + dissect_ssl_payload(tvbuff_t *decrypted, packet_info *pinfo, + proto_tree *tree, SslSession *session, + SslRecordInfo *record, +- dissector_handle_t app_handle_port) ++ dissector_handle_t app_handle_port, ++ struct tlsinfo *tlsinfo) + { + gboolean save_fragmented; + guint16 save_can_desegment; + ++ tlsinfo->seq = record->seq; ++ + /* Preserve current desegmentation ability to prevent the subdissector + * from messing up the ssl desegmentation */ + save_can_desegment = pinfo->can_desegment; +@@ -1708,7 +1867,7 @@ dissect_ssl_payload(tvbuff_t *decrypted, packet_info *pinfo, + pinfo->can_desegment = 2; + desegment_ssl(decrypted, pinfo, 0, record->seq, record->seq + record->data_len, + session, proto_tree_get_root(tree), tree, +- record->flow, app_handle_port); ++ record->flow, app_handle_port, tlsinfo); + } else if (session->app_handle || app_handle_port) { + /* No - just call the subdissector. + Mark this as fragmented, so if somebody throws an exception, +@@ -1717,7 +1876,7 @@ dissect_ssl_payload(tvbuff_t *decrypted, packet_info *pinfo, + save_fragmented = pinfo->fragmented; + pinfo->fragmented = TRUE; + +- process_ssl_payload(decrypted, 0, pinfo, tree, session, app_handle_port); ++ process_ssl_payload(decrypted, 0, pinfo, tree, session, app_handle_port, tlsinfo); + pinfo->fragmented = save_fragmented; + } + +@@ -1737,7 +1896,7 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, + SslSession *session, gint is_from_server, + gboolean *need_desegmentation, + SslDecryptSession *ssl, +- guint8 curr_layer_num_ssl) ++ guint8 curr_layer_num_ssl, struct tlsinfo *tlsinfo) + { + + /* +@@ -1913,7 +2072,9 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, + * record for later usage (without having to decrypt again). The offset is + * used as 'key' to identify this record in the packet (we can have multiple + * handshake records in the same frame). +- * In TLS 1.3, only "Application Data" records are encrypted. ++ * In TLS 1.3, an encrypted record always has (outer) opaque_type of ++ * "Application Data". The actual content type of the record is found ++ * after decryption. + */ + if (ssl && record_length && (session->version != TLSV1DOT3_VERSION || content_type == SSL_ID_APP_DATA)) { + gboolean decrypt_ok = FALSE; +@@ -1985,9 +2146,9 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, + break; + case SSL_ID_ALERT: + if (decrypted) { +- dissect_ssl3_alert(decrypted, pinfo, ssl_record_tree, 0, 2, session); ++ dissect_ssl3_alert(decrypted, pinfo, ssl_record_tree, 0, 2, session, tlsinfo); + } else { +- dissect_ssl3_alert(tvb, pinfo, ssl_record_tree, offset, record_length, session); ++ dissect_ssl3_alert(tvb, pinfo, ssl_record_tree, offset, record_length, session, tlsinfo); + } + break; + case SSL_ID_HANDSHAKE: +@@ -2041,7 +2202,7 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, + } + + if (decrypted) { +- dissect_ssl_payload(decrypted, pinfo, tree, session, record, app_handle); ++ dissect_ssl_payload(decrypted, pinfo, tree, session, record, app_handle, tlsinfo); + } + + /* Set app proto again in case the heuristics found a different proto. */ +@@ -2087,7 +2248,7 @@ dissect_ssl3_record(tvbuff_t *tvb, packet_info *pinfo, + static void + dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo, + proto_tree *tree, guint32 offset, guint32 record_length, +- const SslSession *session) ++ const SslSession *session, struct tlsinfo *tlsinfo) + { + /* struct { + * AlertLevel level; +@@ -2131,6 +2292,13 @@ dissect_ssl3_alert(tvbuff_t *tvb, packet_info *pinfo, + + desc_byte = tvb_get_guint8(tvb, offset+1); /* grab the desc byte */ + desc = val_to_str_const(desc_byte, ssl_31_alert_description, "Unknown"); ++ if (desc_byte == 0) { ++ /* If this is a close_notify, mark it as the end of the stream. ++ * (XXX: Maybe we should do this for other alerts, and maybe ++ * reassembling at FIN should also try reassembling at RST as well?) ++ */ ++ tlsinfo->end_of_stream = TRUE; ++ } + + /* now set the text in the record layer line */ + col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, +diff --git a/epan/dissectors/packet-tls.h b/epan/dissectors/packet-tls.h +index 9cee62b..b28831e 100644 +--- a/epan/dissectors/packet-tls.h ++++ b/epan/dissectors/packet-tls.h +@@ -14,6 +14,15 @@ + #include "ws_symbol_export.h" + #include + ++struct tlsinfo { ++ guint32 seq; /* The sequence number within the TLS stream. */ ++ gboolean is_reassembled; ++ gboolean end_of_stream; /* TCP FIN, close_notify, etc. */ ++ /* The app handle for the session, set by heuristic dissectors ++ * to be called in the future. */ ++ dissector_handle_t *app_handle; ++}; ++ + /** Maps Session-ID to pre-master secrets. */ + WS_DLL_PUBLIC GHashTable *ssl_session_hash; + /** Maps Client Random to pre-master secrets. */ diff --git a/wireshark.spec b/wireshark.spec index 1becb3e..2c59787 100644 --- a/wireshark.spec +++ b/wireshark.spec @@ -6,7 +6,7 @@ Summary: Network traffic analyzer Name: wireshark Version: 3.4.10 -Release: 7%{?dist} +Release: 8%{?dist} Epoch: 1 License: GPL+ Url: http://www.wireshark.org/ @@ -37,6 +37,7 @@ Patch0015: wireshark-0015-cve-2023-2856.patch Patch0016: wireshark-0016-cve-2023-2855.patch Patch0017: wireshark-0017-cve-2023-2952.patch Patch0018: wireshark-0018-sync-pipe-stderr-messages.patch +Patch0019: wireshark-0019-nfs-over-tls.patch #install tshark together with wireshark GUI Requires: %{name}-cli = %{epoch}:%{version}-%{release} @@ -284,6 +285,9 @@ fi %{_libdir}/pkgconfig/%{name}.pc %changelog +* Mon Jul 07 2025 Michal Ruprich - 1:3.4.10-8 +- Resolves: RHEL-55554 - NFS packets decrypted via TLS are displayed as RPC + * Wed Jul 17 2024 Michal Ruprich - 1:3.4.10-7 - Resolves: RHEL-14596 - Wireshark hangs if dumpcap returned unexpected messages in sync pipe.