Fix for CVE-2009-2906.
[samba.git] / source / smbd / process.c
index 1771f552cc25247d0b5f3d50f3619a5bb404de97..c53bfda2219a6be756479b7afb09e1adff590c02 100644 (file)
@@ -164,7 +164,7 @@ static NTSTATUS receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
        ssize_t toread;
        NTSTATUS status;
 
-       memcpy(writeX_header, lenbuf, sizeof(lenbuf));
+       memcpy(writeX_header, lenbuf, 4);
 
        status = read_socket_with_timeout(
                fd, writeX_header + 4,
@@ -279,7 +279,7 @@ static NTSTATUS receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, int fd,
 
        if (CVAL(lenbuf,0) == 0 &&
                        min_recv_size &&
-                       smb_len_large(lenbuf) > min_recv_size && /* Could be a UNIX large writeX. */
+                       smb_len_large(lenbuf) > (min_recv_size + STANDARD_WRITE_AND_X_HEADER_SIZE) && /* Could be a UNIX large writeX. */
                        !srv_is_signing_active()) {
 
                return receive_smb_raw_talloc_partial_read(
@@ -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;
                }
        }
@@ -1033,7 +1048,7 @@ static const struct smb_message_struct {
 /* 0x30 */ { NULL, NULL, 0 },
 /* 0x31 */ { NULL, NULL, 0 },
 /* 0x32 */ { "SMBtrans2",reply_trans2, AS_USER | CAN_IPC },
-/* 0x33 */ { "SMBtranss2",reply_transs2, AS_USER},
+/* 0x33 */ { "SMBtranss2",reply_transs2, AS_USER | CAN_IPC},
 /* 0x34 */ { "SMBfindclose",reply_findclose,AS_USER},
 /* 0x35 */ { "SMBfindnclose",reply_findnclose,AS_USER},
 /* 0x36 */ { NULL, NULL, 0 },
@@ -1469,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;
@@ -1484,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) !=
@@ -1636,6 +1659,7 @@ void chain_reply(struct smb_request *req)
        char *outbuf = (char *)req->outbuf;
        size_t outsize = smb_len(outbuf) + 4;
        size_t outsize_padded;
+       size_t padding;
        size_t ofs, to_move;
 
        struct smb_request *req2;
@@ -1674,6 +1698,7 @@ void chain_reply(struct smb_request *req)
         */
 
        outsize_padded = (outsize + 3) & ~3;
+       padding = outsize_padded - outsize;
 
        /*
         * remember how much the caller added to the chain, only counting
@@ -1787,17 +1812,17 @@ void chain_reply(struct smb_request *req)
        SCVAL(outbuf, smb_vwv0, smb_com2);
        SSVAL(outbuf, smb_vwv1, chain_size + smb_wct - 4);
 
-       if (outsize_padded > outsize) {
+       if (padding != 0) {
 
                /*
                 * Due to padding we have some uninitialized bytes after the
                 * caller's output
                 */
 
-               memset(outbuf + outsize, 0, outsize_padded - outsize);
+               memset(outbuf + outsize, 0, padding);
        }
 
-       smb_setlen(outbuf, outsize2 + chain_size - 4);
+       smb_setlen(outbuf, outsize2 + caller_outputlen + padding - 4);
 
        /*
         * restore the saved data, being careful not to overwrite any data