2 * Routines for exec (rexec) dissection
3 * Copyright 2006, Stephen Fisher (see AUTHORS file)
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * Based on BSD rexecd code/man page and parts of packet-rlogin.c
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 #include <epan/packet.h>
33 #include <epan/conversation.h>
34 #include <epan/wmem/wmem.h>
35 #include <epan/prefs.h>
36 #include <wsutil/str_util.h>
38 /* The exec protocol uses TCP port 512 per its IANA assignment */
41 /* Variables for our preferences */
42 static gboolean preference_info_show_username
= TRUE
;
43 static gboolean preference_info_show_command
= FALSE
;
45 void proto_register_exec(void);
46 void proto_reg_handoff_exec(void);
48 /* Initialize the protocol and registered fields */
49 static int proto_exec
= -1;
51 static int hf_exec_stderr_port
= -1;
52 static int hf_exec_username
= -1;
53 static int hf_exec_password
= -1;
54 static int hf_exec_command
= -1;
56 /* Initialize the subtree pointers */
57 static gint ett_exec
= -1;
59 #define EXEC_STDERR_PORT_LEN 5
60 #define EXEC_USERNAME_LEN 16
61 #define EXEC_PASSWORD_LEN 16
62 #define EXEC_COMMAND_LEN 256 /* Longer depending on server operating system? */
64 /* Initialize the structure that will be tied to each conversation.
65 * This is used to display the username and/or command in the INFO column of
66 * each packet of the conversation. */
75 } exec_session_state_t
;
79 /* Packet number within the conversation */
80 guint first_packet_number
, second_packet_number
;
81 guint third_packet_number
, fourth_packet_number
;
83 /* The following variables are given values from session_state_t
84 * above to keep track of where we are in the beginning of the session
85 * (when the username and other fields show up). This is necessary for
86 * when the user clicks randomly through the initial packets instead of
90 /* Track where we are in the conversation */
91 exec_session_state_t state
;
92 exec_session_state_t first_packet_state
, second_packet_state
;
93 exec_session_state_t third_packet_state
, fourth_packet_state
;
101 dissect_exec(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
103 /* Set up structures needed to add the protocol subtree and manage it */
105 proto_tree
*exec_tree
=NULL
;
107 /* Variables for extracting and displaying data from the packet */
108 guchar
*field_stringz
; /* Temporary storage for each field we extract */
112 conversation_t
*conversation
;
113 exec_hash_entry_t
*hash_info
;
115 conversation
= find_or_create_conversation(pinfo
);
117 /* Retrieve information from conversation
118 * or add it if it isn't there yet
120 hash_info
= (exec_hash_entry_t
*)conversation_get_proto_data(conversation
, proto_exec
);
122 hash_info
= wmem_new(wmem_file_scope(), exec_hash_entry_t
);
124 hash_info
->first_packet_number
= pinfo
->fd
->num
;
125 hash_info
->second_packet_number
= 0;
126 hash_info
->third_packet_number
= 0;
127 hash_info
->fourth_packet_number
= 0;
129 hash_info
->state
= WAIT_FOR_STDERR_PORT
; /* The first field we'll see */
131 /* Start with empty username and command strings */
132 hash_info
->username
=NULL
;
133 hash_info
->command
=NULL
;
135 /* These will be set on the first pass by the first
136 * four packets of the conversation
138 hash_info
->first_packet_state
= NONE
;
139 hash_info
->second_packet_state
= NONE
;
140 hash_info
->third_packet_state
= NONE
;
141 hash_info
->fourth_packet_state
= NONE
;
143 conversation_add_proto_data(conversation
, proto_exec
, hash_info
);
146 /* Store the number of the first three packets of this conversation
147 * as we reach them the first time */
149 if(!hash_info
->second_packet_number
150 && pinfo
->fd
->num
> hash_info
->first_packet_number
){
151 /* We're on the second packet of the conversation */
152 hash_info
->second_packet_number
= pinfo
->fd
->num
;
153 } else if(hash_info
->second_packet_number
154 && !hash_info
->third_packet_number
155 && pinfo
->fd
->num
> hash_info
->second_packet_number
) {
156 /* We're on the third packet of the conversation */
157 hash_info
->third_packet_number
= pinfo
->fd
->num
;
158 } else if(hash_info
->third_packet_number
159 && !hash_info
->fourth_packet_number
160 && pinfo
->fd
->num
> hash_info
->third_packet_number
) {
161 /* We're on the fourth packet of the conversation */
162 hash_info
->fourth_packet_number
= pinfo
->fd
->num
;
165 /* Save this packet's state so we can retrieve it if this packet
166 * is selected again later. If the packet's state was already stored,
167 * then retrieve it */
168 if(pinfo
->fd
->num
== hash_info
->first_packet_number
){
169 if(hash_info
->first_packet_state
== NONE
){
170 hash_info
->first_packet_state
= hash_info
->state
;
172 hash_info
->state
= hash_info
->first_packet_state
;
176 if(pinfo
->fd
->num
== hash_info
->second_packet_number
){
177 if(hash_info
->second_packet_state
== NONE
){
178 hash_info
->second_packet_state
= hash_info
->state
;
180 hash_info
->state
= hash_info
->second_packet_state
;
184 if(pinfo
->fd
->num
== hash_info
->third_packet_number
){
185 if(hash_info
->third_packet_state
== NONE
){
186 hash_info
->third_packet_state
= hash_info
->state
;
188 hash_info
->state
= hash_info
->third_packet_state
;
192 if(pinfo
->fd
->num
== hash_info
->fourth_packet_number
){
193 if(hash_info
->fourth_packet_state
== NONE
){
194 hash_info
->fourth_packet_state
= hash_info
->state
;
196 hash_info
->state
= hash_info
->fourth_packet_state
;
200 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "EXEC");
202 /* First, clear the info column */
203 col_clear(pinfo
->cinfo
, COL_INFO
);
206 if(hash_info
->username
&& preference_info_show_username
== TRUE
){
207 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "Username:%s ", hash_info
->username
);
211 if(hash_info
->command
&& preference_info_show_command
== TRUE
){
212 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "Command:%s ", hash_info
->command
);
215 /* create display subtree for the protocol */
216 ti
= proto_tree_add_item(tree
, proto_exec
, tvb
, 0, -1, ENC_NA
);
217 exec_tree
= proto_item_add_subtree(ti
, ett_exec
);
219 /* If this packet doesn't end with a null terminated string,
220 * then it must be session data only and we can skip looking
221 * for the other fields.
223 if(tvb_find_guint8(tvb
, tvb_length(tvb
)-1, 1, '\0') == -1){
224 hash_info
->state
= WAIT_FOR_DATA
;
227 if(hash_info
->state
== WAIT_FOR_STDERR_PORT
228 && tvb_length_remaining(tvb
, offset
)){
229 field_stringz
= tvb_get_stringz(wmem_packet_scope(), tvb
, offset
, &length
);
231 /* Check if this looks like the stderr_port field.
232 * It is optional, so it may only be 1 character long
235 if(length
== 1 || (isdigit_string(field_stringz
)
236 && length
<= EXEC_STDERR_PORT_LEN
)){
237 proto_tree_add_string(exec_tree
, hf_exec_stderr_port
, tvb
, offset
, length
, (gchar
*)field_stringz
);
238 /* Next field we need */
239 hash_info
->state
= WAIT_FOR_USERNAME
;
241 /* Since the data doesn't match this field, it must be data only */
242 hash_info
->state
= WAIT_FOR_DATA
;
245 /* Used if the next field is in the same packet */
250 if(hash_info
->state
== WAIT_FOR_USERNAME
251 && tvb_length_remaining(tvb
, offset
)){
252 field_stringz
= tvb_get_stringz(wmem_packet_scope(), tvb
, offset
, &length
);
254 /* Check if this looks like the username field */
255 if(length
!= 1 && length
<= EXEC_USERNAME_LEN
256 && isprint_string(field_stringz
)){
257 proto_tree_add_string(exec_tree
, hf_exec_username
, tvb
, offset
, length
, (gchar
*)field_stringz
);
259 /* Store the username so we can display it in the
260 * info column of the entire conversation
262 if(!hash_info
->username
){
263 hash_info
->username
=wmem_strdup(wmem_file_scope(), (gchar
*)field_stringz
);
266 /* Next field we need */
267 hash_info
->state
= WAIT_FOR_PASSWORD
;
269 /* Since the data doesn't match this field, it must be data only */
270 hash_info
->state
= WAIT_FOR_DATA
;
273 /* Used if the next field is in the same packet */
278 if(hash_info
->state
== WAIT_FOR_PASSWORD
279 && tvb_length_remaining(tvb
, offset
)){
280 field_stringz
= tvb_get_stringz(wmem_packet_scope(), tvb
, offset
, &length
);
282 /* Check if this looks like the password field */
283 if(length
!= 1 && length
<= EXEC_PASSWORD_LEN
284 && isprint_string(field_stringz
)){
285 proto_tree_add_string(exec_tree
, hf_exec_password
, tvb
, offset
, length
, (gchar
*)field_stringz
);
287 /* Next field we need */
288 hash_info
->state
= WAIT_FOR_COMMAND
;
290 /* Since the data doesn't match this field, it must be data only */
291 hash_info
->state
= WAIT_FOR_DATA
;
294 /* Used if the next field is in the same packet */
296 /* Next field we are looking for */
297 hash_info
->state
= WAIT_FOR_COMMAND
;
301 if(hash_info
->state
== WAIT_FOR_COMMAND
302 && tvb_length_remaining(tvb
, offset
)){
303 field_stringz
= tvb_get_stringz(wmem_packet_scope(), tvb
, offset
, &length
);
305 /* Check if this looks like the command field */
306 if(length
!= 1 && length
<= EXEC_COMMAND_LEN
307 && isprint_string(field_stringz
)){
308 proto_tree_add_string(exec_tree
, hf_exec_command
, tvb
, offset
, length
, (gchar
*)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(), (gchar
*)field_stringz
);
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_length_remaining(tvb
, offset
)){
326 if(pinfo
->destport
== EXEC_PORT
){
327 /* Packet going to the server */
328 /* offset = 0 since the whole packet is data */
329 proto_tree_add_text(exec_tree
, tvb
, 0, -1, "Client -> Server Data");
331 col_append_str(pinfo
->cinfo
, COL_INFO
, "Client -> Server data");
333 /* This packet must be going back to the client */
334 /* offset = 0 since the whole packet is data */
335 proto_tree_add_text(exec_tree
, tvb
, 0, -1, "Server -> Client Data");
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");
348 proto_register_exec(void)
350 static hf_register_info hf
[] =
352 { &hf_exec_stderr_port
, { "Stderr port (optional)", "exec.stderr_port",
353 FT_STRINGZ
, BASE_NONE
, NULL
, 0,
354 "Client port that is listening for stderr stream from server", HFILL
} },
356 { &hf_exec_username
, { "Client username", "exec.username",
357 FT_STRINGZ
, BASE_NONE
, NULL
, 0,
358 "Username client uses to log in to the server.", HFILL
} },
360 { &hf_exec_password
, { "Client password", "exec.password",
361 FT_STRINGZ
, BASE_NONE
, NULL
, 0,
362 "Password client uses to log in to the server.", HFILL
} },
364 { &hf_exec_command
, { "Command to execute", "exec.command",
365 FT_STRINGZ
, BASE_NONE
, NULL
, 0,
366 "Command client is requesting the server to run.", HFILL
} }
375 module_t
*exec_module
;
377 /* Register the protocol name and description */
378 proto_exec
= proto_register_protocol("Remote Process Execution", "EXEC", "exec");
380 /* Required function calls to register the header fields and subtrees used */
381 proto_register_field_array(proto_exec
, hf
, array_length(hf
));
382 proto_register_subtree_array(ett
, array_length(ett
));
384 /* Register preferences module */
385 exec_module
= prefs_register_protocol(proto_exec
, NULL
);
387 /* Register our preferences */
388 prefs_register_bool_preference(exec_module
, "info_show_username",
389 "Show username in info column",
390 "Controls the display of the session's username in the info column. This is only displayed if the packet containing it was seen during this capture session.", &preference_info_show_username
);
392 prefs_register_bool_preference(exec_module
, "info_show_command",
393 "Show command in info column",
394 "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.", &preference_info_show_command
);
400 proto_reg_handoff_exec(void)
402 dissector_handle_t exec_handle
;
404 exec_handle
= create_dissector_handle(dissect_exec
, proto_exec
);
405 dissector_add_uint("tcp.port", EXEC_PORT
, exec_handle
);