From dff54f716bdd76e3d167dc96bba6e168ef58cadd Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra@samba.org>
Date: Wed, 30 Sep 2009 14:21:56 +0200
Subject: [PATCH] Fix for CVE-2009-2906.

Summary:
Specially crafted SMB requests on
authenticated SMB connections can send smbd
into a 100% CPU loop, causing a DoS on the
Samba server.
---
 source/include/smb.h  |    1 +
 source/smbd/process.c |   20 ++++++++++++++++----
 2 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/source/include/smb.h b/source/include/smb.h
index 7484efd..e512add 100644
--- a/source/include/smb.h
+++ b/source/include/smb.h
@@ -759,6 +759,7 @@ struct pending_message_list {
 	struct pending_message_list *next, *prev;
 	struct timeval request_time; /* When was this first issued? */
 	struct timeval end_time; /* When does this time out? */
+	bool processed;
 	DATA_BLOB buf;
 	DATA_BLOB private_data;
 };
diff --git a/source/smbd/process.c b/source/smbd/process.c
index cf29886..e861e16 100644
--- a/source/smbd/process.c
+++ b/source/smbd/process.c
@@ -93,6 +93,7 @@ static BOOL push_queued_message(char *buf, int msg_len,
 
 	msg->request_time = request_time;
 	msg->end_time = end_time;
+	msg->processed = false;
 
 	if (private_data) {
 		msg->private_data = data_blob_talloc(msg, private_data,
@@ -162,7 +163,7 @@ void schedule_deferred_open_smb_message(uint16 mid)
 }
 
 /****************************************************************************
- Return true if this mid is on the deferred queue.
+ Return true if this mid is on the deferred queue and was not yet processed.
 ****************************************************************************/
 
 BOOL open_was_deferred(uint16 mid)
@@ -170,7 +171,7 @@ BOOL open_was_deferred(uint16 mid)
 	struct pending_message_list *pml;
 
 	for (pml = deferred_open_queue; pml; pml = pml->next) {
-		if (SVAL(pml->buf.data,smb_mid) == mid) {
+		if (SVAL(pml->buf.data,smb_mid) == mid && !pml->processed) {
 			return True;
 		}
 	}
@@ -409,6 +410,10 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
 			/* We leave this message on the queue so the open code can
 			   know this is a retry. */
 			DEBUG(5,("receive_message_or_smb: returning deferred open smb message.\n"));
+
+			/* Mark the message as processed so this is not
+			 * re-processed in error. */
+			msg->processed = true;
 			return True;
 		}
 	}
@@ -967,8 +972,6 @@ static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize
 			}
 
 			if (!change_to_user(conn,session_tag)) {
-				remove_deferred_open_smb_message(
-					SVAL(inbuf, smb_mid));
 				return(ERROR_NT(NT_STATUS_DOS(ERRSRV,ERRbaduid)));
 			}
 
@@ -1017,9 +1020,11 @@ static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize
 
 static int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
 {
+	struct pending_message_list *pml = NULL;
 	int type = CVAL(inbuf,smb_com);
 	int outsize = 0;
 	int msg_type = CVAL(inbuf,0);
+	uint16_t mid = SVAL(inbuf, smb_mid);
 
 	chain_size = 0;
 	file_chain_reset();
@@ -1032,6 +1037,13 @@ static int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
 
 	outsize = switch_message(type,inbuf,outbuf,size,bufsize);
 
+	/* If this was a deferred message and it's still there and
+	 * was processed, remove it. */
+	pml = get_open_deferred_message(mid);
+	if (pml && pml->processed) {
+		remove_deferred_open_smb_message(mid);
+	}
+
 	outsize += chain_size;
 
 	if(outsize > 4)
-- 
1.6.0.2