Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-rsh.c
blob385e3876bafbb046d797c9e9796c1f418b42a9d5
1 /* packet-rsh.c
2 * Routines for rsh (Remote Shell) dissection
3 * Copyright 2012, Stephen Fisher (see AUTHORS file)
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * This dissector is a modified copy of packet-exec.c due to
10 * protocol similarities and replaces the original rsh dissector
11 * by Robert Tsai <rtsai@netapp.com>. It is further based on BSD's
12 * rshd code and man page.
14 * SPDX-License-Identifier: GPL-2.0-or-later
17 #include "config.h"
19 #include <epan/packet.h>
20 #include <epan/conversation.h>
21 #include <epan/prefs.h>
22 #include <wsutil/str_util.h>
24 /* The rsh protocol uses TCP port 512 per its IANA assignment */
25 #define RSH_PORT 514
27 void proto_register_rsh(void);
28 void proto_reg_handoff_rsh(void);
30 static dissector_handle_t rsh_handle;
32 /* Variables for our preferences */
33 static bool preference_info_show_client_username;
34 static bool preference_info_show_server_username = true;
35 static bool preference_info_show_command;
37 /* Initialize the protocol and registered fields */
38 static int proto_rsh;
40 static int hf_rsh_stderr_port;
41 static int hf_rsh_client_username;
42 static int hf_rsh_server_username;
43 static int hf_rsh_command;
44 static int hf_rsh_client_server_data;
45 static int hf_rsh_server_client_data;
47 /* Initialize the subtree pointers */
48 static int ett_rsh;
50 #define RSH_STDERR_PORT_LEN 5
51 #define RSH_CLIENT_USERNAME_LEN 16
52 #define RSH_SERVER_USERNAME_LEN 16
53 #define RSH_COMMAND_LEN 256 /* Based on the size of the system's argument list */
55 /* Initialize the structure that will be tied to each conversation.
56 * This is used to display the username and/or command in the INFO column of
57 * each packet of the conversation. */
59 typedef enum {
60 NONE,
61 WAIT_FOR_STDERR_PORT,
62 WAIT_FOR_CLIENT_USERNAME,
63 WAIT_FOR_SERVER_USERNAME,
64 WAIT_FOR_COMMAND,
65 WAIT_FOR_DATA
66 } rsh_session_state_t;
69 typedef struct {
70 /* Packet number within the conversation */
71 unsigned first_packet_number, second_packet_number;
72 unsigned third_packet_number, fourth_packet_number;
74 /* The following variables are given values from session_state_t
75 * above to keep track of where we are in the beginning of the session
76 * (when the username and other fields show up). This is necessary for
77 * when the user clicks randomly through the initial packets instead of
78 * going in order.
81 /* Track where we are in the conversation */
82 rsh_session_state_t state;
83 rsh_session_state_t first_packet_state, second_packet_state;
84 rsh_session_state_t third_packet_state, fourth_packet_state;
86 char *client_username;
87 char *server_username;
88 char *command;
89 } rsh_hash_entry_t;
92 static int
93 dissect_rsh(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
95 /* Set up structures needed to add the protocol subtree and manage it */
96 proto_item *ti;
97 proto_tree *rsh_tree=NULL;
99 /* Variables for extracting and displaying data from the packet */
100 unsigned char *field_stringz; /* Temporary storage for each field we extract */
102 int length;
103 unsigned offset = 0;
104 conversation_t *conversation;
105 rsh_hash_entry_t *hash_info;
107 conversation = find_or_create_conversation(pinfo);
109 /* Retrieve information from conversation
110 * or add it if it isn't there yet
112 hash_info = (rsh_hash_entry_t *)conversation_get_proto_data(conversation, proto_rsh);
113 if(!hash_info){
114 hash_info = wmem_new(wmem_file_scope(), rsh_hash_entry_t);
116 hash_info->first_packet_number = pinfo->num;
117 hash_info->second_packet_number = 0;
118 hash_info->third_packet_number = 0;
119 hash_info->fourth_packet_number = 0;
121 hash_info->state = WAIT_FOR_STDERR_PORT; /* The first field we'll see */
123 /* Start with empty username and command strings */
124 hash_info->client_username=NULL;
125 hash_info->server_username=NULL;
126 hash_info->command=NULL;
128 /* These will be set on the first pass by the first
129 * four packets of the conversation
131 hash_info->first_packet_state = NONE;
132 hash_info->second_packet_state = NONE;
133 hash_info->third_packet_state = NONE;
134 hash_info->fourth_packet_state = NONE;
136 conversation_add_proto_data(conversation, proto_rsh, hash_info);
139 /* Store the number of the first three packets of this conversation
140 * as we reach them the first time */
142 if(!hash_info->second_packet_number
143 && pinfo->num > hash_info->first_packet_number){
144 /* We're on the second packet of the conversation */
145 hash_info->second_packet_number = pinfo->num;
146 } else if(hash_info->second_packet_number
147 && !hash_info->third_packet_number
148 && pinfo->num > hash_info->second_packet_number) {
149 /* We're on the third packet of the conversation */
150 hash_info->third_packet_number = pinfo->num;
151 } else if(hash_info->third_packet_number
152 && !hash_info->fourth_packet_number
153 && pinfo->num > hash_info->third_packet_number) {
154 /* We're on the fourth packet of the conversation */
155 hash_info->fourth_packet_number = pinfo->num;
158 /* Save this packet's state so we can retrieve it if this packet
159 * is selected again later. If the packet's state was already stored,
160 * then retrieve it */
161 if(pinfo->num == hash_info->first_packet_number){
162 if(hash_info->first_packet_state == NONE){
163 hash_info->first_packet_state = hash_info->state;
164 } else {
165 hash_info->state = hash_info->first_packet_state;
169 if(pinfo->num == hash_info->second_packet_number){
170 if(hash_info->second_packet_state == NONE){
171 hash_info->second_packet_state = hash_info->state;
172 } else {
173 hash_info->state = hash_info->second_packet_state;
177 if(pinfo->num == hash_info->third_packet_number){
178 if(hash_info->third_packet_state == NONE){
179 hash_info->third_packet_state = hash_info->state;
180 } else {
181 hash_info->state = hash_info->third_packet_state;
185 if(pinfo->num == hash_info->fourth_packet_number){
186 if(hash_info->fourth_packet_state == NONE){
187 hash_info->fourth_packet_state = hash_info->state;
188 } else {
189 hash_info->state = hash_info->fourth_packet_state;
193 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RSH");
195 /* First, clear the info column */
196 col_clear(pinfo->cinfo, COL_INFO);
198 /* Client username */
199 if(hash_info->client_username && preference_info_show_client_username == true){
200 col_append_fstr(pinfo->cinfo, COL_INFO, "Client username:%s ", hash_info->client_username);
203 /* Server username */
204 if(hash_info->server_username && preference_info_show_server_username == true){
205 col_append_fstr(pinfo->cinfo, COL_INFO, "Server username:%s ", hash_info->server_username);
208 /* Command */
209 if(hash_info->command && preference_info_show_command == true){
210 col_append_fstr(pinfo->cinfo, COL_INFO, "Command:%s ", hash_info->command);
213 /* create display subtree for the protocol */
214 ti = proto_tree_add_item(tree, proto_rsh, tvb, 0, -1, ENC_NA);
215 rsh_tree = proto_item_add_subtree(ti, ett_rsh);
217 /* If this packet doesn't end with a null terminated string,
218 * then it must be session data only and we can skip looking
219 * for the other fields.
221 if(tvb_find_uint8(tvb, tvb_captured_length(tvb)-1, 1, '\0') == -1){
222 hash_info->state = WAIT_FOR_DATA;
225 if(hash_info->state == WAIT_FOR_STDERR_PORT
226 && tvb_reported_length_remaining(tvb, offset)){
227 field_stringz = tvb_get_stringz_enc(pinfo->pool, tvb, offset, &length, ENC_ASCII);
229 /* Check if this looks like the stderr_port field.
230 * It is optional, so it may only be 1 character long
231 * (the NULL)
233 if(length == 1 || (isdigit_string(field_stringz)
234 && length <= RSH_STDERR_PORT_LEN)){
235 proto_tree_add_string(rsh_tree, hf_rsh_stderr_port, tvb, offset, length, (char*)field_stringz);
236 /* Next field we need */
237 hash_info->state = WAIT_FOR_CLIENT_USERNAME;
238 } else {
239 /* Since the data doesn't match this field, it must be data only */
240 hash_info->state = WAIT_FOR_DATA;
243 /* Used if the next field is in the same packet */
244 offset += length;
248 if(hash_info->state == WAIT_FOR_CLIENT_USERNAME
249 && tvb_reported_length_remaining(tvb, offset)){
250 field_stringz = tvb_get_stringz_enc(pinfo->pool, tvb, offset, &length, ENC_ASCII);
252 /* Check if this looks like the username field */
253 if(length != 1 && length <= RSH_CLIENT_USERNAME_LEN
254 && isprint_string(field_stringz)){
255 proto_tree_add_string(rsh_tree, hf_rsh_client_username, tvb, offset, length, (char*)field_stringz);
257 /* Store the client username so we can display it in the
258 * info column of the entire conversation
260 if(!hash_info->client_username){
261 hash_info->client_username=wmem_strdup(wmem_file_scope(), (char*)field_stringz);
264 /* Next field we need */
265 hash_info->state = WAIT_FOR_SERVER_USERNAME;
266 } else {
267 /* Since the data doesn't match this field, it must be data only */
268 hash_info->state = WAIT_FOR_DATA;
271 /* Used if the next field is in the same packet */
272 offset += length;
276 if(hash_info->state == WAIT_FOR_SERVER_USERNAME
277 && tvb_reported_length_remaining(tvb, offset)){
278 field_stringz = tvb_get_stringz_enc(pinfo->pool, tvb, offset, &length, ENC_ASCII);
280 /* Check if this looks like the password field */
281 if(length != 1 && length <= RSH_SERVER_USERNAME_LEN
282 && isprint_string(field_stringz)){
283 proto_tree_add_string(rsh_tree, hf_rsh_server_username, tvb, offset, length, (char*)field_stringz);
285 /* Store the server username so we can display it in the
286 * info column of the entire conversation
288 if(!hash_info->server_username){
289 hash_info->server_username=wmem_strdup(wmem_file_scope(), (char*)field_stringz);
294 /* Used if the next field is in the same packet */
295 offset += length;
296 /* Next field we are looking for */
297 hash_info->state = WAIT_FOR_COMMAND;
301 if(hash_info->state == WAIT_FOR_COMMAND
302 && tvb_reported_length_remaining(tvb, offset)){
303 field_stringz = tvb_get_stringz_enc(pinfo->pool, tvb, offset, &length, ENC_ASCII);
305 /* Check if this looks like the command field */
306 if(length != 1 && length <= RSH_COMMAND_LEN
307 && isprint_string(field_stringz)){
308 proto_tree_add_string(rsh_tree, hf_rsh_command, tvb, offset, length, (char*)field_stringz);
310 /* Store the command so we can display it in the
311 * info column of the entire conversation
313 if(!hash_info->command){
314 hash_info->command=wmem_strdup(wmem_file_scope(), (char*)field_stringz);
317 } else {
318 /* Since the data doesn't match this field, it must be data only */
319 hash_info->state = WAIT_FOR_DATA;
324 if(hash_info->state == WAIT_FOR_DATA
325 && tvb_reported_length_remaining(tvb, offset)){
326 if(pinfo->destport == RSH_PORT){
327 /* Packet going to the server */
328 /* offset = 0 since the whole packet is data */
329 proto_tree_add_item(rsh_tree, hf_rsh_client_server_data, tvb, 0, -1, ENC_NA);
331 col_append_str(pinfo->cinfo, COL_INFO, "Client -> Server data");
332 } else {
333 /* This packet must be going back to the client */
334 /* offset = 0 since the whole packet is data */
335 proto_tree_add_item(rsh_tree, hf_rsh_server_client_data, tvb, 0, -1, ENC_NA);
337 col_append_str(pinfo->cinfo, COL_INFO, "Server -> Client Data");
341 /* We haven't seen all of the fields yet */
342 if(hash_info->state < WAIT_FOR_DATA){
343 col_set_str(pinfo->cinfo, COL_INFO, "Session Establishment");
345 return tvb_captured_length(tvb);
348 void
349 proto_register_rsh(void)
351 static hf_register_info hf[] = {
352 { &hf_rsh_stderr_port, { "Stderr port (optional)", "rsh.stderr_port",
353 FT_STRINGZ, BASE_NONE, NULL, 0,
354 "Client port that is listening for stderr stream from server", HFILL } },
356 { &hf_rsh_client_username, { "Client username", "rsh.client_username",
357 FT_STRINGZ, BASE_NONE, NULL, 0,
358 "User's identity on the client machine", HFILL } },
360 { &hf_rsh_server_username, { "Server username", "rsh.server_username",
361 FT_STRINGZ, BASE_NONE, NULL, 0,
362 "User's identity on the server machine", HFILL } },
364 { &hf_rsh_command, { "Command to execute", "rsh.command",
365 FT_STRINGZ, BASE_NONE, NULL, 0,
366 "Command client is requesting the server to run", HFILL } },
368 { &hf_rsh_client_server_data, { "Client -> Server Data", "rsh.client_server_data",
369 FT_BYTES, BASE_NONE, NULL, 0,
370 NULL, HFILL } },
372 { &hf_rsh_server_client_data, { "Server -> Client Data", "rsh.server_client_data",
373 FT_BYTES, BASE_NONE, NULL, 0,
374 NULL, HFILL } },
378 static int *ett[] =
380 &ett_rsh
383 module_t *rsh_module;
385 /* Register the protocol name and description */
386 proto_rsh = proto_register_protocol("Remote Shell", "RSH", "rsh");
388 /* Required function calls to register the header fields and subtrees used */
389 proto_register_field_array(proto_rsh, hf, array_length(hf));
390 proto_register_subtree_array(ett, array_length(ett));
392 /* Register the dissector handle */
393 rsh_handle = register_dissector("rsh", dissect_rsh, proto_rsh);
395 /* Register preferences module */
396 rsh_module = prefs_register_protocol(proto_rsh, NULL);
398 /* Register our preferences */
399 prefs_register_bool_preference(rsh_module, "info_show_client_username",
400 "Show client username in info column",
401 "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.",
402 &preference_info_show_client_username);
404 prefs_register_bool_preference(rsh_module, "info_show_server_username",
405 "Show server username in info column",
406 "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.",
407 &preference_info_show_server_username);
409 prefs_register_bool_preference(rsh_module, "info_show_command",
410 "Show command in info column",
411 "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.",
412 &preference_info_show_command);
416 /* Entry function */
417 void
418 proto_reg_handoff_rsh(void)
420 dissector_add_uint_with_preference("tcp.port", RSH_PORT, rsh_handle);
424 * Editor modelines - https://www.wireshark.org/tools/modelines.html
426 * Local variables:
427 * c-basic-offset: 4
428 * tab-width: 8
429 * indent-tabs-mode: nil
430 * End:
432 * vi: set shiftwidth=4 tabstop=8 expandtab:
433 * :indentSize=4:tabSize=8:noTabs=true: