Ethereal-dev: [Ethereal-dev] Patch for Tacacs+: multiple keys

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Emanuele Caratti <wiz@xxxxxxxxx>
Date: Sun, 19 Oct 2003 15:40:28 +0200
Hi!
I added to my tacacs+ dissector the possility to use multiple keys, so it's
possible to dissect a tacacs+ stream where you have a different key for
each client<->server communication.
If the tacplus.key is specified as <server_ip>/<client_ip>=<key> ( multiple
entry MUST be separated by a sigle space char), then this key is binded to
the packet between server_ip and client_ip.
If the tacplus.key is specified as <key>, then this key will be used for
*every* tacacs+ packet.

I've also added some fields in the accounting dissection.

	

-- 
Ciao,
 Emanuele
--- /.1/cvs-mirror/ethereal/packet-tacacs.c	2003-09-30 07:30:31.000000000 +0200
+++ packet-tacacs.c	2003-10-19 14:32:17.000000000 +0200
@@ -5,7 +5,7 @@
  * Full Tacacs+ parsing with decryption by
  *   Emanuele Caratti <wiz@xxxxxx>
  *
- * $Id: packet-tacacs.c,v 1.29 2003/09/29 18:50:47 guy Exp $
+ * $Id: packet-tacacs.c,v 1.32.4.2 2003/10/14 19:54:13 wiz Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@xxxxxxxxxxxx>
@@ -66,7 +66,8 @@
 
 static gint ett_tacacs = -1;
 
-static char *tacplus_key;
+static char *tacplus_opt_key;
+static GSList *tacplus_keys = NULL;
 
 #define VERSION_TACACS	0x00
 #define VERSION_XTACACS	0x80
@@ -303,6 +304,7 @@
 static int hf_tacplus_flags = -1;
 static int hf_tacplus_flags_payload_type = -1;
 static int hf_tacplus_flags_connection_type = -1;
+static int hf_tacplus_acct_flags = -1;
 static int hf_tacplus_session_id = -1;
 static int hf_tacplus_packet_len = -1;
 
@@ -310,27 +312,33 @@
 static gint ett_tacplus_body = -1;
 static gint ett_tacplus_body_chap = -1;
 static gint ett_tacplus_flags = -1;
+static gint ett_tacplus_acct_flags = -1;
 
-#define FLAGS_UNENCRYPTED	0x01
 
 static const true_false_string payload_type = {
   "Unencrypted",
   "Encrypted"
 };
 
-#define FLAGS_SINGLE		0x04
 
 static const true_false_string connection_type = {
   "Single",
   "Multiple"
 };
 
+typedef struct _tacplus_key_entry {
+	address  *s; /* Server address */
+	address  *c; /* client address */
+	char	*k; /* Key */
+} tacplus_key_entry;
+
 static gint 
-tacplus_tvb_setup( tvbuff_t *tvb, tvbuff_t **dst_tvb, packet_info *pinfo, guint32 len, guint8 version )
+tacplus_decrypted_tvb_setup( tvbuff_t *tvb, tvbuff_t **dst_tvb, packet_info *pinfo, guint32 len, guint8 version, char *key )
 {
 	guint8	*buff;
 	u_char session_id[4];
 
+	/* TODO Check the possibility to use pinfo->descrypted_data */
 /* session_id is in NETWORK Byte Order, and is used as byte array in the md5_xor */
 
 	tvb_memcpy(tvb, (guint8*)session_id, 4,4); 
@@ -338,7 +346,7 @@
 	buff = tvb_memdup(tvb, TAC_PLUS_HDR_SIZE, len);
 
 
-	md5_xor( buff, tacplus_key, len, session_id,version, tvb_get_guint8(tvb,2) );
+	md5_xor( buff, key, len, session_id,version, tvb_get_guint8(tvb,2) );
 
 	/* Allocate a new tvbuff, referring to the decrypted data. */
 	*dst_tvb = tvb_new_real_data( buff, len, len );
@@ -364,9 +372,9 @@
 	guint8	buff[257];
 	for(i=0;i<arg_cnt;i++){
 		int len=tvb_get_guint8(tvb,len_off+i);
-		proto_tree_add_text( tree, tvb, len_off+i, 1, "Arg(%d) length: %d", i, len );
+		proto_tree_add_text( tree, tvb, len_off+i, 1, "Arg[%d] length: %d", i, len );
 		tvb_get_nstringz0(tvb, data_off, len+1, buff);
-		proto_tree_add_text( tree, tvb, data_off, len, "Arg(%d) value: %s", i, buff );
+		proto_tree_add_text( tree, tvb, data_off, len, "Arg[%d] value: %s", i, buff );
 		data_off+=len;
 	}
 }
@@ -385,15 +393,15 @@
 	/* authen_type */
 	val=tvb_get_guint8(tvb,offset);
 	proto_tree_add_text( tree, tvb, offset, 1,
-			"Authentication type: 0x%01x (%s)",
-			val, val_to_str( val, tacplus_authen_type_vals, "Unknown Packet" ) );
+			"Authentication type: %s",
+			val_to_str( val, tacplus_authen_type_vals, "Unknown Packet" ) );
 	offset++;
 
 	/* service */
 	val=tvb_get_guint8(tvb,offset);
 	proto_tree_add_text( tree, tvb, offset, 1,
-			"Service: 0x%01x (%s)",
-			val, val_to_str( val, tacplus_authen_service_vals, "Unknown Packet" ) );
+			"Service: %s",
+			val_to_str( val, tacplus_authen_service_vals, "Unknown Packet" ) );
 	offset++;
 
 	/* user_len && user */
@@ -525,7 +533,7 @@
 	val=tvb_get_guint8( tvb, AUTHEN_S_ACTION_OFF );
 	proto_tree_add_text( tree, tvb,
 			AUTHEN_S_ACTION_OFF, 1, 
-			"Action: 0x%01x (%s)", val,
+			"Action: %s", 
 			val_to_str( val, tacplus_authen_action_vals, "Unknown Packet" ) );
 
 	var_off=proto_tree_add_tacplus_common_fields( tvb, tree , AUTHEN_S_PRIV_LVL_OFF, AUTHEN_S_VARDATA_OFF );
@@ -616,8 +624,7 @@
 
 	val=tvb_get_guint8( tvb, AUTHOR_Q_AUTH_METH_OFF ) ;
 	proto_tree_add_text( tree, tvb, AUTHOR_Q_AUTH_METH_OFF, 1, 
-			"Auth Method: 0x%01x (%s)", val,
-				val_to_str( val, tacplus_authen_method, "Unknown Authen Method" ) );
+			"Auth Method: %s", val_to_str( val, tacplus_authen_method, "Unknown Authen Method" ) );
 
 	val=tvb_get_guint8( tvb, AUTHOR_Q_ARGC_OFF );
 	var_off=proto_tree_add_tacplus_common_fields( tvb, tree ,
@@ -662,13 +669,30 @@
 {
 	int val, var_off;
 
+	proto_item *tf;
+	proto_tree *flags_tree;
+
 	val=tvb_get_guint8( tvb, ACCT_Q_FLAGS_OFF ); 
-	proto_tree_add_text( tree, tvb, ACCT_Q_FLAGS_OFF, 1, "Flags: 0x%04x", val );
+	tf = proto_tree_add_uint( tree, hf_tacplus_acct_flags, tvb, ACCT_Q_FLAGS_OFF, 1, val );
+
+	flags_tree = proto_item_add_subtree( tf, ett_tacplus_acct_flags );
+	proto_tree_add_text( flags_tree, tvb, ACCT_Q_FLAGS_OFF, 1, "%s",
+			decode_boolean_bitfield( val, TAC_PLUS_ACCT_FLAG_MORE, 8,
+				"More: Set", "More: Not set" ) );
+	proto_tree_add_text( flags_tree, tvb, ACCT_Q_FLAGS_OFF, 1, "%s",
+			decode_boolean_bitfield( val, TAC_PLUS_ACCT_FLAG_START, 8,
+				"Start: Set", "Start: Not set" ) );
+	proto_tree_add_text( flags_tree, tvb, ACCT_Q_FLAGS_OFF, 1, "%s",
+			decode_boolean_bitfield( val, TAC_PLUS_ACCT_FLAG_STOP, 8,
+				"Stop: Set", "Stop: Not set" ) );
+	proto_tree_add_text( flags_tree, tvb, ACCT_Q_FLAGS_OFF, 1, "%s",
+			decode_boolean_bitfield( val, TAC_PLUS_ACCT_FLAG_WATCHDOG, 8,
+				"Watchdog: Set", "Watchdog: Not set" ) );
 
 	val=tvb_get_guint8( tvb, ACCT_Q_METHOD_OFF );
 	proto_tree_add_text( tree, tvb, ACCT_Q_METHOD_OFF, 1, 
-			"Authen Method: 0x%04x (%s)",  
-			val, val_to_str( val, tacplus_authen_type_vals, "Unknown Packet" ) );
+			"Authen Method: 0x%01x (%s)",  
+			val, val_to_str( val, tacplus_authen_method, "Unknown Authen Method" ) );
 
 	val=tvb_get_guint8( tvb, ACCT_Q_ARG_CNT_OFF );
 
@@ -760,6 +784,137 @@
 	proto_tree_add_text( tree, tvb, 0, tvb_length( tvb ), "Bogus..");
 }
 
+#ifdef DEB_TACPLUS
+static void
+tacplus_print_key_entry( gpointer data, gpointer user_data )
+{
+	tacplus_key_entry *tacplus_data=(tacplus_key_entry *)data;
+	if( user_data ) {
+		printf("%s:%s=%s\n", address_to_str( tacplus_data->s ),
+				address_to_str( tacplus_data->c ), tacplus_data->k );
+	} else {
+		printf("%s:%s\n", address_to_str( tacplus_data->s ),
+				address_to_str( tacplus_data->c ) );
+	}
+}
+#endif
+static int
+cmp_conv_address( gconstpointer p1, gconstpointer p2 )
+{
+	tacplus_key_entry *a1=(tacplus_key_entry*)p1;
+	tacplus_key_entry *a2=(tacplus_key_entry*)p2;
+	gint32	ret;
+	/*
+	printf("p1=>");
+	tacplus_print_key_entry( p1, NULL );
+	printf("p2=>");
+	tacplus_print_key_entry( p2, NULL );
+	*/
+	ret=CMP_ADDRESS( a1->s, a2->s );
+	if( !ret ) {
+		ret=CMP_ADDRESS( a1->c, a2->c );
+		/*
+		if(ret)
+			printf("No Client found!"); */
+	} else {
+		/* printf("No Server found!"); */
+	}
+	return ret;
+}
+
+static char*
+find_key( address *srv, address *cln )
+{
+	tacplus_key_entry data;
+	GSList *match;
+
+	data.s=srv;
+	data.c=cln;
+/*	printf("Looking for: ");
+	tacplus_print_key_entry( (gconstpointer)&data, NULL ); */
+	match=g_slist_find_custom( tacplus_keys, (gpointer)&data, cmp_conv_address );
+/*	printf("Finished (%p)\n", match);  */
+	if( match ) 
+		return ((tacplus_key_entry*)match->data)->k;
+
+	return (tacplus_keys?NULL:tacplus_opt_key);
+}
+#define AF_INET 2
+int inet_pton(int , const char*, void*);
+
+static void
+mkipv4_address( address **addr, char *str_addr )
+{
+	*addr=g_malloc( sizeof(address) );
+	(*addr)->type=AT_IPv4;
+	(*addr)->len=4;
+	(*addr)->data=g_malloc( 4 );
+	inet_pton( AF_INET, (const char*)str_addr, (void*)(*addr)->data );
+}
+static void
+parse_tuple( char *key_from_option )
+{
+	char *client,*key;
+	tacplus_key_entry *tacplus_data=g_malloc( sizeof(tacplus_key_entry) );
+	/*
+	printf("keys: %s\n", key_from_option );
+	*/
+	client=strchr(key_from_option,'/');
+	if(!client)
+		return;
+	*client++='\0';
+	key=strchr(client,'=');
+	if(!key)
+		return;
+	*key++='\0';
+	/*
+	printf("%s %s => %s\n", key_from_option, client, key );
+	*/
+	mkipv4_address( &tacplus_data->s, key_from_option );
+	mkipv4_address( &tacplus_data->c, client );
+	tacplus_data->k=strdup( key );
+	tacplus_keys = g_slist_prepend( tacplus_keys, tacplus_data );
+}
+
+static
+void
+free_tacplus_keys( gpointer data, gpointer user_data _U_ )
+{
+	g_free( ((tacplus_key_entry *)data)->k );
+}
+
+static 
+void
+parse_tacplus_keys( char *keys_from_option )
+{
+	char *s1,*s;
+
+	/* Drop old keys */
+	if( tacplus_keys ) {
+		g_slist_foreach( tacplus_keys, free_tacplus_keys, NULL );
+		g_slist_free( tacplus_keys );
+		tacplus_keys=NULL;
+	}
+
+	if( !strchr( keys_from_option, '/' ) ){
+		/* option not in client/server=key format */
+		return ;
+	}
+	s=strdup(keys_from_option);
+	s1=s;
+	keys_from_option = s;
+	while(keys_from_option){
+		if( (s=strchr( keys_from_option, ' ' )) != NULL )
+			*s++='\0';
+		parse_tuple( keys_from_option );
+		keys_from_option=s;
+	}
+	g_free( s1 );
+#ifdef DEB_TACPLUS
+	g_slist_foreach( tacplus_keys, tacplus_print_key_entry, GINT_TO_POINTER(1) );
+#endif
+}
+
 static void
 dissect_tacplus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
@@ -771,8 +926,14 @@
 	proto_item      *tf;
 	proto_item	*tmp_pi;
 	guint32		len;
-	gboolean	request=(pinfo->match_port == pinfo->destport);
+	gboolean	request=( pinfo->destport == TCP_PORT_TACACS );
+	char		*key=NULL;
 
+	if( request ) {
+		key=find_key( &pinfo->dst, &pinfo->src );
+	} else {
+		key=find_key(  &pinfo->src, &pinfo->dst );
+	}
 	if (check_col(pinfo->cinfo, COL_PROTOCOL))
 		col_set_str(pinfo->cinfo, COL_PROTOCOL, "TACACS+");
 
@@ -814,12 +975,12 @@
 		flags = tvb_get_guint8(tvb,3);
 		tf = proto_tree_add_uint_format(tacplus_tree, hf_tacplus_flags,
 		    tvb, 3, 1, flags,
-		    "Flags: %s, %s (0x%02x)",
-		    (flags&FLAGS_UNENCRYPTED) ? "Unencrypted payload" :
-						"Encrypted payload",
+		    "Flags: 0x%02x (%s payload, %s)",
+			flags,
+		    (flags&FLAGS_UNENCRYPTED) ? "Unencrypted" :
+						"Encrypted",
 		    (flags&FLAGS_SINGLE) ? "Single connection" :
-					   "Multiple Connections",
-		    flags);
+					   "Multiple Connections" );
 		flags_tree = proto_item_add_subtree(tf, ett_tacplus_flags);
 		proto_tree_add_boolean(flags_tree, hf_tacplus_flags_payload_type,
 		    tvb, 3, 1, flags);
@@ -838,8 +999,8 @@
 			new_tvb = tvb_new_subset( tvb, TAC_PLUS_HDR_SIZE, len, len );
 		}  else {
 			new_tvb=NULL;
-			if( tacplus_key && *tacplus_key ){
-				tacplus_tvb_setup(tvb,&new_tvb,pinfo,len,version);
+			if( key && *key ){
+				tacplus_decrypted_tvb_setup( tvb, &new_tvb, pinfo, len, version, key );
 			}
 		}
 		if( new_tvb ) {
@@ -854,6 +1015,12 @@
 }
 
 void
+tacplus_pref_cb()
+{
+	parse_tacplus_keys( tacplus_opt_key );
+}
+
+void
 proto_register_tacplus(void)
 {
 	static hf_register_info hf[] = {
@@ -886,13 +1053,17 @@
 	      FT_UINT8, BASE_HEX, NULL, 0x0,
 	      "Flags", HFILL }},
 	  { &hf_tacplus_flags_payload_type,
-	    { "Payload type",       "tacplus.flags.payload_type",
-	      FT_BOOLEAN, 8, TFS(&payload_type), FLAGS_UNENCRYPTED,
+	    { "Unencrypted",       "tacplus.flags.unencrypted",
+	      FT_BOOLEAN, 8, TFS(&flags_set_truth), FLAGS_UNENCRYPTED,
 	      "Payload type (unencrypted or encrypted)", HFILL }},
 	  { &hf_tacplus_flags_connection_type,
-	    { "Connection type",    "tacplus.flags.connection_type",
-	      FT_BOOLEAN, 8, TFS(&connection_type), FLAGS_SINGLE,
+	    { "Single Connection",    "tacplus.flags.singleconn",
+	      FT_BOOLEAN, 8, TFS(&flags_set_truth), FLAGS_SINGLE,
 	      "Connection type (single or multiple)", HFILL }},
+	  { &hf_tacplus_acct_flags,
+	    { "Flags",    "tacplus.acct.flags",
+	      FT_UINT8, BASE_HEX, NULL, 0x0,
+	      "Flags", HFILL }},
 	  { &hf_tacplus_session_id,
 	    { "Session ID",         "tacplus.session_id",
 	      FT_UINT32, BASE_DEC, NULL, 0x0,
@@ -905,6 +1076,7 @@
 	static gint *ett[] = {
 		&ett_tacplus,
 		&ett_tacplus_flags,
+		&ett_tacplus_acct_flags,
 		&ett_tacplus_body,
 		&ett_tacplus_body_chap, 
 	};
@@ -913,9 +1085,9 @@
 	proto_tacplus = proto_register_protocol("TACACS+", "TACACS+", "tacplus");
 	proto_register_field_array(proto_tacplus, hf, array_length(hf));
 	proto_register_subtree_array(ett, array_length(ett));
-	tacplus_module = prefs_register_protocol (proto_tacplus, NULL);
+	tacplus_module = prefs_register_protocol (proto_tacplus, tacplus_pref_cb );
 	prefs_register_string_preference ( tacplus_module, "key",
-	"TACACS+ Encryption Key", "TACACS+ Encryption Key", &tacplus_key );
+	"TACACS+ Encryption Key", "TACACS+ Encryption Key", &tacplus_opt_key );
 }
 
 void
--- /.1/cvs-mirror/ethereal/packet-tacacs.h	2003-09-30 07:30:31.000000000 +0200
+++ packet-tacacs.h	2003-10-14 20:22:14.000000000 +0200
@@ -30,6 +30,11 @@
 
 #define MD5_LEN           16
 #define MSCHAP_DIGEST_LEN 49
+enum
+{
+	FLAGS_UNENCRYPTED = 0x01,
+	FLAGS_SINGLE = 0x04
+};
 
 /* Tacacs+ packet type */
 enum