/src/wireshark/epan/dissectors/packet-rdp_multitransport.c
Line | Count | Source |
1 | | /* packet-rdpudp.c |
2 | | * Routines for RDP multi transport packet dissection |
3 | | * Copyright 2021, David Fort |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | #include "config.h" |
13 | | |
14 | | #include <epan/packet.h> |
15 | | #include <epan/prefs.h> |
16 | | #include <epan/expert.h> |
17 | | #include <epan/conversation.h> |
18 | | |
19 | | #include "packet-rdp.h" |
20 | | #include "packet-rdpudp.h" |
21 | | |
22 | 14 | #define PNAME "Remote Desktop Protocol Multi-transport" |
23 | 14 | #define PSNAME "RDPMT" |
24 | 14 | #define PFNAME "rdpmt" |
25 | | |
26 | | void proto_register_rdpmt(void); |
27 | | void proto_reg_handoff_rdpmt(void); |
28 | | |
29 | | static dissector_handle_t rdpmt_handle; |
30 | | |
31 | | static int proto_rdpmt; |
32 | | |
33 | | static int hf_rdpmt_action; |
34 | | static int hf_rdpmt_flags; |
35 | | static int hf_rdpmt_payload_len; |
36 | | static int hf_rdpmt_header_len; |
37 | | static int hf_rdpmt_subheader_len; |
38 | | static int hf_rdpmt_subheader_type; |
39 | | static int hf_rdpmt_createreq_reqId; |
40 | | static int hf_rdpmt_createreq_reserved; |
41 | | static int hf_rdpmt_createreq_cookie; |
42 | | static int hf_rdpmt_createresp_hrResponse; |
43 | | |
44 | | static int ett_rdpmt; |
45 | | static int ett_rdpudp_subheaders; |
46 | | static int ett_rdpmt_create_req; |
47 | | static int ett_rdpmt_create_resp; |
48 | | static int ett_rdpmt_data; |
49 | | |
50 | | static dissector_handle_t drdynvcDissector; |
51 | | |
52 | | static const value_string rdpmt_action_vals[] = { |
53 | | { 0x00, "CreateRequest"}, |
54 | | { 0x01, "CreateResponse"}, |
55 | | { 0x02, "Data"}, |
56 | | { 0x00, NULL} |
57 | | }; |
58 | | |
59 | | static const value_string rdpmt_subheader_type_vals[] = { |
60 | | { 0x0, "auto detect request" }, |
61 | | { 0x1, "auto detect response" }, |
62 | | { 0x0, NULL} |
63 | | }; |
64 | | |
65 | | enum { |
66 | | RDPMT_TUNNEL_CREATE_REQ = 0, |
67 | | RDPMT_TUNNEL_CREATE_RESP = 1, |
68 | | RDPMT_TUNNEL_DATA = 2, |
69 | | }; |
70 | | |
71 | | static int |
72 | | dissect_rdpmt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_) |
73 | 0 | { |
74 | 0 | proto_item *item; |
75 | 0 | proto_tree *tree, *subtree; |
76 | 0 | uint8_t action, subheader_len; |
77 | 0 | uint16_t payload_len; |
78 | 0 | int offset = 0; |
79 | |
|
80 | 0 | item = proto_tree_add_item(parent_tree, proto_rdpmt, tvb, 0, -1, ENC_NA); |
81 | 0 | tree = proto_item_add_subtree(item, ett_rdpmt); |
82 | |
|
83 | 0 | action = tvb_get_uint8(tvb, offset) & 0x0f; |
84 | 0 | proto_tree_add_item(tree, hf_rdpmt_action, tvb, offset, 1, ENC_NA); |
85 | 0 | proto_tree_add_item(tree, hf_rdpmt_flags, tvb, offset, 1, ENC_NA); |
86 | 0 | offset++; |
87 | |
|
88 | 0 | payload_len = tvb_get_uint16(tvb, offset, ENC_LITTLE_ENDIAN); |
89 | 0 | proto_tree_add_item(tree, hf_rdpmt_payload_len, tvb, offset, 2, ENC_LITTLE_ENDIAN); |
90 | 0 | offset += 2; |
91 | |
|
92 | 0 | subheader_len = tvb_get_uint8(tvb, offset); |
93 | 0 | proto_tree_add_item(tree, hf_rdpmt_header_len, tvb, offset, 1, ENC_NA); |
94 | 0 | offset += 1; |
95 | |
|
96 | 0 | if (subheader_len > 4) { |
97 | 0 | tvbuff_t *subheaders = tvb_new_subset_length(tvb, offset, subheader_len-4); |
98 | 0 | proto_tree *subheaders_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_rdpudp_subheaders, NULL, "SubHeaders"); |
99 | 0 | dissect_rdp_bandwidth_req(subheaders, 0, pinfo, subheaders_tree, !!rdp_isServerAddressTarget(pinfo)); |
100 | 0 | } |
101 | | |
102 | |
|
103 | 0 | offset += subheader_len - 4; |
104 | |
|
105 | 0 | switch (action) { |
106 | 0 | case RDPMT_TUNNEL_CREATE_REQ: { |
107 | 0 | uint8_t cookie[16]; |
108 | 0 | uint32_t reqId; |
109 | 0 | conversation_t *conv = find_or_create_conversation(pinfo); |
110 | |
|
111 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "TunnelCreateRequest"); |
112 | |
|
113 | 0 | subtree = proto_tree_add_subtree(tree, tvb, offset, payload_len, ett_rdpmt_create_req, NULL, "TunnelCreateRequest"); |
114 | 0 | proto_tree_add_item(subtree, hf_rdpmt_createreq_reqId, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
115 | 0 | reqId = tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN); |
116 | 0 | offset += 4; |
117 | |
|
118 | 0 | proto_tree_add_item(subtree, hf_rdpmt_createreq_reserved, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
119 | 0 | offset += 4; |
120 | |
|
121 | 0 | proto_tree_add_item(subtree, hf_rdpmt_createreq_cookie, tvb, offset, 16, ENC_NA); |
122 | 0 | tvb_memcpy(tvb, cookie, offset, 16); |
123 | 0 | offset += 4; |
124 | |
|
125 | 0 | rdp_transport_set_udp_conversation(pinfo, rdpudp_is_reliable_transport(pinfo), reqId, cookie, conv); |
126 | 0 | break; |
127 | 0 | } |
128 | 0 | case RDPMT_TUNNEL_CREATE_RESP: |
129 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "TunnelCreateResponse"); |
130 | 0 | subtree = proto_tree_add_subtree(tree, tvb, offset, payload_len, ett_rdpmt_create_resp, NULL, "TunnelCreateResponse"); |
131 | 0 | proto_tree_add_item(subtree, hf_rdpmt_createresp_hrResponse, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
132 | 0 | break; |
133 | | |
134 | 0 | case RDPMT_TUNNEL_DATA: |
135 | 0 | if (payload_len) { |
136 | 0 | tvbuff_t *payload = tvb_new_subset_length(tvb, offset, payload_len); |
137 | 0 | subtree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_rdpmt_data, NULL, "Data"); |
138 | 0 | call_dissector(drdynvcDissector, payload, pinfo, subtree); |
139 | 0 | } |
140 | 0 | break; |
141 | 0 | } |
142 | | |
143 | 0 | return offset; |
144 | 0 | } |
145 | | |
146 | | void |
147 | 14 | proto_register_rdpmt(void) { |
148 | | /* List of fields */ |
149 | 14 | static hf_register_info hf[] = { |
150 | | |
151 | 14 | {&hf_rdpmt_action, |
152 | 14 | {"Action", "rdpmt.action", FT_UINT8, BASE_HEX, VALS(rdpmt_action_vals), 0x0F, NULL, HFILL} |
153 | 14 | }, |
154 | 14 | {&hf_rdpmt_flags, |
155 | 14 | {"Flags", "rdpmt.flags", FT_UINT8, BASE_HEX, NULL, 0xF0, NULL, HFILL} |
156 | 14 | }, |
157 | 14 | {&hf_rdpmt_payload_len, |
158 | 14 | {"Payload length", "rdpmt.payloadlen", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL} |
159 | 14 | }, |
160 | 14 | {&hf_rdpmt_header_len, |
161 | 14 | {"Header length", "rdpmt.headerlen", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL} |
162 | 14 | }, |
163 | 14 | {&hf_rdpmt_subheader_len, |
164 | 14 | {"Sub header length", "rdpmt.subheaderlen", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL} |
165 | 14 | }, |
166 | 14 | {&hf_rdpmt_subheader_type, |
167 | 14 | {"Sub header type", "rdpmt.subheadertype", FT_UINT8, BASE_HEX, VALS(rdpmt_subheader_type_vals), 0, NULL, HFILL} |
168 | 14 | }, |
169 | 14 | {&hf_rdpmt_createreq_reqId, |
170 | 14 | {"RequestID", "rdpmt.createrequest.requestid", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL} |
171 | 14 | }, |
172 | 14 | {&hf_rdpmt_createreq_reserved, |
173 | 14 | {"Reserved", "rdpmt.createrequest.reserved", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL} |
174 | 14 | }, |
175 | 14 | {&hf_rdpmt_createreq_cookie, |
176 | 14 | {"Security cookie", "rdpmt.createrequest.cookie", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL} |
177 | 14 | }, |
178 | 14 | {&hf_rdpmt_createresp_hrResponse, |
179 | 14 | {"hrResponse", "rdpmt.createresponse.hrresponse", FT_INT32, BASE_DEC, NULL, 0, NULL, HFILL} |
180 | 14 | } |
181 | 14 | }; |
182 | | |
183 | | /* List of subtrees */ |
184 | 14 | static int *ett[] = { |
185 | 14 | &ett_rdpmt, |
186 | 14 | &ett_rdpudp_subheaders, |
187 | 14 | &ett_rdpmt_create_req, |
188 | 14 | &ett_rdpmt_create_resp, |
189 | 14 | &ett_rdpmt_data |
190 | 14 | }; |
191 | | |
192 | | /* Register protocol */ |
193 | 14 | proto_rdpmt = proto_register_protocol(PNAME, PSNAME, PFNAME); |
194 | | /* Register fields and subtrees */ |
195 | 14 | proto_register_field_array(proto_rdpmt, hf, array_length(hf)); |
196 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
197 | | |
198 | 14 | rdpmt_handle = register_dissector("rdpmt", dissect_rdpmt, proto_rdpmt); |
199 | 14 | } |
200 | | |
201 | | static bool |
202 | | rdpmt_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
203 | 0 | { |
204 | 0 | uint8_t action, header_len; |
205 | 0 | uint16_t payload_len; |
206 | |
|
207 | 0 | if (tvb_reported_length(tvb) <= 4) |
208 | 0 | return false; |
209 | | |
210 | 0 | action = tvb_get_uint8(tvb, 0); |
211 | 0 | if (action > 2) |
212 | 0 | return false; |
213 | | |
214 | 0 | payload_len = tvb_get_uint16(tvb, 1, ENC_LITTLE_ENDIAN); |
215 | 0 | header_len = tvb_get_uint8(tvb, 3); |
216 | |
|
217 | 0 | if ((header_len < 4UL) || (tvb_reported_length_remaining(tvb, header_len) < payload_len)) |
218 | 0 | return false; |
219 | | |
220 | 0 | if (header_len > 4) { |
221 | 0 | uint8_t subheader_len, subheader_type; |
222 | |
|
223 | 0 | if(header_len < 6) |
224 | 0 | return false; |
225 | | |
226 | 0 | subheader_len = tvb_get_uint8(tvb, 4); |
227 | 0 | if ((subheader_len < 2) || (subheader_len > header_len-4)) |
228 | 0 | return false; |
229 | | |
230 | 0 | subheader_type = tvb_get_uint8(tvb, 5); |
231 | 0 | if (subheader_type > 1) /* AUTODETECT_REQUEST or AUTODETECT_RESPONSE */ |
232 | 0 | return false; |
233 | 0 | } |
234 | | |
235 | 0 | return dissect_rdpmt(tvb, pinfo, tree, data) > 0; |
236 | 0 | } |
237 | | |
238 | | void |
239 | | proto_reg_handoff_rdpmt(void) |
240 | 14 | { |
241 | 14 | drdynvcDissector = find_dissector("rdp_drdynvc"); |
242 | | |
243 | 14 | heur_dissector_add("tls", rdpmt_heur, "RDP MultiTransport", "rdpmt_tls_", proto_rdpmt, true); |
244 | | heur_dissector_add("dtls", rdpmt_heur, "RDP MultiTransport", "rdpmt_dtls", proto_rdpmt, true); |
245 | 14 | } |
246 | | |
247 | | /* |
248 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
249 | | * |
250 | | * Local Variables: |
251 | | * c-basic-offset: 2 |
252 | | * tab-width: 8 |
253 | | * indent-tabs-mode: nil |
254 | | * End: |
255 | | * |
256 | | * ex: set shiftwidth=2 tabstop=8 expandtab: |
257 | | * :indentSize=2:tabSize=8:noTabs=true: |
258 | | */ |