/src/wireshark/epan/dissectors/packet-socks.c
Line | Count | Source |
1 | | /* packet-socks.c |
2 | | * Routines for socks versions 4 &5 packet dissection |
3 | | * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com> |
4 | | * Copyright 2008, Jelmer Vernooij <jelmer@samba.org> |
5 | | * |
6 | | * Wireshark - Network traffic analyzer |
7 | | * By Gerald Combs <gerald@wireshark.org> |
8 | | * Copyright 1998 Gerald Combs |
9 | | * |
10 | | * SPDX-License-Identifier: GPL-2.0-or-later |
11 | | * |
12 | | * |
13 | | * The Version 4 decode is based on SOCKS4.protocol and SOCKS4A.protocol. |
14 | | * The Version 5 decoder is based upon rfc-1928 |
15 | | * The Version 5 User/Password authentication is based on rfc-1929. |
16 | | * |
17 | | * See |
18 | | * http://www.openssh.org/txt/socks4.protocol |
19 | | * http://www.openssh.org/txt/socks4a.protocol |
20 | | * |
21 | | * for information on SOCKS version 4 and 4a. |
22 | | * |
23 | | * Revisions: |
24 | | * |
25 | | * 2003-09-18 JCFoster Fixed problem with socks tunnel in socks tunnel |
26 | | * causing heap overflow because of an infinite loop |
27 | | * where the socks dissect was call over and over. |
28 | | * |
29 | | * Also remove some old code marked with __JUNK__ |
30 | | * |
31 | | * 2001-01-08 JCFoster Fixed problem with NULL pointer for hash data. |
32 | | * Now test and exit if hash_info is null. |
33 | | */ |
34 | | |
35 | | /* Possible enhancements - |
36 | | * |
37 | | * Add GSS-API authentication per rfc-1961 |
38 | | * Add CHAP authentication |
39 | | * Decode FLAG bits per |
40 | | * https://tools.ietf.org/html/draft-ietf-aft-socks-pro-v5-04 |
41 | | * In call_next_dissector, could load the destination address into |
42 | | * pinfo->src or pinfo->dst structure before calling next dissector. |
43 | | */ |
44 | | |
45 | | #include "config.h" |
46 | | |
47 | | #include <epan/packet.h> |
48 | | #include <epan/exceptions.h> |
49 | | #include <epan/proto_data.h> |
50 | | |
51 | | #include "packet-tcp.h" |
52 | | #include "packet-udp.h" |
53 | | #include "packet-tls.h" |
54 | | |
55 | | #include <epan/strutil.h> |
56 | | |
57 | 16 | #define TCP_PORT_SOCKS 1080 |
58 | | |
59 | | |
60 | | /**************** Socks commands ******************/ |
61 | | |
62 | 3 | #define CONNECT_COMMAND 1 |
63 | 0 | #define BIND_COMMAND 2 |
64 | 2 | #define UDP_ASSOCIATE_COMMAND 3 |
65 | 27 | #define PING_COMMAND 0x80 |
66 | 27 | #define TRACERT_COMMAND 0x81 |
67 | | |
68 | | |
69 | | /********** V5 Authentication methods *************/ |
70 | | |
71 | 0 | #define NO_AUTHENTICATION 0 |
72 | 0 | #define GSS_API_AUTHENTICATION 1 |
73 | 0 | #define USER_NAME_AUTHENTICATION 2 |
74 | | #define CHAP_AUTHENTICATION 3 |
75 | | #define AUTHENTICATION_FAILED 0xff |
76 | | |
77 | | void proto_register_socks(void); |
78 | | void proto_reg_handoff_socks(void); |
79 | | |
80 | | /*********** Header field identifiers *************/ |
81 | | |
82 | | static int proto_socks; |
83 | | |
84 | | static int ett_socks; |
85 | | static int ett_socks_auth; |
86 | | static int ett_socks_name; |
87 | | |
88 | | static int hf_socks_ver; |
89 | | static int hf_socks_ip_dst; |
90 | | static int hf_socks_ip6_dst; |
91 | | static int hf_gssapi_payload; |
92 | | static int hf_gssapi_command; |
93 | | static int hf_gssapi_length; |
94 | | static int hf_v4a_dns_name; |
95 | | static int hf_socks_dstport; |
96 | | static int hf_socks_cmd; |
97 | | static int hf_socks_results_4; |
98 | | static int hf_socks_results_5; |
99 | | static int hf_client_auth_method_count; |
100 | | static int hf_client_auth_method; |
101 | | static int hf_socks_reserved; |
102 | | static int hf_socks_reserved2; |
103 | | static int hf_client_port; |
104 | | static int hf_server_accepted_auth_method; |
105 | | static int hf_server_auth_status; |
106 | | static int hf_server_remote_host_port; |
107 | | static int hf_socks_subnegotiation_version; |
108 | | static int hf_socks_username; |
109 | | static int hf_socks_password; |
110 | | static int hf_socks_remote_name; |
111 | | static int hf_socks_address_type; |
112 | | static int hf_socks_fragment_number; |
113 | | static int hf_socks_ping_end_command; |
114 | | static int hf_socks_ping_results; |
115 | | static int hf_socks_traceroute_end_command; |
116 | | static int hf_socks_traceroute_results; |
117 | | |
118 | | /************* Dissector handles ***********/ |
119 | | |
120 | | static dissector_handle_t socks_handle; |
121 | | static dissector_handle_t socks_handle_tls; |
122 | | static dissector_handle_t socks_udp_handle; |
123 | | |
124 | | /************* State Machine names ***********/ |
125 | | |
126 | | enum ClientState { |
127 | | clientNoInit = -1, |
128 | | clientStart = 0, |
129 | | clientWaitForAuthReply, |
130 | | clientV5Command, |
131 | | clientUserNameRequest, |
132 | | clientGssApiAuthRequest, |
133 | | clientDone, |
134 | | clientError |
135 | | }; |
136 | | |
137 | | enum ServerState { |
138 | | serverNoInit = -1, |
139 | | serverStart = 0, |
140 | | serverInitReply, |
141 | | serverCommandReply, |
142 | | serverUserReply, |
143 | | serverGssApiReply, |
144 | | serverBindReply, |
145 | | serverDone, |
146 | | serverError |
147 | | }; |
148 | | |
149 | | typedef struct { |
150 | | int in_socks_dissector_flag; |
151 | | enum ClientState client; |
152 | | enum ServerState server; |
153 | | } sock_state_t; |
154 | | |
155 | | typedef struct { |
156 | | conversation_t *proxy_conv; |
157 | | enum ClientState clientState; |
158 | | enum ServerState serverState; |
159 | | int version; |
160 | | int command; |
161 | | int authentication_method; |
162 | | uint32_t server_port; |
163 | | uint32_t port; |
164 | | uint32_t udp_port; |
165 | | uint32_t udp_remote_port; |
166 | | address dst_addr; |
167 | | |
168 | | uint32_t start_done_frame; |
169 | | }socks_hash_entry_t; |
170 | | |
171 | | |
172 | | static const value_string address_type_table[] = { |
173 | | {1, "IPv4"}, |
174 | | {3, "Domain Name"}, |
175 | | {4, "IPv6"}, |
176 | | {0, NULL} |
177 | | }; |
178 | | |
179 | | /* String table for the V4 reply status messages */ |
180 | | |
181 | | static const value_string reply_table_v4[] = { |
182 | | {90, "Granted"}, |
183 | | {91, "Rejected or Failed"}, |
184 | | {92, "Rejected because SOCKS server cannot connect to identd on the client"}, |
185 | | {93, "Rejected because the client program and identd report different user-ids"}, |
186 | | {0, NULL} |
187 | | }; |
188 | | |
189 | | /* String table for the V5 reply status messages */ |
190 | | |
191 | | static const value_string reply_table_v5[] = { |
192 | | {0, "Succeeded"}, |
193 | | {1, "General SOCKS server failure"}, |
194 | | {2, "Connection not allowed by ruleset"}, |
195 | | {3, "Network unreachable"}, |
196 | | {4, "Host unreachable"}, |
197 | | {5, "Connection refused"}, |
198 | | {6, "TTL expired"}, |
199 | | {7, "Command not supported"}, |
200 | | {8, "Address type not supported"}, |
201 | | {0, NULL}, |
202 | | }; |
203 | | |
204 | | static const value_string cmd_strings[] = { |
205 | | {CONNECT_COMMAND, "Connect"}, |
206 | | {BIND_COMMAND, "Bind"}, |
207 | | {UDP_ASSOCIATE_COMMAND, "UdpAssociate"}, |
208 | | {PING_COMMAND, "Ping"}, |
209 | | {TRACERT_COMMAND, "Traceroute"}, |
210 | | {0, NULL} |
211 | | }; |
212 | | |
213 | | static const value_string gssapi_command_table[] = { |
214 | | { 1, "Authentication" }, |
215 | | { 0xFF, "Failure" }, |
216 | | { 0, NULL } |
217 | | }; |
218 | | |
219 | | |
220 | | /************************* Support routines ***************************/ |
221 | | |
222 | 2.67k | static const char *get_auth_method_name( unsigned Number){ |
223 | | |
224 | | /* return the name of the authentication method */ |
225 | | |
226 | 2.67k | if ( Number == 0) return "No authentication"; |
227 | 2.15k | if ( Number == 1) return "GSSAPI"; |
228 | 2.03k | if ( Number == 2) return "Username/Password"; |
229 | 1.91k | if ( Number == 3) return "Chap"; |
230 | 1.86k | if (( Number >= 4) && ( Number <= 0x7f))return "IANA assigned"; |
231 | 800 | if (( Number >= 0x80) && ( Number <= 0xfe)) return "private method"; |
232 | 337 | if ( Number == 0xff) return "no acceptable method"; |
233 | | |
234 | | /* shouldn't reach here */ |
235 | | |
236 | 0 | return "Bad method number (not 0-0xff)"; |
237 | 337 | } |
238 | | |
239 | 1 | static int display_address(packet_info *pinfo, tvbuff_t *tvb, int offset, proto_tree *tree) { |
240 | | |
241 | | /* decode and display the v5 address, return offset of next byte */ |
242 | | |
243 | 1 | int a_type = tvb_get_uint8(tvb, offset); |
244 | | |
245 | 1 | proto_tree_add_item( tree, hf_socks_address_type, tvb, offset, 1, ENC_BIG_ENDIAN); |
246 | 1 | offset += 1; |
247 | | |
248 | 1 | switch (a_type) |
249 | 1 | { |
250 | 0 | case 1: /* IPv4 address */ |
251 | 0 | proto_tree_add_item( tree, hf_socks_ip_dst, tvb, offset, 4, ENC_BIG_ENDIAN); |
252 | 0 | offset += 4; |
253 | 0 | break; |
254 | 0 | case 3: /* domain name address */ |
255 | 0 | { |
256 | 0 | uint8_t len; |
257 | 0 | char* str; |
258 | |
|
259 | 0 | len = tvb_get_uint8(tvb, offset); |
260 | 0 | str = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset+1, len, ENC_ASCII); |
261 | 0 | proto_tree_add_string(tree, hf_socks_remote_name, tvb, offset, len+1, str); |
262 | 0 | offset += (len+1); |
263 | 0 | } |
264 | 0 | break; |
265 | 0 | case 4: /* IPv6 address */ |
266 | 0 | proto_tree_add_item( tree, hf_socks_ip6_dst, tvb, offset, 16, ENC_NA); |
267 | 0 | offset += 16; |
268 | 0 | break; |
269 | 1 | } |
270 | | |
271 | 1 | return offset; |
272 | 1 | } |
273 | | |
274 | | |
275 | | static int get_address_v5(tvbuff_t *tvb, int offset, |
276 | 2 | socks_hash_entry_t *hash_info) { |
277 | | |
278 | | /* decode the v5 address and return offset of next byte */ |
279 | 2 | int a_type; |
280 | 2 | address addr; |
281 | | |
282 | 2 | a_type = tvb_get_uint8(tvb, offset); |
283 | 2 | offset += 1; |
284 | | |
285 | 2 | switch(a_type) |
286 | 2 | { |
287 | 0 | case 1: /* IPv4 address */ |
288 | 0 | if ( hash_info) { |
289 | 0 | set_address_tvb(&addr, AT_IPv4, 4, tvb, offset); |
290 | 0 | copy_address_wmem(wmem_file_scope(), &hash_info->dst_addr, &addr); |
291 | 0 | } |
292 | 0 | offset += 4; |
293 | 0 | break; |
294 | | |
295 | 0 | case 4: /* IPv6 address */ |
296 | 0 | if ( hash_info) { |
297 | 0 | set_address_tvb(&addr, AT_IPv6, 16, tvb, offset); |
298 | 0 | copy_address_wmem(wmem_file_scope(), &hash_info->dst_addr, &addr); |
299 | 0 | } |
300 | 0 | offset += 16; |
301 | 0 | break; |
302 | | |
303 | 0 | case 3: /* domain name address */ |
304 | 0 | offset += tvb_get_uint8(tvb, offset) + 1; |
305 | 0 | break; |
306 | 2 | } |
307 | | |
308 | 2 | return offset; |
309 | 2 | } |
310 | | |
311 | | |
312 | | /********************* V5 UDP Associate handlers ***********************/ |
313 | | |
314 | | static int |
315 | 0 | socks_udp_dissector(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { |
316 | | |
317 | | /* Conversation dissector called from UDP dissector. Decode and display */ |
318 | | /* the socks header, the pass the rest of the data to the udp port */ |
319 | | /* decode routine to handle the payload. */ |
320 | |
|
321 | 0 | int offset = 0; |
322 | 0 | uint32_t *ptr; |
323 | 0 | socks_hash_entry_t *hash_info; |
324 | 0 | conversation_t *conversation; |
325 | 0 | proto_tree *socks_tree; |
326 | 0 | proto_item *ti; |
327 | |
|
328 | 0 | conversation = find_conversation_pinfo( pinfo, 0); |
329 | |
|
330 | 0 | DISSECTOR_ASSERT( conversation); /* should always find a conversation */ |
331 | |
|
332 | 0 | hash_info = (socks_hash_entry_t *)conversation_get_proto_data(conversation, proto_socks); |
333 | |
|
334 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "Socks"); |
335 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "Version: 5, UDP Associated packet"); |
336 | |
|
337 | 0 | if ( tree) { |
338 | 0 | ti = proto_tree_add_protocol_format( tree, proto_socks, tvb, offset, -1, "Socks" ); |
339 | |
|
340 | 0 | socks_tree = proto_item_add_subtree(ti, ett_socks); |
341 | |
|
342 | 0 | proto_tree_add_item(socks_tree, hf_socks_reserved2, tvb, offset, 2, ENC_BIG_ENDIAN); |
343 | 0 | offset += 2; |
344 | |
|
345 | 0 | proto_tree_add_item(socks_tree, hf_socks_fragment_number, tvb, offset, 1, ENC_BIG_ENDIAN); |
346 | 0 | offset += 1; |
347 | |
|
348 | 0 | offset = display_address(pinfo, tvb, offset, socks_tree); |
349 | 0 | hash_info->udp_remote_port = tvb_get_ntohs(tvb, offset); |
350 | |
|
351 | 0 | proto_tree_add_uint( socks_tree, hf_socks_dstport, tvb, |
352 | 0 | offset, 2, hash_info->udp_remote_port); |
353 | |
|
354 | 0 | offset += 2; |
355 | 0 | } |
356 | 0 | else { /* no tree, skip past the socks header */ |
357 | 0 | offset += 3; |
358 | 0 | offset = get_address_v5( tvb, offset, 0) + 2; |
359 | 0 | } |
360 | | |
361 | | /* set pi src/dst port and call the udp sub-dissector lookup */ |
362 | |
|
363 | 0 | if ( pinfo->srcport == hash_info->port) |
364 | 0 | ptr = &pinfo->destport; |
365 | 0 | else |
366 | 0 | ptr = &pinfo->srcport; |
367 | |
|
368 | 0 | *ptr = hash_info->udp_remote_port; |
369 | |
|
370 | 0 | decode_udp_ports( tvb, offset, pinfo, tree, pinfo->srcport, pinfo->destport, -1); |
371 | |
|
372 | 0 | *ptr = hash_info->udp_port; |
373 | 0 | return tvb_captured_length(tvb); |
374 | 0 | } |
375 | | |
376 | | |
377 | | static void |
378 | 0 | new_udp_conversation( socks_hash_entry_t *hash_info, packet_info *pinfo){ |
379 | |
|
380 | 0 | conversation_t *conversation = conversation_new( pinfo->num, &pinfo->src, &pinfo->dst, CONVERSATION_UDP, |
381 | 0 | hash_info->udp_port, hash_info->port, 0); |
382 | |
|
383 | 0 | DISSECTOR_ASSERT( conversation); |
384 | |
|
385 | 0 | conversation_add_proto_data(conversation, proto_socks, hash_info); |
386 | 0 | conversation_set_dissector(conversation, socks_udp_handle); |
387 | 0 | } |
388 | | |
389 | | static void |
390 | | save_client_state(packet_info *pinfo, enum ClientState state) |
391 | 26 | { |
392 | 26 | sock_state_t* state_info = (sock_state_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_socks, 0); |
393 | 26 | if ((state_info != NULL) && (state_info->client == clientNoInit)) { |
394 | 26 | state_info->client = state; |
395 | 26 | } |
396 | 26 | } |
397 | | |
398 | | static void |
399 | | save_server_state(packet_info *pinfo, enum ServerState state) |
400 | 26 | { |
401 | 26 | sock_state_t* state_info = (sock_state_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_socks, 0); |
402 | 26 | if ((state_info != NULL) && (state_info->server == serverNoInit)) { |
403 | 26 | state_info->server = state; |
404 | 26 | } |
405 | 26 | } |
406 | | |
407 | | |
408 | | /**************** Protocol Tree Display routines ******************/ |
409 | | |
410 | | static void |
411 | | display_socks_v4(tvbuff_t *tvb, int offset, packet_info *pinfo, |
412 | 1 | proto_tree *tree, socks_hash_entry_t *hash_info, sock_state_t* state_info) { |
413 | | |
414 | | |
415 | | /* Display the protocol tree for the V4 version. This routine uses the */ |
416 | | /* stored frame information to decide what to do with the row. */ |
417 | | |
418 | 1 | unsigned char ipaddr[4]; |
419 | 1 | unsigned str_len; |
420 | | |
421 | | /* Either there is an error, or we're done with the state machine |
422 | | (so there's nothing to display) */ |
423 | 1 | if (state_info == NULL) |
424 | 0 | return; |
425 | | |
426 | 1 | if (hash_info->server_port == pinfo->destport) { |
427 | | /* Client side */ |
428 | 1 | switch (state_info->client) |
429 | 1 | { |
430 | 1 | case clientStart: |
431 | 1 | proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, ENC_BIG_ENDIAN); |
432 | 1 | offset += 1; |
433 | | |
434 | 1 | proto_tree_add_item( tree, hf_socks_cmd, tvb, offset, 1, ENC_BIG_ENDIAN); |
435 | 1 | offset += 1; |
436 | | |
437 | | /* Do remote port */ |
438 | 1 | proto_tree_add_item( tree, hf_socks_dstport, tvb, offset, 2, ENC_BIG_ENDIAN); |
439 | 1 | offset += 2; |
440 | | |
441 | | /* Do destination address */ |
442 | 1 | tvb_memcpy(tvb, ipaddr, offset, 4); |
443 | 1 | proto_tree_add_item( tree, hf_socks_ip_dst, tvb, offset, 4, ENC_BIG_ENDIAN); |
444 | 1 | offset += 4; |
445 | | |
446 | | /* display user name */ |
447 | 1 | str_len = tvb_strsize(tvb, offset); |
448 | 1 | proto_tree_add_item( tree, hf_socks_username, tvb, offset, str_len, ENC_ASCII); |
449 | 1 | offset += str_len; |
450 | | |
451 | 1 | if ( ipaddr[0] == 0 && ipaddr[1] == 0 && |
452 | 0 | ipaddr[2] == 0 && ipaddr[3] != 0) { |
453 | | /* 0.0.0.x , where x!=0 means v4a support */ |
454 | 0 | str_len = tvb_strsize(tvb, offset); |
455 | 0 | proto_tree_add_item( tree, hf_v4a_dns_name, tvb, offset, str_len, ENC_ASCII); |
456 | 0 | } |
457 | 1 | break; |
458 | 0 | default: |
459 | 0 | break; |
460 | 1 | } |
461 | 1 | } else { |
462 | | /* Server side */ |
463 | 0 | switch (state_info->server) |
464 | 0 | { |
465 | 0 | case serverStart: |
466 | 0 | proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, ENC_BIG_ENDIAN); |
467 | 0 | offset += 1; |
468 | | /* Do results code */ |
469 | 0 | proto_tree_add_item( tree, hf_socks_results_4, tvb, offset, 1, ENC_BIG_ENDIAN); |
470 | 0 | offset += 1; |
471 | | |
472 | | /* Do remote port */ |
473 | 0 | proto_tree_add_item( tree, hf_socks_dstport, tvb, offset, 2, ENC_BIG_ENDIAN); |
474 | 0 | offset += 2; |
475 | | /* Do remote address */ |
476 | 0 | proto_tree_add_item( tree, hf_socks_ip_dst, tvb, offset, 4, ENC_BIG_ENDIAN); |
477 | 0 | break; |
478 | 0 | default: |
479 | 0 | break; |
480 | 0 | } |
481 | 0 | } |
482 | 1 | } |
483 | | |
484 | | static void |
485 | | // NOLINTNEXTLINE(misc-no-recursion) |
486 | | client_display_socks_v5(tvbuff_t *tvb, int offset, packet_info *pinfo, |
487 | 26 | proto_tree *tree, socks_hash_entry_t *hash_info, sock_state_t* state_info) { |
488 | | |
489 | | /* Display the protocol tree for the version. This routine uses the */ |
490 | | /* stored conversation information to decide what to do with the row. */ |
491 | | /* Per packet information would have been better to do this, but we */ |
492 | | /* didn't have that when I wrote this. And I didn't expect this to get */ |
493 | | /* so messy. */ |
494 | | |
495 | 26 | unsigned int i; |
496 | 26 | const char *AuthMethodStr; |
497 | 26 | sock_state_t new_state_info; |
498 | 26 | proto_item *ti; |
499 | | |
500 | | /* Either there is an error, or we're done with the state machine |
501 | | (so there's nothing to display) */ |
502 | 26 | if (state_info == NULL) |
503 | 0 | return; |
504 | | |
505 | 26 | if (state_info->client == clientStart) |
506 | 24 | { |
507 | 24 | proto_tree *AuthTree; |
508 | 24 | uint8_t num_auth_methods, auth; |
509 | | |
510 | 24 | col_append_str(pinfo->cinfo, COL_INFO, " Connect to server request"); |
511 | | |
512 | 24 | proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, ENC_BIG_ENDIAN); |
513 | 24 | offset += 1; |
514 | | |
515 | 24 | AuthTree = proto_tree_add_subtree( tree, tvb, offset, -1, ett_socks_auth, &ti, "Client Authentication Methods"); |
516 | | |
517 | 24 | num_auth_methods = tvb_get_uint8(tvb, offset); |
518 | 24 | proto_item_set_len(ti, num_auth_methods+1); |
519 | | |
520 | 24 | proto_tree_add_item( AuthTree, hf_client_auth_method_count, tvb, offset, 1, ENC_BIG_ENDIAN); |
521 | 24 | offset += 1; |
522 | | |
523 | 2.71k | for( i = 0; i < num_auth_methods; ++i) { |
524 | 2.68k | auth = tvb_get_uint8( tvb, offset); |
525 | 2.68k | AuthMethodStr = get_auth_method_name(auth); |
526 | | |
527 | 2.68k | proto_tree_add_uint_format(AuthTree, hf_client_auth_method, tvb, offset, 1, auth, |
528 | 2.68k | "Method[%u]: %u (%s)", i, auth, AuthMethodStr); |
529 | 2.68k | offset += 1; |
530 | 2.68k | } |
531 | | |
532 | 24 | if ((num_auth_methods == 1) && |
533 | 1 | (tvb_bytes_exist(tvb, offset + 2, 1)) && |
534 | 1 | (tvb_get_uint8(tvb, offset + 2) == 0) && |
535 | 1 | (tvb_reported_length_remaining(tvb, offset + 2 + num_auth_methods) > 0)) { |
536 | 1 | new_state_info.client = clientV5Command; |
537 | 1 | increment_dissection_depth(pinfo); |
538 | 1 | client_display_socks_v5(tvb, offset, pinfo, tree, hash_info, &new_state_info); |
539 | 1 | decrement_dissection_depth(pinfo); |
540 | 1 | } |
541 | 24 | } |
542 | 2 | else if (state_info->client == clientV5Command) { |
543 | 1 | col_append_fstr(pinfo->cinfo, COL_INFO, " Command Request - %s", |
544 | 1 | val_to_str_const(hash_info->command, cmd_strings, "Unknown")); |
545 | | |
546 | 1 | proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, ENC_BIG_ENDIAN); |
547 | 1 | offset += 1; |
548 | | |
549 | 1 | proto_tree_add_item( tree, hf_socks_cmd, tvb, offset, 1, ENC_BIG_ENDIAN); |
550 | 1 | offset += 1; |
551 | | |
552 | 1 | proto_tree_add_item( tree, hf_socks_reserved, tvb, offset, 1, ENC_BIG_ENDIAN); |
553 | 1 | offset += 1; |
554 | | |
555 | 1 | offset = display_address(pinfo, tvb, offset, tree); |
556 | 1 | proto_tree_add_item( tree, hf_client_port, tvb, offset, 2, ENC_BIG_ENDIAN); |
557 | 1 | } |
558 | 1 | else if ((state_info->client == clientWaitForAuthReply) && |
559 | 1 | (state_info->server == serverInitReply)) { |
560 | 0 | uint16_t len; |
561 | 0 | char* str; |
562 | |
|
563 | 0 | ti = proto_tree_add_uint( tree, hf_socks_ver, tvb, offset, 0, 5); |
564 | 0 | proto_item_set_generated(ti); |
565 | |
|
566 | 0 | proto_tree_add_item( tree, hf_socks_subnegotiation_version, tvb, offset, 1, ENC_BIG_ENDIAN); |
567 | 0 | offset += 1; |
568 | |
|
569 | 0 | switch(hash_info->authentication_method) |
570 | 0 | { |
571 | 0 | case NO_AUTHENTICATION: |
572 | 0 | break; |
573 | 0 | case USER_NAME_AUTHENTICATION: |
574 | 0 | col_append_str(pinfo->cinfo, COL_INFO, " User authentication request"); |
575 | | |
576 | | /* process user name */ |
577 | 0 | len = tvb_get_uint8(tvb, offset); |
578 | 0 | str = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset+1, len, ENC_ASCII); |
579 | 0 | proto_tree_add_string(tree, hf_socks_username, tvb, offset, len+1, str); |
580 | 0 | offset += (len+1); |
581 | |
|
582 | 0 | len = tvb_get_uint8(tvb, offset); |
583 | 0 | str = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset+1, len, ENC_ASCII); |
584 | 0 | proto_tree_add_string(tree, hf_socks_password, tvb, offset, len+1, str); |
585 | | /* offset += (len+1); */ |
586 | 0 | break; |
587 | 0 | case GSS_API_AUTHENTICATION: |
588 | 0 | col_append_str(pinfo->cinfo, COL_INFO, " GSSAPI authentication request"); |
589 | |
|
590 | 0 | proto_tree_add_item( tree, hf_gssapi_command, tvb, offset, 1, ENC_BIG_ENDIAN); |
591 | 0 | proto_tree_add_item( tree, hf_gssapi_length, tvb, offset+1, 2, ENC_BIG_ENDIAN); |
592 | 0 | len = tvb_get_ntohs(tvb, offset+1); |
593 | 0 | if (len > 0) |
594 | 0 | proto_tree_add_item( tree, hf_gssapi_payload, tvb, offset+3, len, ENC_NA); |
595 | 0 | break; |
596 | 0 | default: |
597 | 0 | break; |
598 | 0 | } |
599 | 0 | } |
600 | 1 | else { |
601 | 1 | if (hash_info->port != 0) |
602 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, ", Remote Port: %u", |
603 | 0 | hash_info->port); |
604 | 1 | } |
605 | 26 | } |
606 | | |
607 | | static void |
608 | | server_display_socks_v5(tvbuff_t *tvb, int offset, packet_info *pinfo, |
609 | 0 | proto_tree *tree, socks_hash_entry_t *hash_info _U_, sock_state_t* state_info) { |
610 | | |
611 | | /* Display the protocol tree for the version. This routine uses the */ |
612 | | /* stored conversation information to decide what to do with the row. */ |
613 | | /* Per packet information would have been better to do this, but we */ |
614 | | /* didn't have that when I wrote this. And I didn't expect this to get */ |
615 | | /* so messy. */ |
616 | |
|
617 | 0 | const char *AuthMethodStr; |
618 | 0 | uint8_t auth, auth_status; |
619 | 0 | proto_item *ti; |
620 | | |
621 | | /* Either there is an error, or we're done with the state machine |
622 | | (so there's nothing to display) */ |
623 | 0 | if (state_info == NULL) |
624 | 0 | return; |
625 | | |
626 | 0 | switch(state_info->server) |
627 | 0 | { |
628 | 0 | case serverStart: |
629 | 0 | col_append_str(pinfo->cinfo, COL_INFO, " Connect to server response"); |
630 | |
|
631 | 0 | proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, ENC_BIG_ENDIAN); |
632 | 0 | offset += 1; |
633 | |
|
634 | 0 | auth = tvb_get_uint8( tvb, offset); |
635 | 0 | AuthMethodStr = get_auth_method_name(auth); |
636 | |
|
637 | 0 | proto_tree_add_uint_format_value(tree, hf_server_accepted_auth_method, tvb, offset, 1, auth, |
638 | 0 | "0x%0x (%s)", auth, AuthMethodStr); |
639 | 0 | break; |
640 | | |
641 | 0 | case serverUserReply: |
642 | 0 | col_append_str(pinfo->cinfo, COL_INFO, " User authentication reply"); |
643 | |
|
644 | 0 | ti = proto_tree_add_uint( tree, hf_socks_ver, tvb, offset, 0, 5); |
645 | 0 | proto_item_set_generated(ti); |
646 | |
|
647 | 0 | proto_tree_add_item( tree, hf_socks_subnegotiation_version, tvb, offset, 1, ENC_BIG_ENDIAN); |
648 | 0 | offset += 1; |
649 | |
|
650 | 0 | ti = proto_tree_add_item_ret_uint8(tree, hf_server_auth_status, tvb, offset, 1, ENC_BIG_ENDIAN, &auth_status); |
651 | 0 | if(auth_status != 0) |
652 | 0 | proto_item_append_text(ti, " (failure)"); |
653 | 0 | else |
654 | 0 | proto_item_append_text(ti, " (success)"); |
655 | 0 | break; |
656 | | |
657 | 0 | case serverGssApiReply: |
658 | 0 | col_append_str(pinfo->cinfo, COL_INFO, " GSSAPI authentication reply"); |
659 | |
|
660 | 0 | ti = proto_tree_add_uint( tree, hf_socks_ver, tvb, offset, 0, 5); |
661 | 0 | proto_item_set_generated(ti); |
662 | |
|
663 | 0 | proto_tree_add_item( tree, hf_socks_subnegotiation_version, tvb, offset, 1, ENC_BIG_ENDIAN); |
664 | 0 | offset += 1; |
665 | |
|
666 | 0 | auth_status = tvb_get_uint8(tvb, offset); |
667 | 0 | proto_tree_add_item( tree, hf_gssapi_command, tvb, offset, 1, ENC_BIG_ENDIAN); |
668 | 0 | if (auth_status != 0xFF) { |
669 | 0 | uint16_t len; |
670 | |
|
671 | 0 | proto_tree_add_item( tree, hf_gssapi_length, tvb, offset+1, 2, ENC_BIG_ENDIAN); |
672 | 0 | len = tvb_get_ntohs(tvb, offset+1); |
673 | 0 | if (len > 0) |
674 | 0 | proto_tree_add_item( tree, hf_gssapi_payload, tvb, offset+3, len, ENC_NA); |
675 | 0 | } |
676 | 0 | break; |
677 | | |
678 | 0 | case serverCommandReply: |
679 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " Command Response - %s", |
680 | 0 | val_to_str_const(hash_info->command, cmd_strings, "Unknown")); |
681 | |
|
682 | 0 | proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, ENC_BIG_ENDIAN); |
683 | 0 | offset += 1; |
684 | |
|
685 | 0 | proto_tree_add_item( tree, hf_socks_results_5, tvb, offset, 1, ENC_BIG_ENDIAN); |
686 | 0 | offset += 1; |
687 | |
|
688 | 0 | proto_tree_add_item( tree, hf_socks_reserved, tvb, offset, 1, ENC_BIG_ENDIAN); |
689 | 0 | offset += 1; |
690 | |
|
691 | 0 | offset = display_address(pinfo, tvb, offset, tree); |
692 | 0 | proto_tree_add_item( tree, hf_client_port, tvb, offset, 2, ENC_BIG_ENDIAN); |
693 | 0 | break; |
694 | | |
695 | 0 | case serverBindReply: |
696 | 0 | col_append_str(pinfo->cinfo, COL_INFO, " Command Response: Bind remote host info"); |
697 | |
|
698 | 0 | proto_tree_add_item( tree, hf_socks_ver, tvb, offset, 1, ENC_BIG_ENDIAN); |
699 | 0 | offset += 1; |
700 | |
|
701 | 0 | proto_tree_add_item( tree, hf_socks_results_5, tvb, offset, 1, ENC_BIG_ENDIAN); |
702 | 0 | offset += 1; |
703 | |
|
704 | 0 | proto_tree_add_item( tree, hf_socks_reserved, tvb, offset, 1, ENC_BIG_ENDIAN); |
705 | 0 | offset += 1; |
706 | |
|
707 | 0 | offset = display_address(pinfo, tvb, offset, tree); |
708 | 0 | proto_tree_add_item( tree, hf_server_remote_host_port, tvb, offset, 2, ENC_BIG_ENDIAN); |
709 | 0 | break; |
710 | | |
711 | 0 | default: |
712 | 0 | if ( hash_info->port != 0) |
713 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, ", Remote Port: %u", |
714 | 0 | hash_info->port); |
715 | |
|
716 | 0 | break; |
717 | 0 | } |
718 | 0 | } |
719 | | |
720 | | |
721 | | /**************** Decoder State Machines ******************/ |
722 | | |
723 | | |
724 | | static void |
725 | | state_machine_v4( socks_hash_entry_t *hash_info, tvbuff_t *tvb, |
726 | 1 | int offset, packet_info *pinfo) { |
727 | | |
728 | | /* Decode V4 protocol. This is done on the first pass through the */ |
729 | | /* list. Based upon the current state, decode the packet and determine */ |
730 | | /* what the next state should be. */ |
731 | 1 | address addr; |
732 | | |
733 | 1 | if (hash_info->clientState != clientDone) |
734 | 1 | save_client_state(pinfo, hash_info->clientState); |
735 | | |
736 | 1 | if (hash_info->serverState != serverDone) |
737 | 1 | save_server_state(pinfo, hash_info->serverState); |
738 | | |
739 | 1 | if (hash_info->server_port == pinfo->destport) { |
740 | | /* Client side, only a single request */ |
741 | 1 | col_append_str(pinfo->cinfo, COL_INFO, " Connect to server request"); |
742 | | |
743 | 1 | hash_info->command = tvb_get_uint8(tvb, offset + 1); |
744 | | |
745 | | /* get remote port */ |
746 | 1 | if ( hash_info->command == CONNECT_COMMAND) |
747 | 0 | hash_info->port = tvb_get_ntohs(tvb, offset + 2); |
748 | | |
749 | | /* get remote address */ |
750 | 1 | set_address_tvb(&addr, AT_IPv4, 4, tvb, offset); |
751 | 1 | copy_address_wmem(wmem_file_scope(), &hash_info->dst_addr, &addr); |
752 | | |
753 | 1 | hash_info->clientState = clientDone; |
754 | 1 | } |
755 | 0 | else { |
756 | 0 | col_append_str(pinfo->cinfo, COL_INFO, " Connect Response"); |
757 | |
|
758 | 0 | if (tvb_get_uint8(tvb, offset + 1) == 90) |
759 | 0 | hash_info->serverState = serverDone; |
760 | 0 | else |
761 | 0 | hash_info->serverState = serverError; |
762 | 0 | } |
763 | 1 | } |
764 | | |
765 | | static void |
766 | | // NOLINTNEXTLINE(misc-no-recursion) |
767 | | client_state_machine_v5( socks_hash_entry_t *hash_info, tvbuff_t *tvb, |
768 | 27 | int offset, packet_info *pinfo, bool start_of_frame) { |
769 | | |
770 | | /* Decode client side of V5 protocol. This is done on the first pass through the */ |
771 | | /* list. Based upon the current state, decode the packet and determine */ |
772 | | /* what the next state should be. */ |
773 | | |
774 | 27 | if (start_of_frame) { |
775 | 25 | save_client_state(pinfo, hash_info->clientState); |
776 | 25 | save_server_state(pinfo, hash_info->serverState); |
777 | 25 | } |
778 | | |
779 | 27 | if (hash_info->clientState == clientStart) |
780 | 24 | { |
781 | 24 | uint8_t num_auth_methods; |
782 | | |
783 | 24 | num_auth_methods = tvb_get_uint8(tvb, offset + 1); |
784 | | /* skip past auth methods */ |
785 | | |
786 | 24 | if ((num_auth_methods == 0) || |
787 | 23 | ((num_auth_methods == 1) && |
788 | 2 | (tvb_get_uint8(tvb, offset + 2) == 0))) { |
789 | | /* No authentication needed */ |
790 | 2 | hash_info->clientState = clientV5Command; |
791 | 2 | if (tvb_reported_length_remaining(tvb, offset + 2 + num_auth_methods) > 0) { |
792 | 2 | increment_dissection_depth(pinfo); |
793 | 2 | client_state_machine_v5(hash_info, tvb, offset + 2 + num_auth_methods, pinfo, false); |
794 | 2 | decrement_dissection_depth(pinfo); |
795 | 2 | } |
796 | 22 | } else { |
797 | 22 | hash_info->clientState = clientWaitForAuthReply; |
798 | 22 | } |
799 | 24 | } else if ((hash_info->clientState == clientWaitForAuthReply) && |
800 | 1 | (hash_info->serverState == serverInitReply)) { |
801 | |
|
802 | 0 | switch(hash_info->authentication_method) |
803 | 0 | { |
804 | 0 | case NO_AUTHENTICATION: |
805 | 0 | hash_info->clientState = clientV5Command; |
806 | 0 | hash_info->serverState = serverCommandReply; |
807 | 0 | break; |
808 | 0 | case USER_NAME_AUTHENTICATION: |
809 | 0 | hash_info->clientState = clientV5Command; |
810 | 0 | hash_info->serverState = serverUserReply; |
811 | 0 | break; |
812 | 0 | case GSS_API_AUTHENTICATION: |
813 | 0 | hash_info->clientState = clientV5Command; |
814 | 0 | hash_info->serverState = serverGssApiReply; |
815 | 0 | break; |
816 | 0 | default: |
817 | 0 | hash_info->clientState = clientError; /*Auth failed or error*/ |
818 | 0 | break; |
819 | 0 | } |
820 | 3 | } else if (hash_info->clientState == clientV5Command) { |
821 | 2 | hash_info->command = tvb_get_uint8(tvb, offset + 1); /* get command */ |
822 | | |
823 | 2 | offset += 3; /* skip to address type */ |
824 | | |
825 | 2 | offset = get_address_v5(tvb, offset, hash_info); |
826 | | |
827 | | /** temp = tvb_get_uint8(tvb, offset); XX: what was this for ? **/ |
828 | | |
829 | 2 | if (( hash_info->command == CONNECT_COMMAND) || |
830 | 2 | ( hash_info->command == UDP_ASSOCIATE_COMMAND)) |
831 | | /* get remote port */ |
832 | 0 | hash_info->port = tvb_get_ntohs(tvb, offset); |
833 | | |
834 | 2 | hash_info->clientState = clientDone; |
835 | 2 | } |
836 | 27 | } |
837 | | |
838 | | static void |
839 | | server_state_machine_v5( socks_hash_entry_t *hash_info, tvbuff_t *tvb, |
840 | 0 | int offset, packet_info *pinfo, bool start_of_frame) { |
841 | | |
842 | | /* Decode server side of V5 protocol. This is done on the first pass through the */ |
843 | | /* list. Based upon the current state, decode the packet and determine */ |
844 | | /* what the next state should be. */ |
845 | |
|
846 | 0 | if (start_of_frame) |
847 | 0 | save_server_state(pinfo, hash_info->serverState); |
848 | |
|
849 | 0 | switch (hash_info->serverState) { |
850 | 0 | case serverStart: |
851 | 0 | hash_info->authentication_method = tvb_get_uint8(tvb, offset + 1); |
852 | 0 | switch (hash_info->authentication_method) |
853 | 0 | { |
854 | 0 | case NO_AUTHENTICATION: |
855 | | /* If there is no authentication, client should expect command immediately */ |
856 | 0 | hash_info->serverState = serverCommandReply; |
857 | 0 | hash_info->clientState = clientV5Command; |
858 | 0 | break; |
859 | 0 | case USER_NAME_AUTHENTICATION: |
860 | 0 | hash_info->serverState = serverInitReply; |
861 | 0 | break; |
862 | 0 | case GSS_API_AUTHENTICATION: |
863 | 0 | hash_info->serverState = serverInitReply; |
864 | 0 | break; |
865 | 0 | default: |
866 | 0 | hash_info->serverState = serverError; |
867 | 0 | break; |
868 | 0 | } |
869 | 0 | break; |
870 | 0 | case serverUserReply: |
871 | 0 | hash_info->serverState = serverCommandReply; |
872 | 0 | break; |
873 | 0 | case serverGssApiReply: |
874 | 0 | if (tvb_get_uint8(tvb, offset+1) == 0xFF) { |
875 | 0 | hash_info->serverState = serverError; |
876 | 0 | } else { |
877 | 0 | if (tvb_get_ntohs(tvb, offset+2) == 0) |
878 | 0 | hash_info->serverState = serverCommandReply; |
879 | 0 | } |
880 | 0 | break; |
881 | 0 | case serverCommandReply: |
882 | 0 | switch(hash_info->command) |
883 | 0 | { |
884 | 0 | case CONNECT_COMMAND: |
885 | 0 | case PING_COMMAND: |
886 | 0 | case TRACERT_COMMAND: |
887 | 0 | hash_info->serverState = serverDone; |
888 | 0 | break; |
889 | | |
890 | 0 | case BIND_COMMAND: |
891 | 0 | hash_info->serverState = serverBindReply; |
892 | 0 | if ((tvb_get_uint8(tvb, offset + 2) == 0) && |
893 | 0 | (tvb_reported_length_remaining(tvb, offset) > 5)) { |
894 | 0 | offset = display_address(pinfo, tvb, offset, NULL); |
895 | 0 | client_state_machine_v5(hash_info, tvb, offset, pinfo, false); |
896 | 0 | } |
897 | 0 | break; |
898 | | |
899 | 0 | case UDP_ASSOCIATE_COMMAND: |
900 | 0 | offset += 3; /* skip to address type */ |
901 | 0 | offset = get_address_v5(tvb, offset, hash_info); |
902 | | |
903 | | /* save server udp port and create udp conversation */ |
904 | 0 | hash_info->udp_port = tvb_get_ntohs(tvb, offset); |
905 | |
|
906 | 0 | if (!pinfo->fd->visited) |
907 | 0 | new_udp_conversation( hash_info, pinfo); |
908 | |
|
909 | 0 | break; |
910 | 0 | } |
911 | 0 | break; |
912 | 0 | case serverBindReply: |
913 | 0 | break; |
914 | 0 | default: |
915 | 0 | break; |
916 | 0 | } |
917 | 0 | } |
918 | | |
919 | | |
920 | | static void |
921 | 0 | display_ping_and_tracert(tvbuff_t *tvb, unsigned offset, packet_info *pinfo, proto_tree *tree, socks_hash_entry_t *hash_info) { |
922 | | |
923 | | /* Display the ping/trace_route conversation */ |
924 | |
|
925 | 0 | unsigned linelen; |
926 | | |
927 | | /* handle the end command */ |
928 | 0 | if ( pinfo->destport == pinfo->match_uint){ |
929 | 0 | col_append_str(pinfo->cinfo, COL_INFO, ", Terminate Request"); |
930 | |
|
931 | 0 | proto_tree_add_item(tree, (hash_info->command == PING_COMMAND) ? hf_socks_ping_end_command : hf_socks_traceroute_end_command, tvb, offset, 1, ENC_NA); |
932 | 0 | } |
933 | 0 | else { /* display the PING or Traceroute results */ |
934 | 0 | col_append_str(pinfo->cinfo, COL_INFO, ", Results"); |
935 | |
|
936 | 0 | if ( tree){ |
937 | 0 | proto_tree_add_item(tree, (hash_info->command == PING_COMMAND) ? hf_socks_ping_results : hf_socks_traceroute_results, tvb, offset, -1, ENC_NA); |
938 | |
|
939 | 0 | while (tvb_captured_length_remaining(tvb, offset)) { |
940 | 0 | unsigned next_offset; |
941 | 0 | tvb_find_line_end_remaining(tvb, offset, NULL, &next_offset); |
942 | | /* Use the linelen including the line terminator. */ |
943 | 0 | linelen = next_offset - offset; |
944 | |
|
945 | 0 | proto_tree_add_format_text( tree, tvb, offset, linelen); |
946 | 0 | offset = next_offset; |
947 | 0 | } |
948 | 0 | } |
949 | 0 | } |
950 | 0 | } |
951 | | |
952 | | static void clear_in_socks_dissector_flag(void *s) |
953 | 0 | { |
954 | 0 | sock_state_t* state_info = (sock_state_t*)s; |
955 | 0 | state_info->in_socks_dissector_flag = 0; /* avoid recursive overflow */ |
956 | 0 | } |
957 | | |
958 | | static void call_next_dissector(tvbuff_t *tvb, int offset, packet_info *pinfo, |
959 | | proto_tree *tree, proto_tree *socks_tree, |
960 | | socks_hash_entry_t *hash_info, sock_state_t* state_info, struct tcpinfo *tcpinfo) |
961 | 0 | { |
962 | | |
963 | | /* Display the results for PING and TRACERT extensions or */ |
964 | | /* Call TCP dissector for the port that was passed during the */ |
965 | | /* connect process */ |
966 | | /* Load pointer to pinfo->XXXport depending upon the direction, */ |
967 | | /* change pinfo port to the remote port, call next dissector to decode */ |
968 | | /* the payload, and restore the pinfo port after that is done. */ |
969 | |
|
970 | 0 | uint32_t *ptr; |
971 | 0 | uint16_t save_port; |
972 | 0 | uint16_t save_can_desegment; |
973 | 0 | struct tcp_analysis *tcpd=NULL; |
974 | | |
975 | |
|
976 | 0 | if (( hash_info->command == PING_COMMAND) || |
977 | 0 | ( hash_info->command == TRACERT_COMMAND)) |
978 | | |
979 | 0 | display_ping_and_tracert(tvb, offset, pinfo, tree, hash_info); |
980 | | |
981 | 0 | else { /* call the tcp port decoder to handle the payload */ |
982 | | |
983 | | /*XXX may want to load dest address here */ |
984 | |
|
985 | 0 | if (pinfo->destport == pinfo->match_uint) { |
986 | 0 | ptr = &pinfo->destport; |
987 | 0 | } else { |
988 | 0 | ptr = &pinfo->srcport; |
989 | 0 | } |
990 | |
|
991 | 0 | save_port = *ptr; |
992 | 0 | *ptr = hash_info->port; |
993 | |
|
994 | 0 | if (hash_info->proxy_conv == NULL) { |
995 | 0 | hash_info->proxy_conv = conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, |
996 | 0 | CONVERSATION_TCP /* CONVERSATION_SOCKS? */, pinfo->srcport, pinfo->destport, 0); |
997 | 0 | } |
998 | |
|
999 | 0 | tcpd = get_tcp_conversation_data(hash_info->proxy_conv, pinfo); |
1000 | |
|
1001 | 0 | state_info->in_socks_dissector_flag = 1; /* avoid recursive overflow */ |
1002 | 0 | CLEANUP_PUSH(clear_in_socks_dissector_flag, state_info); |
1003 | |
|
1004 | 0 | save_can_desegment = pinfo->can_desegment; |
1005 | 0 | pinfo->can_desegment = pinfo->saved_can_desegment; |
1006 | 0 | dissect_tcp_payload(tvb, pinfo, offset, tcpinfo->seq, |
1007 | 0 | tcpinfo->nxtseq, pinfo->srcport, pinfo->destport, |
1008 | 0 | tree, socks_tree, tcpd, tcpinfo); |
1009 | 0 | pinfo->can_desegment = save_can_desegment; |
1010 | |
|
1011 | 0 | CLEANUP_CALL_AND_POP; |
1012 | |
|
1013 | 0 | *ptr = save_port; |
1014 | 0 | } |
1015 | 0 | } |
1016 | | |
1017 | | |
1018 | | |
1019 | | static int |
1020 | 27 | dissect_socks(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { |
1021 | | |
1022 | 27 | int offset = 0; |
1023 | 27 | proto_tree *socks_tree = NULL; |
1024 | 27 | proto_item *ti; |
1025 | 27 | socks_hash_entry_t *hash_info; |
1026 | 27 | conversation_t *conversation; |
1027 | 27 | sock_state_t* state_info; |
1028 | 27 | uint8_t version; |
1029 | 27 | struct tcpinfo *tcpinfo = (struct tcpinfo*)data; |
1030 | | |
1031 | 27 | state_info = (sock_state_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_socks, 0); |
1032 | 27 | if (state_info == NULL) { |
1033 | 27 | state_info = wmem_new(wmem_file_scope(), sock_state_t); |
1034 | 27 | state_info->in_socks_dissector_flag = 0; |
1035 | 27 | state_info->client = clientNoInit; |
1036 | 27 | state_info->server = serverNoInit; |
1037 | | |
1038 | 27 | p_add_proto_data(wmem_file_scope(), pinfo, proto_socks, 0, state_info); |
1039 | 27 | } |
1040 | | |
1041 | | /* avoid recursive overflow */ |
1042 | 27 | if (state_info->in_socks_dissector_flag) |
1043 | 0 | return 0; |
1044 | | |
1045 | 27 | conversation = find_conversation_pinfo(pinfo, 0); |
1046 | 27 | if (conversation == NULL) { |
1047 | | /* If we don't already have a conversation, make sure the first |
1048 | | byte is a valid version number */ |
1049 | 0 | version = tvb_get_uint8(tvb, offset); |
1050 | 0 | if ((version != 4) && (version != 5)) |
1051 | 0 | return 0; |
1052 | | |
1053 | 0 | conversation = conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, |
1054 | 0 | conversation_pt_to_conversation_type(pinfo->ptype), pinfo->srcport, pinfo->destport, 0); |
1055 | 0 | } |
1056 | | |
1057 | 27 | hash_info = (socks_hash_entry_t *)conversation_get_proto_data(conversation,proto_socks); |
1058 | 27 | if (hash_info == NULL){ |
1059 | 26 | hash_info = wmem_new0(wmem_file_scope(), socks_hash_entry_t); |
1060 | 26 | hash_info->start_done_frame = INT_MAX; |
1061 | 26 | hash_info->clientState = clientStart; |
1062 | 26 | hash_info->serverState = serverStart; |
1063 | | |
1064 | 26 | hash_info->server_port = pinfo->destport; |
1065 | 26 | hash_info->port = 0; |
1066 | 26 | hash_info->version = tvb_get_uint8(tvb, offset); /* get version*/ |
1067 | | |
1068 | 26 | conversation_add_proto_data(conversation, proto_socks, hash_info); |
1069 | | |
1070 | | /* set dissector for now */ |
1071 | 26 | if (conversation_get_dissector(conversation, pinfo->num) != NULL) { |
1072 | 0 | conversation_set_dissector(conversation, socks_handle); |
1073 | 0 | } |
1074 | 26 | } |
1075 | | |
1076 | | /* display summary window information */ |
1077 | 27 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "Socks"); |
1078 | | |
1079 | 27 | if (( hash_info->version == 4) || ( hash_info->version == 5)){ |
1080 | 26 | col_add_fstr(pinfo->cinfo, COL_INFO, "Version: %d", |
1081 | 26 | hash_info->version); |
1082 | 26 | } |
1083 | 1 | else /* unknown version display error */ |
1084 | 1 | col_set_str(pinfo->cinfo, COL_INFO, "Unknown"); |
1085 | | |
1086 | | |
1087 | 27 | if ( hash_info->command == PING_COMMAND) |
1088 | 0 | col_append_str(pinfo->cinfo, COL_INFO, ", Ping Req"); |
1089 | 27 | if ( hash_info->command == TRACERT_COMMAND) |
1090 | 0 | col_append_str(pinfo->cinfo, COL_INFO, ", Traceroute Req"); |
1091 | | |
1092 | | /* run state machine if needed */ |
1093 | 27 | if ((!pinfo->fd->visited) && |
1094 | 27 | (!((hash_info->clientState == clientDone) && |
1095 | 27 | (hash_info->serverState == serverDone)))) { |
1096 | | |
1097 | 27 | if (hash_info->server_port == pinfo->destport) { |
1098 | 27 | if ((hash_info->clientState != clientError) && |
1099 | 27 | (hash_info->clientState != clientDone)) |
1100 | 27 | { |
1101 | 27 | if ( hash_info->version == 4) { |
1102 | 1 | state_machine_v4( hash_info, tvb, offset, pinfo); |
1103 | 26 | } else if ( hash_info->version == 5) { |
1104 | 25 | client_state_machine_v5( hash_info, tvb, offset, pinfo, true); |
1105 | 25 | } |
1106 | 27 | } |
1107 | 27 | } else { |
1108 | 0 | if ((hash_info->serverState != serverError) && |
1109 | 0 | (hash_info->serverState != serverDone)) { |
1110 | 0 | if ( hash_info->version == 4) { |
1111 | 0 | state_machine_v4( hash_info, tvb, offset, pinfo); |
1112 | 0 | } else if ( hash_info->version == 5) { |
1113 | 0 | server_state_machine_v5( hash_info, tvb, offset, pinfo, true); |
1114 | 0 | } |
1115 | 0 | } |
1116 | 0 | } |
1117 | | |
1118 | 27 | if ((hash_info->clientState == clientDone) && |
1119 | 3 | (hash_info->serverState == serverDone)) { /* if done now */ |
1120 | 0 | hash_info->start_done_frame = pinfo->num; |
1121 | 0 | } |
1122 | 27 | } |
1123 | | |
1124 | | /* if proto tree, decode and display */ |
1125 | 27 | if (tree) { |
1126 | 27 | ti = proto_tree_add_item( tree, proto_socks, tvb, offset, -1, ENC_NA ); |
1127 | 27 | socks_tree = proto_item_add_subtree(ti, ett_socks); |
1128 | | |
1129 | | /* if past startup, add the faked stuff */ |
1130 | 27 | if ( pinfo->num > hash_info->start_done_frame){ |
1131 | | /* add info to tree */ |
1132 | 0 | ti = proto_tree_add_uint( socks_tree, hf_socks_ver, tvb, offset, 0, hash_info->version); |
1133 | 0 | proto_item_set_generated(ti); |
1134 | |
|
1135 | 0 | ti = proto_tree_add_uint( socks_tree, hf_socks_cmd, tvb, offset, 0, hash_info->command); |
1136 | 0 | proto_item_set_generated(ti); |
1137 | |
|
1138 | 0 | if (hash_info->dst_addr.type == AT_IPv4) { |
1139 | 0 | ti = proto_tree_add_ipv4( socks_tree, hf_socks_ip_dst, tvb, |
1140 | 0 | offset, 0, *((const uint32_t*)hash_info->dst_addr.data)); |
1141 | 0 | proto_item_set_generated(ti); |
1142 | 0 | } else if (hash_info->dst_addr.type == AT_IPv6) { |
1143 | 0 | ti = proto_tree_add_ipv6( socks_tree, hf_socks_ip6_dst, tvb, |
1144 | 0 | offset, 0, (const ws_in6_addr *)hash_info->dst_addr.data); |
1145 | 0 | proto_item_set_generated(ti); |
1146 | 0 | } |
1147 | | |
1148 | | /* no fake address for ping & traceroute */ |
1149 | |
|
1150 | 0 | if (( hash_info->command != PING_COMMAND) && |
1151 | 0 | ( hash_info->command != TRACERT_COMMAND)){ |
1152 | 0 | ti = proto_tree_add_uint( socks_tree, hf_socks_dstport, tvb, offset, 0, hash_info->port); |
1153 | 0 | proto_item_set_generated(ti); |
1154 | 0 | } |
1155 | 27 | } else { |
1156 | 27 | if (hash_info->server_port == pinfo->destport) { |
1157 | 27 | if ( hash_info->version == 4) { |
1158 | 1 | display_socks_v4(tvb, offset, pinfo, socks_tree, hash_info, state_info); |
1159 | 26 | } else if ( hash_info->version == 5) { |
1160 | 25 | client_display_socks_v5(tvb, offset, pinfo, socks_tree, hash_info, state_info); |
1161 | 25 | } |
1162 | 27 | } else { |
1163 | 0 | if ( hash_info->version == 4) { |
1164 | 0 | display_socks_v4(tvb, offset, pinfo, socks_tree, hash_info, state_info); |
1165 | 0 | } else if ( hash_info->version == 5) { |
1166 | 0 | server_display_socks_v5(tvb, offset, pinfo, socks_tree, hash_info, state_info); |
1167 | 0 | } |
1168 | 0 | } |
1169 | 27 | } |
1170 | | |
1171 | 27 | } |
1172 | | |
1173 | | |
1174 | | /* call next dissector if ready */ |
1175 | 27 | if ( pinfo->num > hash_info->start_done_frame){ |
1176 | 0 | call_next_dissector(tvb, offset, pinfo, tree, socks_tree, |
1177 | 0 | hash_info, state_info, tcpinfo); |
1178 | 0 | } |
1179 | | |
1180 | 27 | return tvb_reported_length(tvb); |
1181 | 27 | } |
1182 | | |
1183 | | |
1184 | | static int |
1185 | 0 | dissect_socks_tls(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { |
1186 | 0 | if (data != NULL) { |
1187 | 0 | return dissect_socks(tvb, pinfo, tree, data); |
1188 | 0 | } else { |
1189 | | /* lets fake a tcpinfo, which TLS does not give us */ |
1190 | 0 | struct tcpinfo tmp; |
1191 | 0 | tmp.flags = 0; |
1192 | 0 | tmp.is_reassembled = false; |
1193 | 0 | tmp.lastackseq = 0; |
1194 | 0 | tmp.nxtseq = 0; |
1195 | 0 | tmp.seq = 0; |
1196 | 0 | tmp.urgent_pointer = 0; |
1197 | 0 | return dissect_socks(tvb, pinfo, tree, &tmp); |
1198 | 0 | } |
1199 | 0 | } |
1200 | | |
1201 | | void |
1202 | 16 | proto_register_socks( void){ |
1203 | | |
1204 | 16 | static int *ett[] = { |
1205 | 16 | &ett_socks, |
1206 | 16 | &ett_socks_auth, |
1207 | 16 | &ett_socks_name |
1208 | 16 | }; |
1209 | | |
1210 | 16 | static hf_register_info hf[] = { |
1211 | | |
1212 | | |
1213 | 16 | { &hf_socks_ver, |
1214 | 16 | { "Version", "socks.version", FT_UINT8, BASE_DEC, NULL, |
1215 | 16 | 0x0, NULL, HFILL |
1216 | 16 | } |
1217 | 16 | }, |
1218 | 16 | { &hf_socks_ip_dst, |
1219 | 16 | { "Remote Address", "socks.dst", FT_IPv4, BASE_NONE, NULL, |
1220 | 16 | 0x0, NULL, HFILL |
1221 | 16 | } |
1222 | 16 | }, |
1223 | 16 | { &hf_socks_ip6_dst, |
1224 | 16 | { "Remote Address(ipv6)", "socks.dstV6", FT_IPv6, BASE_NONE, NULL, |
1225 | 16 | 0x0, NULL, HFILL |
1226 | 16 | } |
1227 | 16 | }, |
1228 | 16 | { &hf_gssapi_payload, |
1229 | 16 | { "GSSAPI data", "socks.gssapi.data", FT_BYTES, BASE_NONE, NULL, |
1230 | 16 | 0x0, NULL, HFILL |
1231 | 16 | } |
1232 | 16 | }, |
1233 | 16 | { &hf_gssapi_command, |
1234 | 16 | { "SOCKS/GSSAPI command", "socks.gssapi.command", FT_UINT8, BASE_DEC, |
1235 | 16 | VALS(gssapi_command_table), 0x0, NULL, HFILL |
1236 | 16 | } |
1237 | 16 | }, |
1238 | 16 | { &hf_gssapi_length, |
1239 | 16 | { "SOCKS/GSSAPI data length", "socks.gssapi.length", FT_UINT16, BASE_DEC, NULL, |
1240 | 16 | 0x0, NULL, HFILL |
1241 | 16 | } |
1242 | 16 | }, |
1243 | 16 | { &hf_v4a_dns_name, |
1244 | 16 | { "SOCKS v4a Remote Domain Name", "socks.v4a_dns_name", FT_STRINGZ, BASE_NONE, |
1245 | 16 | NULL, 0x0, NULL, HFILL |
1246 | 16 | } |
1247 | 16 | }, |
1248 | 16 | { &hf_socks_dstport, |
1249 | 16 | { "Remote Port", "socks.dstport", FT_UINT16, |
1250 | 16 | BASE_DEC, NULL, 0x0, NULL, HFILL |
1251 | 16 | } |
1252 | 16 | }, |
1253 | 16 | { &hf_socks_cmd, |
1254 | 16 | { "Command", "socks.command", FT_UINT8, |
1255 | 16 | BASE_DEC, VALS(cmd_strings), 0x0, NULL, HFILL |
1256 | 16 | } |
1257 | 16 | }, |
1258 | 16 | { &hf_socks_results_4, |
1259 | 16 | { "Results(V4)", "socks.results", FT_UINT8, |
1260 | 16 | BASE_DEC, VALS(reply_table_v4), 0x0, NULL, HFILL |
1261 | 16 | } |
1262 | 16 | }, |
1263 | 16 | { &hf_socks_results_5, |
1264 | 16 | { "Results(V5)", "socks.results", FT_UINT8, |
1265 | 16 | BASE_DEC, VALS(reply_table_v5), 0x0, NULL, HFILL |
1266 | 16 | } |
1267 | 16 | }, |
1268 | 16 | { &hf_client_auth_method_count, |
1269 | 16 | { "Authentication Method Count", "socks.auth_method_count", FT_UINT8, |
1270 | 16 | BASE_DEC, NULL, 0x0, NULL, HFILL |
1271 | 16 | } |
1272 | 16 | }, |
1273 | 16 | { &hf_client_auth_method, |
1274 | 16 | { "Method", "socks.auth_method", FT_UINT8, |
1275 | 16 | BASE_DEC, NULL, 0x0, NULL, HFILL |
1276 | 16 | } |
1277 | 16 | }, |
1278 | 16 | { &hf_socks_reserved, |
1279 | 16 | { "Reserved", "socks.reserved", FT_UINT8, |
1280 | 16 | BASE_DEC, NULL, 0x0, NULL, HFILL |
1281 | 16 | } |
1282 | 16 | }, |
1283 | 16 | { &hf_socks_reserved2, |
1284 | 16 | { "Reserved", "socks.reserved", FT_UINT16, |
1285 | 16 | BASE_DEC, NULL, 0x0, NULL, HFILL |
1286 | 16 | } |
1287 | 16 | }, |
1288 | 16 | { &hf_client_port, |
1289 | 16 | { "Port", "socks.port", FT_UINT16, |
1290 | 16 | BASE_DEC, NULL, 0x0, NULL, HFILL |
1291 | 16 | } |
1292 | 16 | }, |
1293 | 16 | { &hf_server_accepted_auth_method, |
1294 | 16 | { "Accepted Auth Method", "socks.auth_accepted_method", FT_UINT8, |
1295 | 16 | BASE_DEC, NULL, 0x0, NULL, HFILL |
1296 | 16 | } |
1297 | 16 | }, |
1298 | 16 | { &hf_server_auth_status, |
1299 | 16 | { "Status", "socks.auth_status", FT_UINT8, |
1300 | 16 | BASE_DEC, NULL, 0x0, NULL, HFILL |
1301 | 16 | } |
1302 | 16 | }, |
1303 | 16 | { &hf_server_remote_host_port, |
1304 | 16 | { "Remote Host Port", "socks.remote_host_port", FT_UINT16, |
1305 | 16 | BASE_DEC, NULL, 0x0, NULL, HFILL |
1306 | 16 | } |
1307 | 16 | }, |
1308 | 16 | { &hf_socks_subnegotiation_version, |
1309 | 16 | { "Subnegotiation Version", "socks.subnegotiation_version", FT_UINT8, BASE_DEC, NULL, |
1310 | 16 | 0x0, NULL, HFILL |
1311 | 16 | } |
1312 | 16 | }, |
1313 | 16 | { &hf_socks_username, |
1314 | 16 | { "User name", "socks.username", FT_STRING, BASE_NONE, |
1315 | 16 | NULL, 0x0, NULL, HFILL |
1316 | 16 | } |
1317 | 16 | }, |
1318 | 16 | { &hf_socks_password, |
1319 | 16 | { "Password", "socks.password", FT_STRING, BASE_NONE, |
1320 | 16 | NULL, 0x0, NULL, HFILL |
1321 | 16 | } |
1322 | 16 | }, |
1323 | 16 | { &hf_socks_remote_name, |
1324 | 16 | { "Remote name", "socks.remote_name", FT_STRING, BASE_NONE, |
1325 | 16 | NULL, 0x0, NULL, HFILL |
1326 | 16 | } |
1327 | 16 | }, |
1328 | 16 | { &hf_socks_address_type, |
1329 | 16 | { "Address Type", "socks.address_type", FT_UINT8, |
1330 | 16 | BASE_DEC, VALS(address_type_table), 0x0, NULL, HFILL |
1331 | 16 | } |
1332 | 16 | }, |
1333 | 16 | { &hf_socks_fragment_number, |
1334 | 16 | { "Fragment Number", "socks.fragment_number", FT_UINT8, |
1335 | 16 | BASE_DEC, NULL, 0x0, NULL, HFILL |
1336 | 16 | } |
1337 | 16 | }, |
1338 | 16 | { &hf_socks_ping_end_command, |
1339 | 16 | { "Ping: End command", "socks.ping_end_command", FT_NONE, |
1340 | 16 | BASE_NONE, NULL, 0x0, NULL, HFILL |
1341 | 16 | } |
1342 | 16 | }, |
1343 | 16 | { &hf_socks_ping_results, |
1344 | 16 | { "Ping Results", "socks.ping_results", FT_NONE, |
1345 | 16 | BASE_NONE, NULL, 0x0, NULL, HFILL |
1346 | 16 | } |
1347 | 16 | }, |
1348 | 16 | { &hf_socks_traceroute_end_command, |
1349 | 16 | { "Traceroute: End command", "socks.traceroute_end_command", FT_NONE, |
1350 | 16 | BASE_NONE, NULL, 0x0, NULL, HFILL |
1351 | 16 | } |
1352 | 16 | }, |
1353 | 16 | { &hf_socks_traceroute_results, |
1354 | 16 | { "Traceroute Results", "socks.traceroute_results", FT_NONE, |
1355 | 16 | BASE_NONE, NULL, 0x0, NULL, HFILL |
1356 | 16 | } |
1357 | 16 | }, |
1358 | 16 | }; |
1359 | | |
1360 | 16 | proto_socks = proto_register_protocol ( "Socks Protocol", "Socks", "socks"); |
1361 | | |
1362 | 16 | proto_register_field_array(proto_socks, hf, array_length(hf)); |
1363 | 16 | proto_register_subtree_array(ett, array_length(ett)); |
1364 | | |
1365 | 16 | socks_udp_handle = register_dissector_with_description("socks_udp", "SOCKS over UDP", socks_udp_dissector, proto_socks); |
1366 | 16 | socks_handle = register_dissector_with_description("socks_tcp", "SOCKS over TCP", dissect_socks, proto_socks); |
1367 | 16 | socks_handle_tls = register_dissector_with_description("socks_tls", "SOCKS over TLS", dissect_socks_tls, proto_socks); |
1368 | 16 | } |
1369 | | |
1370 | | |
1371 | | void |
1372 | 16 | proto_reg_handoff_socks(void) { |
1373 | | |
1374 | | /* dissector install routine */ |
1375 | | |
1376 | 16 | dissector_add_uint_with_preference("tcp.port", TCP_PORT_SOCKS, socks_handle); |
1377 | | |
1378 | 16 | ssl_dissector_add(0, socks_handle_tls); |
1379 | 16 | } |
1380 | | |
1381 | | /* |
1382 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
1383 | | * |
1384 | | * Local variables: |
1385 | | * c-basic-offset: 4 |
1386 | | * tab-width: 8 |
1387 | | * indent-tabs-mode: nil |
1388 | | * End: |
1389 | | * |
1390 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
1391 | | * :indentSize=4:tabSize=8:noTabs=true: |
1392 | | */ |