From 5f1f1c47623f846909481073d56bc909d13e5e37 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 30 Sep 2009 14:27:26 +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 | 28 +++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/source/include/smb.h b/source/include/smb.h index 56d9461..327f212 100644 --- a/source/include/smb.h +++ b/source/include/smb.h @@ -715,6 +715,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; }; diff --git a/source/smbd/process.c b/source/smbd/process.c index 365c972..446b868 100644 --- a/source/smbd/process.c +++ b/source/smbd/process.c @@ -438,6 +438,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, @@ -493,6 +494,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; @@ -507,7 +518,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) @@ -515,7 +526,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; } } @@ -784,6 +795,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; } } @@ -1428,7 +1443,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; } @@ -1493,6 +1507,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; @@ -1508,6 +1523,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) != -- 1.6.0.2