diff options
author | Jeremy Allison <jra@samba.org> | 2009-09-30 14:24:50 +0200 |
---|---|---|
committer | Karolin Seeger <kseeger@samba.org> | 2009-10-01 14:26:00 +0200 |
commit | 0e51f10a96acb5389a1326b4a85646b1cbac705d (patch) | |
tree | 25e45a88da07bb05d03b54898fc7f0c06c0d8737 | |
parent | 4e25072de62595b491acf3f5003b309f8b22a78b (diff) | |
download | samba-v3-2-test.tar.gz samba-v3-2-test.tar.xz samba-v3-2-test.zip |
Fix for CVE-2009-2906.v3-2-test
Summary:
Specially crafted SMB requests on
authenticated SMB connections can send smbd
into a 100% CPU loop, causing a DoS on the
Samba server.
(cherry picked from commit e2dff319a6d5c0c54acdba3d4fec05477ca60f11)
-rw-r--r-- | source/include/smb.h | 1 | ||||
-rw-r--r-- | 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 dc346d8ad94..960984f19e8 100644 --- a/source/include/smb.h +++ b/source/include/smb.h @@ -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; }; diff --git a/source/smbd/process.c b/source/smbd/process.c index ad01a52b39c..c53bfda2219 100644 --- a/source/smbd/process.c +++ b/source/smbd/process.c @@ -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) != |