Fix for CVE-2009-2906.
authorJeremy Allison <jra@samba.org>
Wed, 30 Sep 2009 12:24:50 +0000 (14:24 +0200)
committerVolker Lendecke <vl@samba.org>
Fri, 22 Jan 2010 10:21:59 +0000 (11:21 +0100)
Summary:
Specially crafted SMB requests on
authenticated SMB connections can send smbd
into a 100% CPU loop, causing a DoS on the
Samba server.

Signed-off-by: Christian Ambach <christian.ambach@de.ibm.com>
source/include/smb.h
source/smbd/process.c

index dc346d8ad949054cefef1ed5b3266192e5c0eafe..960984f19e8a53cde90fa4558da8f3b02e75c618 100644 (file)
@@ -759,6 +759,7 @@ struct pending_message_list {
        struct timeval request_time; /* When was this first issued? */
        struct timeval end_time; /* When does this time out? */
        bool encrypted;
+       bool processed;
        DATA_BLOB buf;
        DATA_BLOB private_data;
 };
index ad01a52b39cdc9fb4440a6ab757287f5290c1228..c53bfda2219a6be756479b7afb09e1adff590c02 100644 (file)
@@ -434,6 +434,7 @@ static bool push_queued_message(struct smb_request *req,
        msg->request_time = request_time;
        msg->end_time = end_time;
        msg->encrypted = req->encrypted;
+       msg->processed = false;
 
        if (private_data) {
                msg->private_data = data_blob_talloc(msg, private_data,
@@ -489,6 +490,16 @@ void schedule_deferred_open_smb_message(uint16 mid)
                DEBUG(10,("schedule_deferred_open_smb_message: [%d] msg_mid = %u\n", i++,
                        (unsigned int)msg_mid ));
                if (mid == msg_mid) {
+
+                       if (pml->processed) {
+                               /* A processed message should not be
+                                * rescheduled. */
+                               DEBUG(0,("schedule_deferred_open_smb_message: LOGIC ERROR "
+                                       "message mid %u was already processed\n",
+                                       (unsigned int)msg_mid ));
+                               continue;
+                       }
+
                        DEBUG(10,("schedule_deferred_open_smb_message: scheduling mid %u\n",
                                mid ));
                        pml->end_time.tv_sec = 0;
@@ -503,7 +514,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)
@@ -511,7 +522,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;
                }
        }
@@ -778,6 +789,10 @@ static NTSTATUS receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
                        /* 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 NT_STATUS_OK;
                }
        }
@@ -1405,7 +1420,6 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
 
                if (!change_to_user(conn,session_tag)) {
                        reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRbaduid));
-                       remove_deferred_open_smb_message(req->mid);
                        return conn;
                }
 
@@ -1470,6 +1484,7 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
 
 static void construct_reply(char *inbuf, int size, size_t unread_bytes, bool encrypted)
 {
+       struct pending_message_list *pml = NULL;
        uint8 type = CVAL(inbuf,smb_com);
        connection_struct *conn;
        struct smb_request *req;
@@ -1485,6 +1500,13 @@ static void construct_reply(char *inbuf, int size, size_t unread_bytes, bool enc
 
        conn = switch_message(type, req, size);
 
+       /* If this was a deferred message and it's still there and
+        * was processed, remove it. */
+       pml = get_open_deferred_message(req->mid);
+       if (pml && pml->processed) {
+               remove_deferred_open_smb_message(req->mid);
+       }
+
        if (req->unread_bytes) {
                /* writeX failed. drain socket. */
                if (drain_socket(smbd_server_fd(), req->unread_bytes) !=