481 lines
12 KiB
C
481 lines
12 KiB
C
![]() |
/* packet-rlogin.c
|
||
|
* Routines for unix rlogin packet dissection
|
||
|
* Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
|
||
|
*
|
||
|
* $Id: packet-rlogin.c,v 1.1 2000/04/08 03:32:09 guy Exp $
|
||
|
*
|
||
|
* Ethereal - Network traffic analyzer
|
||
|
* By Gerald Combs <gerald@zing.org>
|
||
|
* Copyright 1998 Gerald Combs
|
||
|
*
|
||
|
*
|
||
|
* Based upon RFC-1282 - BSD Rlogin
|
||
|
*
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU General Public License
|
||
|
* as published by the Free Software Foundation; either version 2
|
||
|
* of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
|
*
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
# include "config.h"
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#ifdef HAVE_SYS_TYPES_H
|
||
|
# include <sys/types.h>
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE_NETINET_IN_H
|
||
|
# include <netinet/in.h>
|
||
|
#endif
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <glib.h>
|
||
|
#include "packet.h"
|
||
|
#include "resolv.h"
|
||
|
#include "globals.h"
|
||
|
#include "alignment.h"
|
||
|
#include "conversation.h"
|
||
|
|
||
|
#include "packet-tcp.h"
|
||
|
|
||
|
#ifdef NEED_SNPRINTF_H
|
||
|
# ifdef HAVE_STDARG_H
|
||
|
# include <stdarg.h>
|
||
|
# else
|
||
|
# include <varargs.h>
|
||
|
# endif
|
||
|
# include "snprintf.h"
|
||
|
#endif
|
||
|
|
||
|
#define CHECK_PACKET_LENGTH(X) if ((offset+X) > fd->cap_len){ \
|
||
|
proto_tree_add_text(tree, offset, 0, "*** FRAME TOO SHORT ***"); \
|
||
|
return; }
|
||
|
|
||
|
|
||
|
#define TCP_PORT_RLOGIN 513
|
||
|
|
||
|
static int proto_rlogin = -1;
|
||
|
|
||
|
static int ett_rlogin = -1;
|
||
|
static int ett_rlogin_window = -1;
|
||
|
static int ett_rlogin_user_info = -1;
|
||
|
static int ett_rlogin_window_rows = -1;
|
||
|
static int ett_rlogin_window_cols = -1;
|
||
|
static int ett_rlogin_window_x_pixels = -1;
|
||
|
static int ett_rlogin_window_y_pixels = -1;
|
||
|
|
||
|
|
||
|
static int hf_user_info = -1;
|
||
|
static int hf_window_info = -1;
|
||
|
static int hf_window_info_rows = -1;
|
||
|
static int hf_window_info_cols = -1;
|
||
|
static int hf_window_info_x_pixels = -1;
|
||
|
static int hf_window_info_y_pixels = -1;
|
||
|
|
||
|
|
||
|
#define RLOGIN_PORT 513
|
||
|
|
||
|
#define row_pointer_t frame_data*
|
||
|
#define compare_packet_ptr(X) (X == (fd))
|
||
|
#define get_packet_ptr (fd)
|
||
|
|
||
|
#define NAME_LEN 32
|
||
|
|
||
|
typedef struct {
|
||
|
int state;
|
||
|
row_pointer_t info_row;
|
||
|
row_pointer_t last_row;
|
||
|
char name[ NAME_LEN];
|
||
|
|
||
|
}rlogin_hash_entry_t;
|
||
|
|
||
|
|
||
|
#define NONE 0
|
||
|
#define USER_INFO_WAIT 1
|
||
|
#define DONE 2
|
||
|
#define BAD 2
|
||
|
|
||
|
|
||
|
static GMemChunk *rlogin_vals = NULL;
|
||
|
|
||
|
#define rlogin_hash_init_count 20
|
||
|
|
||
|
static guint32 last_abs_sec = 0;
|
||
|
static guint32 last_abs_usec= 0;
|
||
|
|
||
|
static void
|
||
|
rlogin_init( void){
|
||
|
|
||
|
/* Routine to initialize rlogin protocol before each capture or filter pass. */
|
||
|
/* Release any memory if needed. Then setup the memory chunks. */
|
||
|
|
||
|
last_abs_sec = 0;
|
||
|
last_abs_usec= 0;
|
||
|
|
||
|
if (rlogin_vals)
|
||
|
g_mem_chunk_destroy(rlogin_vals);
|
||
|
|
||
|
rlogin_vals = g_mem_chunk_new("rlogin_vals",
|
||
|
sizeof( rlogin_hash_entry_t),
|
||
|
rlogin_hash_init_count * sizeof( rlogin_hash_entry_t),
|
||
|
G_ALLOC_AND_FREE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**************** Decoder State Machine ******************/
|
||
|
|
||
|
static void
|
||
|
rlogin_state_machine( rlogin_hash_entry_t *hash_info, const u_char *pd,
|
||
|
int offset, frame_data *fd) {
|
||
|
|
||
|
/* rlogin stream decoder */
|
||
|
/* Just watched for second packet from client with the user name and */
|
||
|
/* terminal type information. */
|
||
|
|
||
|
|
||
|
if ( pi.destport != RLOGIN_PORT) /* not from client */
|
||
|
return;
|
||
|
/* exit if not needed */
|
||
|
if (( hash_info->state == DONE) || ( hash_info->state == BAD))
|
||
|
return;
|
||
|
|
||
|
/* test timestamp */
|
||
|
if (( last_abs_sec > fd->abs_secs) ||
|
||
|
(( last_abs_sec == fd->abs_secs) && ( last_abs_usec >= fd->abs_usecs)))
|
||
|
return;
|
||
|
|
||
|
last_abs_sec = fd->abs_secs; /* save timestamp */
|
||
|
last_abs_usec = fd->abs_usecs;
|
||
|
|
||
|
if ( !IS_DATA_IN_FRAME(offset)) /* exit if no data */
|
||
|
return;
|
||
|
|
||
|
if ( hash_info->state == NONE){ /* new connection*/
|
||
|
|
||
|
if ( GBYTE( pd, offset + 1)) { /* expect a NULL */
|
||
|
hash_info->state = DONE; /* quit, no NULL */
|
||
|
return;
|
||
|
}
|
||
|
else {
|
||
|
if (( END_OF_FRAME) <= 1) /* if no data */
|
||
|
hash_info->state = USER_INFO_WAIT;
|
||
|
else {
|
||
|
hash_info->state = DONE;
|
||
|
hash_info->info_row = get_packet_ptr;
|
||
|
}
|
||
|
}
|
||
|
} /* expect user data here */
|
||
|
/*$$$ may need to do more checking here */
|
||
|
else if ( hash_info->state == USER_INFO_WAIT) {
|
||
|
hash_info->state = DONE;
|
||
|
hash_info->info_row = get_packet_ptr;
|
||
|
/* save name for later*/
|
||
|
strncpy( hash_info->name, &pd[ offset], NAME_LEN);
|
||
|
|
||
|
hash_info->name[ NAME_LEN] = 0;
|
||
|
|
||
|
if (check_col(fd, COL_INFO)) /* update summary */
|
||
|
col_append_str( fd, COL_INFO, ", User information");
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rlogin_display( rlogin_hash_entry_t *hash_info, const u_char *pd,
|
||
|
int offset, frame_data *fd, proto_tree *tree) {
|
||
|
|
||
|
/* Display the proto tree */
|
||
|
|
||
|
proto_tree *rlogin_tree, *user_info_tree, *window_tree;
|
||
|
proto_item *ti;
|
||
|
guint8 *Ptr;
|
||
|
const char *str;
|
||
|
proto_item *user_info_item, *window_info_item;
|
||
|
|
||
|
ti = proto_tree_add_item( tree, proto_rlogin, offset,
|
||
|
END_OF_FRAME, NULL, "Rlogin:" );
|
||
|
|
||
|
rlogin_tree = proto_item_add_subtree(ti, ett_rlogin);
|
||
|
|
||
|
if ( !END_OF_FRAME) /* exit if no data */
|
||
|
return;
|
||
|
|
||
|
if ( tcp_urgent_pointer && /* if control message */
|
||
|
BYTES_ARE_IN_FRAME(offset + tcp_urgent_pointer - 1, 1)) {
|
||
|
|
||
|
int i = offset + tcp_urgent_pointer - 1;
|
||
|
guint16 Temp = GBYTE( pd, i);
|
||
|
|
||
|
if ( i < offset) /* check for data in front */
|
||
|
proto_tree_add_text( rlogin_tree, offset, i - offset,
|
||
|
"Data");
|
||
|
|
||
|
proto_tree_add_text( rlogin_tree, i, 1, "Control byte: %u (%s)",
|
||
|
Temp,
|
||
|
(Temp == 2) ? "Clear buffer" :
|
||
|
(Temp == 0x10) ? "Raw mode" :
|
||
|
(Temp == 0x20) ? "Cooked mode" :
|
||
|
(Temp == 0x80) ? "Window size request" :
|
||
|
"Unknown");
|
||
|
offset = i; /* adjust offset */
|
||
|
}
|
||
|
|
||
|
else if ( !GBYTE( pd, offset)){ /* startup */
|
||
|
if ( pi.srcport== RLOGIN_PORT) /* from server */
|
||
|
proto_tree_add_text(rlogin_tree, offset, 1,
|
||
|
"Startup info received flag (0x00)");
|
||
|
|
||
|
else
|
||
|
proto_tree_add_text(rlogin_tree, offset, 1,
|
||
|
"Client Startup Flag (0x00)");
|
||
|
++offset;
|
||
|
}
|
||
|
|
||
|
if ( compare_packet_ptr( hash_info->info_row)){ /* user info ?*/
|
||
|
|
||
|
user_info_item = proto_tree_add_item( rlogin_tree, hf_user_info,
|
||
|
offset, END_OF_FRAME, NULL );
|
||
|
|
||
|
str = &pd[ offset]; /* do server user name */
|
||
|
|
||
|
CHECK_PACKET_LENGTH( strlen( str));
|
||
|
|
||
|
user_info_tree = proto_item_add_subtree( user_info_item,
|
||
|
ett_rlogin_user_info);
|
||
|
|
||
|
proto_tree_add_text( user_info_tree, offset, strlen( str) + 1,
|
||
|
"Server User Name: %s", str);
|
||
|
|
||
|
offset += strlen( str) + 1;
|
||
|
str = &pd[ offset]; /* do client user name */
|
||
|
CHECK_PACKET_LENGTH( strlen( str));
|
||
|
proto_tree_add_text( user_info_tree, offset, strlen( str) + 1,
|
||
|
"Client User Name: %s", str);
|
||
|
|
||
|
offset += strlen( str) + 1;
|
||
|
|
||
|
str = &pd[ offset]; /* do terminal type/speed */
|
||
|
CHECK_PACKET_LENGTH( strlen( str));
|
||
|
proto_tree_add_text( user_info_tree, offset, strlen( str) + 1,
|
||
|
"Terminal Type/Speed: %s", str);
|
||
|
|
||
|
offset += strlen( str) + 1;
|
||
|
}
|
||
|
|
||
|
/* test for terminal information, the data will have 2 0xff bytes */
|
||
|
|
||
|
/* look for first 0xff byte */
|
||
|
Ptr = (guint8*)memchr( &pd[ offset], 0xff, END_OF_FRAME);
|
||
|
|
||
|
if (( Ptr) && (*(Ptr + 1) == 0xff)) { /* found terminal info */
|
||
|
|
||
|
int ti_offset = Ptr - pd; /* get offset */
|
||
|
|
||
|
if ( ti_offset < offset){ /*if data before terminal info*/
|
||
|
proto_tree_add_text( rlogin_tree, offset,
|
||
|
(ti_offset - offset), "Data");
|
||
|
offset = ti_offset;
|
||
|
}
|
||
|
|
||
|
CHECK_PACKET_LENGTH( 12);
|
||
|
window_info_item = proto_tree_add_item( rlogin_tree,
|
||
|
hf_window_info, offset, 12, NULL );
|
||
|
|
||
|
window_tree = proto_item_add_subtree( window_info_item,
|
||
|
ett_rlogin_window);
|
||
|
|
||
|
CHECK_PACKET_LENGTH( 2);
|
||
|
proto_tree_add_text( window_tree, offset, 2,
|
||
|
"Magic Cookie: (0xff, 0xff)");
|
||
|
offset += 2;
|
||
|
|
||
|
CHECK_PACKET_LENGTH( 2);
|
||
|
proto_tree_add_text( window_tree, offset, 2,
|
||
|
"Window size marker: 'ss'");
|
||
|
offset += 2;
|
||
|
|
||
|
CHECK_PACKET_LENGTH( 2);
|
||
|
proto_tree_add_item( window_tree, hf_window_info_rows, offset,
|
||
|
2, pntohs( &pd[offset]));
|
||
|
offset += 2;
|
||
|
|
||
|
CHECK_PACKET_LENGTH( 2);
|
||
|
proto_tree_add_item( window_tree, hf_window_info_cols, offset,
|
||
|
2, pntohs( &pd[offset]) );
|
||
|
offset += 2;
|
||
|
|
||
|
CHECK_PACKET_LENGTH( 2);
|
||
|
proto_tree_add_item( window_tree, hf_window_info_x_pixels,
|
||
|
offset, 2, pntohs( &pd[offset]));
|
||
|
offset += 2;
|
||
|
|
||
|
CHECK_PACKET_LENGTH( 2);
|
||
|
proto_tree_add_item( window_tree, hf_window_info_y_pixels,
|
||
|
offset, 2, pntohs( &pd[offset]) );
|
||
|
offset += 2;
|
||
|
}
|
||
|
|
||
|
if ( END_OF_FRAME != 0) /* if more data */
|
||
|
proto_tree_add_text(rlogin_tree, offset, END_OF_FRAME, "Data");
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static void
|
||
|
dissect_rlogin(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
|
||
|
|
||
|
|
||
|
guint8 *Ptr;
|
||
|
rlogin_hash_entry_t *hash_info = 0;
|
||
|
conversation_t *conversation;
|
||
|
|
||
|
/* Lookup this connection*/
|
||
|
conversation = find_conversation( &pi.src, &pi.dst, pi.ptype,
|
||
|
pi.srcport, pi.destport);
|
||
|
|
||
|
if ( conversation) /* conversation found */
|
||
|
hash_info = conversation->data;
|
||
|
|
||
|
/* new conversation create local data structure */
|
||
|
else {
|
||
|
hash_info = g_mem_chunk_alloc(rlogin_vals);
|
||
|
hash_info->state = NONE;
|
||
|
hash_info->info_row = 0;
|
||
|
hash_info->name[ 0] = 0;
|
||
|
|
||
|
conversation_new( &pi.src, &pi.dst, pi.ptype,
|
||
|
pi.srcport, pi.destport, hash_info);
|
||
|
}
|
||
|
|
||
|
if (check_col(fd, COL_PROTOCOL)) /* update protocol */
|
||
|
col_add_str(fd, COL_PROTOCOL, "Rlogin");
|
||
|
|
||
|
if (check_col(fd, COL_INFO)){ /* display packet info*/
|
||
|
|
||
|
char temp[1000];
|
||
|
|
||
|
if ( hash_info->name[0]) {
|
||
|
strcpy( temp, "User name: ");
|
||
|
strcat( temp, hash_info->name);
|
||
|
strcat( temp, ", ");
|
||
|
}
|
||
|
else
|
||
|
temp[0] = 0;
|
||
|
|
||
|
if ( !GBYTE(pd, offset))
|
||
|
strcat( temp, "Start Handshake");
|
||
|
else if ( tcp_urgent_pointer)
|
||
|
strcat( temp, "Control Message");
|
||
|
|
||
|
else { /* check for terminal info */
|
||
|
Ptr = (guint8*)memchr( &pd[ offset], 0xff, END_OF_FRAME);
|
||
|
|
||
|
if (( Ptr) && (*(Ptr + 1) == 0xff))
|
||
|
strcat( temp, "Terminal Info");
|
||
|
else {
|
||
|
int i;
|
||
|
strcat( temp, "Data:");
|
||
|
i = strlen( temp);
|
||
|
strncat( temp, &pd[ offset], 128);
|
||
|
temp[ i + MIN( 128, END_OF_FRAME)] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
col_add_str(fd, COL_INFO, temp);
|
||
|
}
|
||
|
|
||
|
rlogin_state_machine( hash_info, pd, offset, fd);
|
||
|
|
||
|
if ( tree) /* if proto tree, decode data */
|
||
|
rlogin_display( hash_info, pd, offset, fd, tree);
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
proto_register_rlogin( void){
|
||
|
|
||
|
/* Prep the rlogin protocol, for now, just register it */
|
||
|
|
||
|
static gint *ett[] = {
|
||
|
&ett_rlogin,
|
||
|
&ett_rlogin_window,
|
||
|
&ett_rlogin_window_rows,
|
||
|
&ett_rlogin_window_cols,
|
||
|
&ett_rlogin_window_x_pixels,
|
||
|
&ett_rlogin_window_y_pixels,
|
||
|
&ett_rlogin_user_info
|
||
|
};
|
||
|
|
||
|
static hf_register_info hf[] = {
|
||
|
|
||
|
{ &hf_user_info,
|
||
|
{ "User Info", "rlogin.user_info", FT_NONE, BASE_NONE,
|
||
|
NULL, 0x0, ""
|
||
|
}
|
||
|
},
|
||
|
{ &hf_window_info,
|
||
|
{ "Window Info", "rlogin.window_size", FT_NONE, BASE_NONE,
|
||
|
NULL, 0x0, ""
|
||
|
}
|
||
|
},
|
||
|
{ &hf_window_info_rows,
|
||
|
{ "Rows", "rlogin.window_size.rows", FT_UINT16, BASE_DEC,
|
||
|
NULL, 0x0, ""
|
||
|
}
|
||
|
},
|
||
|
{ &hf_window_info_cols,
|
||
|
{ "Columns", "rlogin.window_size.cols", FT_UINT16, BASE_DEC,
|
||
|
NULL, 0x0, ""
|
||
|
}
|
||
|
},
|
||
|
{ &hf_window_info_x_pixels,
|
||
|
{ "X Pixels", "rlogin.window_size.x_pixels", FT_UINT16, BASE_DEC,
|
||
|
NULL, 0x0, ""
|
||
|
}
|
||
|
},
|
||
|
{ &hf_window_info_y_pixels,
|
||
|
{ "Y Pixels", "rlogin.window_size.y_pixels", FT_UINT16, BASE_DEC,
|
||
|
NULL, 0x0, ""
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
proto_rlogin = proto_register_protocol (
|
||
|
"Rlogin Protocol", "rlogin");
|
||
|
|
||
|
proto_register_field_array(proto_rlogin, hf, array_length(hf));
|
||
|
proto_register_subtree_array(ett, array_length(ett));
|
||
|
|
||
|
register_init_routine( &rlogin_init); /* register re-init routine */
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
void
|
||
|
proto_reg_handoff_rlogin(void) {
|
||
|
|
||
|
/* dissector install routine */
|
||
|
|
||
|
dissector_add("tcp.port", TCP_PORT_RLOGIN, dissect_rlogin);
|
||
|
}
|