Wireshark-dev: Re: [Wireshark-dev] packet-smb not properly handling transact requests and respo
From: Richard Sharpe <realrichardsharpe@xxxxxxxxx>
Date: Sat, 9 Jun 2012 20:10:48 -0700
On Sat, Jun 9, 2012 at 5:20 PM, Richard Sharpe
<realrichardsharpe@xxxxxxxxx> wrote:
> On Sat, Jun 9, 2012 at 4:51 PM, Richard Sharpe
> <realrichardsharpe@xxxxxxxxx> wrote:
>> On Sat, Jun 9, 2012 at 3:12 PM, Richard Sharpe
>> <realrichardsharpe@xxxxxxxxx> wrote:
>>> Hi folks,
>>>
>>> So, in Samba bug https://bugzilla.samba.org/show_bug.cgi?id=8989 you
>>> will find two captures relating to the handling of NT TRANSACT SET
>>> SECURITY DESCRIPTOR.
>>>
>>> Wireshark does not handle the dissection of these correctly, and I
>>> suspect, normal SMB TRANSACT and SMB TRANSACT2 requests/responses.
>>>
>>> In dissect_smb, in the code for a normal bidirectional request or
>>> response we lookup, using g_hash_table_lookup, the sip for the pid_mid
>>> for the current frame. However, we look it up in the unmatched
>>> requests.
>>>
>>> By the time we see a secondary, the original request with that pid_mid
>>> is no longer unmatched, so we have a null sip. Later, when we call
>>> smb_trans_defragment on the secondary (so we can give this fragment to
>>> the original request), we do not have a sip, so we do nothing.
>>>
>>> It seems that in dissect_smb, if the request is an XXX_SECONDARY, we
>>> should look up the sip in the matched packets not the unmatched
>>> packets.
>>>
>>> What say ye?
>>>
>>> I will give that a try to see if I can make progress.
>>
>> The following patch gets much closer to handling this, but it is not
>> yet correct. For example, it lables the secondary as an unknown and it
>> shows extra byte parameters against the primary.
>
> A minor mod fixes the problem of the extra byte parameters ... now to
> fix the two remaining problems:
>
> 1. The secondary is labeled as unknown. I suspect that it would better
> to actually associate the dissection with the last secondary ...
>
> 2. Requests that require multiple secondaries are not handled
> correctly. This will require adding some code from the response
> handling section.
>
> Feedback welcome.

OK, this patch fixes the re-assembly of NT_TRANSACT requests and
responses. I suspect that the others will have to be fixed similarly.
Patch is attached as well.

There is also the issue of handling the continuations better.

Index: epan/dissectors/packet-smb.c
===================================================================
--- epan/dissectors/packet-smb.c	(revision 43181)
+++ epan/dissectors/packet-smb.c	(working copy)
@@ -8852,7 +8852,8 @@
 dissect_nt_transaction_request(tvbuff_t *tvb, packet_info *pinfo _U_,
proto_tree *tree, int offset, proto_tree *smb_tree _U_)
 {
 	guint8 wc, sc;
-	guint32 pc=0, po=0, dc=0, od=0;
+	guint32 pc=0, pd = 0, po=0, dc=0, od=0, dd = 0;
+	guint32 td=0, tp=0;
 	smb_info_t *si;
 	smb_saved_info_t *sip;
 	int subcmd;
@@ -8860,6 +8861,9 @@
 	guint16 bc;
 	guint32 padcnt;
 	smb_nt_transact_info_t *nti=NULL;
+	fragment_data *r_fd = NULL;
+	tvbuff_t *pd_tvb=NULL;
+	gboolean save_fragmented;

 	ntd.subcmd = ntd.sd_len = ntd.ea_len = 0;

@@ -8887,11 +8891,13 @@


 	/* total param count */
-	proto_tree_add_item(tree, hf_smb_total_param_count, tvb, offset, 4,
ENC_LITTLE_ENDIAN);
+	tp = tvb_get_letohl(tvb, offset);
+	proto_tree_add_uint(tree, hf_smb_total_param_count, tvb, offset, 4, tp);
 	offset += 4;

 	/* total data count */
-	proto_tree_add_item(tree, hf_smb_total_data_count, tvb, offset, 4,
ENC_LITTLE_ENDIAN);
+	td = tvb_get_letohl(tvb, offset);
+	proto_tree_add_uint(tree, hf_smb_total_data_count, tvb, offset, 4, td);
 	offset += 4;

 	if(wc>=19){
@@ -8940,7 +8946,8 @@
 		/* primary request */
 	} else {
 		/* secondary request */
-		proto_tree_add_item(tree, hf_smb_data_disp32, tvb, offset, 4,
ENC_LITTLE_ENDIAN);
+		dd = tvb_get_letohl(tvb, offset);
+		proto_tree_add_uint(tree, hf_smb_data_disp32, tvb, offset, 4, dd);
 		offset += 4;
 	}

@@ -9003,8 +9010,54 @@

 	BYTE_COUNT;

-	/* parameters */
-	if(po>(guint32)offset){
+	/* reassembly of SMB NT Transaction data payload.
+	   In this section we do reassembly of both the data and parameters
+	   blocks of the SMB transaction command.
+	*/
+	save_fragmented = pinfo->fragmented;
+	/* do we need reassembly? */
+	if( (td&&(td!=dc)) || (tp&&(tp!=pc)) ){
+		/* oh yeah, either data or parameter section needs
+		   reassembly...
+		*/
+		pinfo->fragmented = TRUE;
+		if(smb_trans_reassembly){
+			/* ...and we were told to do reassembly */
+			if(pc && ((unsigned int)tvb_length_remaining(tvb, po)>=pc) ){
+				r_fd = smb_trans_defragment(tree, pinfo, tvb,
+							     po, pc, pd, td+tp);
+			}
+			if((r_fd==NULL) && dc && ((unsigned int)tvb_length_remaining(tvb,
od)>=dc) ){
+				r_fd = smb_trans_defragment(tree, pinfo, tvb,
+							     od, dc, dd+tp, td+tp);
+			}
+		}
+	}
+
+	/* if we got a reassembled fd structure from the reassembly routine we
+	   must create pd_tvb from it
+	*/
+	if(r_fd){
+		proto_item *frag_tree_item;
+
+		pd_tvb = tvb_new_child_real_data(tvb, r_fd->data, r_fd->datalen,
+						 r_fd->datalen);
+		add_new_data_source(pinfo, pd_tvb, "Reassembled SMB");
+
+		show_fragment_tree(r_fd, &smb_frag_items, tree, pinfo, pd_tvb,
&frag_tree_item);
+	}
+
+	if(pd_tvb){
+	  /* we have reassembled data, grab param and data from there */
+	  dissect_nt_trans_param_request(pd_tvb, pinfo, 0, tree, tp,
+					  &ntd, (guint16) tvb_length(pd_tvb), nti);
+	  dissect_nt_trans_data_request(pd_tvb, pinfo, tp, tree, td, &ntd, nti);
+	  COUNT_BYTES(bc); /* We are done */
+	} else {
+	  /* we do not have reassembled data, just use what we have in the
+	     packet as well as we can */
+	  /* parameters */
+	  if(po>(guint32)offset){
 		/* We have some initial padding bytes.
 		*/
 		padcnt = po-offset;
@@ -9013,15 +9066,15 @@
 		CHECK_BYTE_COUNT(padcnt);
 	        proto_tree_add_item(tree, hf_smb_padding, tvb, offset,
padcnt, ENC_NA);
 		COUNT_BYTES(padcnt);
-	}
-	if(pc){
+	  }
+	  if(pc){
 		CHECK_BYTE_COUNT(pc);
 		dissect_nt_trans_param_request(tvb, pinfo, offset, tree, pc, &ntd, bc, nti);
 		COUNT_BYTES(pc);
-	}
+	  }

-	/* data */
-	if(od>(guint32)offset){
+	  /* data */
+	  if(od>(guint32)offset){
 		/* We have some initial padding bytes.
 		*/
 		padcnt = od-offset;
@@ -9029,12 +9082,13 @@
 			padcnt = bc;
 	        proto_tree_add_item(tree, hf_smb_padding, tvb, offset,
padcnt, ENC_NA);
 		COUNT_BYTES(padcnt);
-	}
-	if(dc){
+	  }
+	  if(dc){
 		CHECK_BYTE_COUNT(dc);
 		dissect_nt_trans_data_request(
 			tvb, pinfo, offset, tree, dc, &ntd, nti);
 		COUNT_BYTES(dc);
+	  }
 	}

 	END_OF_SMB
@@ -9549,6 +9603,7 @@
 	  dissect_nt_trans_param_response(pd_tvb, pinfo, 0, tree, tp,
 					  &ntd, (guint16) tvb_length(pd_tvb));
 	  dissect_nt_trans_data_response(pd_tvb, pinfo, tp, tree, td, &ntd, nti);
+	  COUNT_BYTES(bc); /* We are done */
 	} else {
 	  /* we do not have reassembled data, just use what we have in the
 	     packet as well as we can */
@@ -17213,6 +17268,8 @@
 		g_hash_table_destroy(ct->unmatched);
 	if (ct->matched)
 		g_hash_table_destroy(ct->matched);
+	if (ct->primaries)
+		g_hash_table_destroy(ct->primaries);
 	if (ct->tid_service)
 		g_hash_table_destroy(ct->tid_service);
 	g_free(ct);
@@ -17575,6 +17632,10 @@
 			smb_saved_info_equal_matched);
 		si->ct->unmatched= g_hash_table_new(smb_saved_info_hash_unmatched,
 			smb_saved_info_equal_unmatched);
+		/* We used the same key format as the unmatched entries */
+		si->ct->primaries=g_hash_table_new(
+			smb_saved_info_hash_unmatched,
+			smb_saved_info_equal_unmatched);
 		si->ct->tid_service=g_hash_table_new(
 			smb_saved_info_hash_unmatched,
 			smb_saved_info_equal_unmatched);
@@ -17640,6 +17701,12 @@
 				new_key->pid_mid = pid_mid;
 				g_hash_table_insert(si->ct->matched, new_key,
 				    sip);
+			} else {
+				if (si->cmd == SMB_COM_TRANSACTION_SECONDARY ||
+				    si->cmd == SMB_COM_TRANSACTION2_SECONDARY ||
+				    si->cmd == SMB_COM_NT_TRANSACT_SECONDARY) {
+					sip = g_hash_table_lookup(si->ct->primaries, GUINT_TO_POINTER(pid_mid));
+				}
 			}
 		} else {
 			/* we have seen this packet before; check the
@@ -17785,6 +17852,12 @@
 						sip=NULL;
 					}
 				}
+			} else {
+				if (si->cmd == SMB_COM_TRANSACTION ||
+				    si->cmd == SMB_COM_TRANSACTION2 ||
+				    si->cmd == SMB_COM_NT_TRANSACT) {
+					sip = g_hash_table_lookup(si->ct->primaries, GUINT_TO_POINTER(pid_mid));
+				}
 			}
 			if(si->request){
 				sip = se_alloc(sizeof(smb_saved_info_t));
@@ -17806,6 +17879,13 @@
 				new_key->frame = sip->frame_req;
 				new_key->pid_mid = pid_mid;
 				g_hash_table_insert(si->ct->matched, new_key, sip);
+
+				/* If it is a TRANSACT cmd, insert in hash */
+				if (si->cmd == SMB_COM_TRANSACTION ||
+				    si->cmd == SMB_COM_TRANSACTION2 ||
+				    si->cmd == SMB_COM_NT_TRANSACT) {
+					g_hash_table_insert(si->ct->primaries, GUINT_TO_POINTER(pid_mid), sip);
+				}
 			}
 		} else {
 			/* we have seen this packet before; check the
Index: epan/dissectors/packet-smb.h
===================================================================
--- epan/dissectors/packet-smb.h	(revision 42332)
+++ epan/dissectors/packet-smb.h	(working copy)
@@ -282,6 +282,9 @@
 	/* these two tables are used to match requests with responses */
 	GHashTable *unmatched;
 	GHashTable *matched;
+	/* This table keeps primary transact requests so secondaries can find
+	   them */
+	GHashTable *primaries;

 	/* This table is used to track TID->services for a conversation */
 	GHashTable *tid_service;



-- 
Regards,
Richard Sharpe
(何以解憂?唯有杜康。--曹操)

Attachment: packet-smb.nttrans.patch
Description: Binary data