2 * Routines for rsh (Remote Shell) dissection
3 * Copyright 2012, Stephen Fisher (see AUTHORS file)
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * This dissector is a modified copy of packet-exec.c due to
12 * protocol similarities and replaces the original rsh dissector
13 * by Robert Tsai <rtsai@netapp.com>. It is further based on BSD's
14 * rshd code and man page.
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
37 #include <epan/packet.h>
38 #include <epan/conversation.h>
39 #include <epan/prefs.h>
40 #include <epan/wmem/wmem.h>
41 #include <wsutil/str_util.h>
43 /* The rsh protocol uses TCP port 512 per its IANA assignment */
46 /* Variables for our preferences */
47 static gboolean preference_info_show_client_username
= FALSE
;
48 static gboolean preference_info_show_server_username
= TRUE
;
49 static gboolean preference_info_show_command
= FALSE
;
51 /* Initialize the protocol and registered fields */
52 static int proto_rsh
= -1;
54 static int hf_rsh_stderr_port
= -1;
55 static int hf_rsh_client_username
= -1;
56 static int hf_rsh_server_username
= -1;
57 static int hf_rsh_command
= -1;
59 /* Initialize the subtree pointers */
60 static gint ett_rsh
= -1;
62 #define RSH_STDERR_PORT_LEN 5
63 #define RSH_CLIENT_USERNAME_LEN 16
64 #define RSH_SERVER_USERNAME_LEN 16
65 #define RSH_COMMAND_LEN 256 /* Based on the size of the system's argument list */
67 /* Initialize the structure that will be tied to each conversation.
68 * This is used to display the username and/or command in the INFO column of
69 * each packet of the conversation. */
74 WAIT_FOR_CLIENT_USERNAME
,
75 WAIT_FOR_SERVER_USERNAME
,
78 } rsh_session_state_t
;
82 /* Packet number within the conversation */
83 guint first_packet_number
, second_packet_number
;
84 guint third_packet_number
, fourth_packet_number
;
86 /* The following variables are given values from session_state_t
87 * above to keep track of where we are in the beginning of the session
88 * (when the username and other fields show up). This is necessary for
89 * when the user clicks randomly through the initial packets instead of
93 /* Track where we are in the conversation */
94 rsh_session_state_t state
;
95 rsh_session_state_t first_packet_state
, second_packet_state
;
96 rsh_session_state_t third_packet_state
, fourth_packet_state
;
98 gchar
*client_username
;
99 gchar
*server_username
;
105 dissect_rsh(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
107 /* Set up structures needed to add the protocol subtree and manage it */
109 proto_tree
*rsh_tree
=NULL
;
111 /* Variables for extracting and displaying data from the packet */
112 guchar
*field_stringz
; /* Temporary storage for each field we extract */
116 conversation_t
*conversation
;
117 rsh_hash_entry_t
*hash_info
;
119 conversation
= find_or_create_conversation(pinfo
);
121 /* Retrieve information from conversation
122 * or add it if it isn't there yet
124 hash_info
= (rsh_hash_entry_t
*)conversation_get_proto_data(conversation
, proto_rsh
);
126 hash_info
= wmem_new(wmem_file_scope(), rsh_hash_entry_t
);
128 hash_info
->first_packet_number
= pinfo
->fd
->num
;
129 hash_info
->second_packet_number
= 0;
130 hash_info
->third_packet_number
= 0;
131 hash_info
->fourth_packet_number
= 0;
133 hash_info
->state
= WAIT_FOR_STDERR_PORT
; /* The first field we'll see */
135 /* Start with empty username and command strings */
136 hash_info
->client_username
=NULL
;
137 hash_info
->server_username
=NULL
;
138 hash_info
->command
=NULL
;
140 /* These will be set on the first pass by the first
141 * four packets of the conversation
143 hash_info
->first_packet_state
= NONE
;
144 hash_info
->second_packet_state
= NONE
;
145 hash_info
->third_packet_state
= NONE
;
146 hash_info
->fourth_packet_state
= NONE
;
148 conversation_add_proto_data(conversation
, proto_rsh
, hash_info
);
151 /* Store the number of the first three packets of this conversation
152 * as we reach them the first time */
154 if(!hash_info
->second_packet_number
155 && pinfo
->fd
->num
> hash_info
->first_packet_number
){
156 /* We're on the second packet of the conversation */
157 hash_info
->second_packet_number
= pinfo
->fd
->num
;
158 } else if(hash_info
->second_packet_number
159 && !hash_info
->third_packet_number
160 && pinfo
->fd
->num
> hash_info
->second_packet_number
) {
161 /* We're on the third packet of the conversation */
162 hash_info
->third_packet_number
= pinfo
->fd
->num
;
163 } else if(hash_info
->third_packet_number
164 && !hash_info
->fourth_packet_number
165 && pinfo
->fd
->num
> hash_info
->third_packet_number
) {
166 /* We're on the fourth packet of the conversation */
167 hash_info
->fourth_packet_number
= pinfo
->fd
->num
;
170 /* Save this packet's state so we can retrieve it if this packet
171 * is selected again later. If the packet's state was already stored,
172 * then retrieve it */
173 if(pinfo
->fd
->num
== hash_info
->first_packet_number
){
174 if(hash_info
->first_packet_state
== NONE
){
175 hash_info
->first_packet_state
= hash_info
->state
;
177 hash_info
->state
= hash_info
->first_packet_state
;
181 if(pinfo
->fd
->num
== hash_info
->second_packet_number
){
182 if(hash_info
->second_packet_state
== NONE
){
183 hash_info
->second_packet_state
= hash_info
->state
;
185 hash_info
->state
= hash_info
->second_packet_state
;
189 if(pinfo
->fd
->num
== hash_info
->third_packet_number
){
190 if(hash_info
->third_packet_state
== NONE
){
191 hash_info
->third_packet_state
= hash_info
->state
;
193 hash_info
->state
= hash_info
->third_packet_state
;
197 if(pinfo
->fd
->num
== hash_info
->fourth_packet_number
){
198 if(hash_info
->fourth_packet_state
== NONE
){
199 hash_info
->fourth_packet_state
= hash_info
->state
;
201 hash_info
->state
= hash_info
->fourth_packet_state
;
205 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "RSH");
207 /* First, clear the info column */
208 col_clear(pinfo
->cinfo
, COL_INFO
);
210 /* Client username */
211 if(hash_info
->client_username
&& preference_info_show_client_username
== TRUE
){
212 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "Client username:%s ", hash_info
->client_username
);
215 /* Server username */
216 if(hash_info
->server_username
&& preference_info_show_server_username
== TRUE
){
217 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "Server username:%s ", hash_info
->server_username
);
221 if(hash_info
->command
&& preference_info_show_command
== TRUE
){
222 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "Command:%s ", hash_info
->command
);
225 /* create display subtree for the protocol */
226 ti
= proto_tree_add_item(tree
, proto_rsh
, tvb
, 0, -1, ENC_NA
);
227 rsh_tree
= proto_item_add_subtree(ti
, ett_rsh
);
229 /* If this packet doesn't end with a null terminated string,
230 * then it must be session data only and we can skip looking
231 * for the other fields.
233 if(tvb_find_guint8(tvb
, tvb_length(tvb
)-1, 1, '\0') == -1){
234 hash_info
->state
= WAIT_FOR_DATA
;
237 if(hash_info
->state
== WAIT_FOR_STDERR_PORT
238 && tvb_length_remaining(tvb
, offset
)){
239 field_stringz
= tvb_get_stringz(wmem_packet_scope(), tvb
, offset
, &length
);
241 /* Check if this looks like the stderr_port field.
242 * It is optional, so it may only be 1 character long
245 if(length
== 1 || (isdigit_string(field_stringz
)
246 && length
<= RSH_STDERR_PORT_LEN
)){
247 proto_tree_add_string(rsh_tree
, hf_rsh_stderr_port
, tvb
, offset
, length
, (gchar
*)field_stringz
);
248 /* Next field we need */
249 hash_info
->state
= WAIT_FOR_CLIENT_USERNAME
;
251 /* Since the data doesn't match this field, it must be data only */
252 hash_info
->state
= WAIT_FOR_DATA
;
255 /* Used if the next field is in the same packet */
260 if(hash_info
->state
== WAIT_FOR_CLIENT_USERNAME
261 && tvb_length_remaining(tvb
, offset
)){
262 field_stringz
= tvb_get_stringz(wmem_packet_scope(), tvb
, offset
, &length
);
264 /* Check if this looks like the username field */
265 if(length
!= 1 && length
<= RSH_CLIENT_USERNAME_LEN
266 && isprint_string(field_stringz
)){
267 proto_tree_add_string(rsh_tree
, hf_rsh_client_username
, tvb
, offset
, length
, (gchar
*)field_stringz
);
269 /* Store the client username so we can display it in the
270 * info column of the entire conversation
272 if(!hash_info
->client_username
){
273 hash_info
->client_username
=wmem_strdup(wmem_file_scope(), (gchar
*)field_stringz
);
276 /* Next field we need */
277 hash_info
->state
= WAIT_FOR_SERVER_USERNAME
;
279 /* Since the data doesn't match this field, it must be data only */
280 hash_info
->state
= WAIT_FOR_DATA
;
283 /* Used if the next field is in the same packet */
288 if(hash_info
->state
== WAIT_FOR_SERVER_USERNAME
289 && tvb_length_remaining(tvb
, offset
)){
290 field_stringz
= tvb_get_stringz(wmem_packet_scope(), tvb
, offset
, &length
);
292 /* Check if this looks like the password field */
293 if(length
!= 1 && length
<= RSH_SERVER_USERNAME_LEN
294 && isprint_string(field_stringz
)){
295 proto_tree_add_string(rsh_tree
, hf_rsh_server_username
, tvb
, offset
, length
, (gchar
*)field_stringz
);
297 /* Store the server username so we can display it in the
298 * info column of the entire conversation
300 if(!hash_info
->server_username
){
301 hash_info
->server_username
=wmem_strdup(wmem_file_scope(), (gchar
*)field_stringz
);
304 /* Next field we need */
305 hash_info
->state
= WAIT_FOR_COMMAND
;
307 /* Since the data doesn't match this field, it must be data only */
308 hash_info
->state
= WAIT_FOR_DATA
;
311 /* Used if the next field is in the same packet */
313 /* Next field we are looking for */
314 hash_info
->state
= WAIT_FOR_COMMAND
;
318 if(hash_info
->state
== WAIT_FOR_COMMAND
319 && tvb_length_remaining(tvb
, offset
)){
320 field_stringz
= tvb_get_stringz(wmem_packet_scope(), tvb
, offset
, &length
);
322 /* Check if this looks like the command field */
323 if(length
!= 1 && length
<= RSH_COMMAND_LEN
324 && isprint_string(field_stringz
)){
325 proto_tree_add_string(rsh_tree
, hf_rsh_command
, tvb
, offset
, length
, (gchar
*)field_stringz
);
327 /* Store the command so we can display it in the
328 * info column of the entire conversation
330 if(!hash_info
->command
){
331 hash_info
->command
=wmem_strdup(wmem_file_scope(), (gchar
*)field_stringz
);
335 /* Since the data doesn't match this field, it must be data only */
336 hash_info
->state
= WAIT_FOR_DATA
;
341 if(hash_info
->state
== WAIT_FOR_DATA
342 && tvb_length_remaining(tvb
, offset
)){
343 if(pinfo
->destport
== RSH_PORT
){
344 /* Packet going to the server */
345 /* offset = 0 since the whole packet is data */
346 proto_tree_add_text(rsh_tree
, tvb
, 0, -1, "Client -> Server Data");
348 col_append_str(pinfo
->cinfo
, COL_INFO
, "Client -> Server data");
350 /* This packet must be going back to the client */
351 /* offset = 0 since the whole packet is data */
352 proto_tree_add_text(rsh_tree
, tvb
, 0, -1, "Server -> Client Data");
354 col_append_str(pinfo
->cinfo
, COL_INFO
, "Server -> Client Data");
358 /* We haven't seen all of the fields yet */
359 if(hash_info
->state
< WAIT_FOR_DATA
){
360 col_set_str(pinfo
->cinfo
, COL_INFO
, "Session Establishment");
365 proto_register_rsh(void)
367 static hf_register_info hf
[] = {
368 { &hf_rsh_stderr_port
, { "Stderr port (optional)", "rsh.stderr_port",
369 FT_STRINGZ
, BASE_NONE
, NULL
, 0,
370 "Client port that is listening for stderr stream from server", HFILL
} },
372 { &hf_rsh_client_username
, { "Client username", "rsh.client_username",
373 FT_STRINGZ
, BASE_NONE
, NULL
, 0,
374 "User's identity on the client machine", HFILL
} },
376 { &hf_rsh_server_username
, { "Server username", "rsh.server_username",
377 FT_STRINGZ
, BASE_NONE
, NULL
, 0,
378 "User's identity on the server machine", HFILL
} },
380 { &hf_rsh_command
, { "Command to execute", "rsh.command",
381 FT_STRINGZ
, BASE_NONE
, NULL
, 0,
382 "Command client is requesting the server to run", HFILL
} }
391 module_t
*rsh_module
;
393 /* Register the protocol name and description */
394 proto_rsh
= proto_register_protocol("Remote Shell", "RSH", "rsh");
396 /* Required function calls to register the header fields and subtrees used */
397 proto_register_field_array(proto_rsh
, hf
, array_length(hf
));
398 proto_register_subtree_array(ett
, array_length(ett
));
400 /* Register preferences module */
401 rsh_module
= prefs_register_protocol(proto_rsh
, NULL
);
403 /* Register our preferences */
404 prefs_register_bool_preference(rsh_module
, "info_show_client_username",
405 "Show client username in info column",
406 "Controls the display of the session's client username in the info column. This is only displayed if the packet containing it was seen during this capture session.",
407 &preference_info_show_client_username
);
409 prefs_register_bool_preference(rsh_module
, "info_show_server_username",
410 "Show server username in info column",
411 "Controls the display of the session's server username in the info column. This is only displayed if the packet containing it was seen during this capture session.",
412 &preference_info_show_server_username
);
414 prefs_register_bool_preference(rsh_module
, "info_show_command",
415 "Show command in info column",
416 "Controls the display of the command being run on the server by this session in the info column. This is only displayed if the packet containing it was seen during this capture session.",
417 &preference_info_show_command
);
423 proto_reg_handoff_rsh(void)
425 dissector_handle_t rsh_handle
;
427 rsh_handle
= create_dissector_handle(dissect_rsh
, proto_rsh
);
428 dissector_add_uint("tcp.port", RSH_PORT
, rsh_handle
);
432 * Editor modelines - http://www.wireshark.org/tools/modelines.html
437 * indent-tabs-mode: nil
440 * vi: set shiftwidth=4 tabstop=8 expandtab:
441 * :indentSize=4:tabSize=8:noTabs=true: