Fix for CVE-2009-2906.
[samba.git] / source / smbd / process.c
index eca51a74a36d88694f07b4bfcc0161bb0304b37a..c53bfda2219a6be756479b7afb09e1adff590c02 100644 (file)
 
 #include "includes.h"
 
-extern struct auth_context *negprot_global_auth_context;
 extern int smb_echo_count;
-extern int smb_read_error;
-
-const int total_buffer_size = (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN);
 
 /*
  * Size of data we can send to client. Set
@@ -38,70 +34,105 @@ int max_send = BUFFER_SIZE;
  */
 int max_recv = BUFFER_SIZE;
 
-extern int last_message;
-extern int smb_read_error;
 SIG_ATOMIC_T reload_after_sighup = 0;
 SIG_ATOMIC_T got_sig_term = 0;
 extern bool global_machine_password_needs_changing;
 extern int max_send;
 
-/* Socket functions for smbd packet processing. */
+/* Accessor function for smb_read_error for smbd functions. */
+
+/****************************************************************************
+ Send an smb to a fd.
+****************************************************************************/
 
-static bool valid_packet_size(len)
+bool srv_send_smb(int fd, char *buffer, bool do_encrypt)
 {
-       /*
-        * A WRITEX with CAP_LARGE_WRITEX can be 64k worth of data plus 65 bytes
-        * of header. Don't print the error if this fits.... JRA.
-        */
+       size_t len;
+       size_t nwritten=0;
+       ssize_t ret;
+       char *buf_out = buffer;
 
-       if (len > (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE)) {
-               DEBUG(0,("Invalid packet length! (%lu bytes).\n",
-                                       (unsigned long)len));
-               if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) {
+       /* Sign the outgoing packet if required. */
+       srv_calculate_sign_mac(buf_out);
 
-                       /*
-                        * Correct fix. smb_read_error may have already been
-                        * set. Only set it here if not already set. Global
-                        * variables still suck :-). JRA.
-                        */
+       if (do_encrypt) {
+               NTSTATUS status = srv_encrypt_buffer(buffer, &buf_out);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("send_smb: SMB encryption failed "
+                               "on outgoing packet! Error %s\n",
+                               nt_errstr(status) ));
+                       return false;
+               }
+       }
+
+       len = smb_len(buf_out) + 4;
 
-                       if (smb_read_error == 0)
-                               smb_read_error = READ_ERROR;
+       while (nwritten < len) {
+               ret = write_data(fd,buf_out+nwritten,len - nwritten);
+               if (ret <= 0) {
+                       DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n",
+                               (int)len,(int)ret, strerror(errno) ));
+                       srv_free_enc_buffer(buf_out);
                        return false;
                }
+               nwritten += ret;
        }
+
+       srv_free_enc_buffer(buf_out);
        return true;
 }
 
-static ssize_t read_packet_remainder(int fd,
-                                       char *buffer,
-                                       unsigned int timeout,
-                                       ssize_t len)
+/*******************************************************************
+ Setup the word count and byte count for a smb message.
+********************************************************************/
+
+int srv_set_message(char *buf,
+                        int num_words,
+                        int num_bytes,
+                        bool zero)
 {
-       ssize_t ret;
+       if (zero && (num_words || num_bytes)) {
+               memset(buf + smb_size,'\0',num_words*2 + num_bytes);
+       }
+       SCVAL(buf,smb_wct,num_words);
+       SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
+       smb_setlen(buf,(smb_size + num_words*2 + num_bytes - 4));
+       return (smb_size + num_words*2 + num_bytes);
+}
 
-       if(len <= 0) {
-               return len;
+static bool valid_smb_header(const uint8_t *inbuf)
+{
+       if (is_encrypted_packet(inbuf)) {
+               return true;
        }
+       return (strncmp(smb_base(inbuf),"\377SMB",4) == 0);
+}
 
-       if (timeout > 0) {
-               ret = read_socket_with_timeout(fd,
-                                               buffer,
-                                               len,
-                                               len,
-                                               timeout);
-       } else {
-               ret = read_data(fd, buffer, len);
+/* Socket functions for smbd packet processing. */
+
+static bool valid_packet_size(size_t len)
+{
+       /*
+        * A WRITEX with CAP_LARGE_WRITEX can be 64k worth of data plus 65 bytes
+        * of header. Don't print the error if this fits.... JRA.
+        */
+
+       if (len > (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE)) {
+               DEBUG(0,("Invalid packet length! (%lu bytes).\n",
+                                       (unsigned long)len));
+               return false;
        }
+       return true;
+}
 
-       if (ret != len) {
-               if (smb_read_error == 0) {
-                       smb_read_error = READ_ERROR;
-               }
-               return -1;
+static NTSTATUS read_packet_remainder(int fd, char *buffer,
+                                     unsigned int timeout, ssize_t len)
+{
+       if (len <= 0) {
+               return NT_STATUS_OK;
        }
 
-       return len;
+       return read_socket_with_timeout(fd, buffer, len, len, timeout, NULL);
 }
 
 /****************************************************************************
@@ -120,38 +151,29 @@ static ssize_t read_packet_remainder(int fd,
                                (2*14) + /* word count (including bcc) */ \
                                1 /* pad byte */)
 
-ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
-                                       const char lenbuf[4],
-                                       int fd,
-                                       char **buffer,
-                                       unsigned int timeout,
-                                       size_t *p_unread)
+static NTSTATUS receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
+                                                   const char lenbuf[4],
+                                                   int fd, char **buffer,
+                                                   unsigned int timeout,
+                                                   size_t *p_unread,
+                                                   size_t *len_ret)
 {
        /* Size of a WRITEX call (+4 byte len). */
        char writeX_header[4 + STANDARD_WRITE_AND_X_HEADER_SIZE];
        ssize_t len = smb_len_large(lenbuf); /* Could be a UNIX large writeX. */
        ssize_t toread;
-       ssize_t ret;
+       NTSTATUS status;
 
-       memcpy(writeX_header, lenbuf, sizeof(lenbuf));
+       memcpy(writeX_header, lenbuf, 4);
 
-       if (timeout > 0) {
-               ret = read_socket_with_timeout(fd,
-                                       writeX_header + 4,
-                                       STANDARD_WRITE_AND_X_HEADER_SIZE,
-                                       STANDARD_WRITE_AND_X_HEADER_SIZE,
-                                       timeout);
-       } else {
-               ret = read_data(fd,
-                               writeX_header+4,
-                               STANDARD_WRITE_AND_X_HEADER_SIZE);
-       }
+       status = read_socket_with_timeout(
+               fd, writeX_header + 4,
+               STANDARD_WRITE_AND_X_HEADER_SIZE,
+               STANDARD_WRITE_AND_X_HEADER_SIZE,
+               timeout, NULL);
 
-       if (ret != STANDARD_WRITE_AND_X_HEADER_SIZE) {
-               if (smb_read_error == 0) {
-                       smb_read_error = READ_ERROR;
-               }
-               return -1;
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
        /*
@@ -159,7 +181,7 @@ ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
         * valid writeX call.
         */
 
-       if (is_valid_writeX_buffer(writeX_header)) {
+       if (is_valid_writeX_buffer((uint8_t *)writeX_header)) {
                /*
                 * If the data offset is beyond what
                 * we've read, drain the extra bytes.
@@ -183,26 +205,24 @@ ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
 
                /* Copy the header we've written. */
 
-               *buffer = TALLOC_MEMDUP(mem_ctx,
+               *buffer = (char *)TALLOC_MEMDUP(mem_ctx,
                                writeX_header,
                                sizeof(writeX_header));
 
                if (*buffer == NULL) {
                        DEBUG(0, ("Could not allocate inbuf of length %d\n",
                                  (int)sizeof(writeX_header)));
-                       if (smb_read_error == 0)
-                               smb_read_error = READ_ERROR;
-                       return -1;
+                       return NT_STATUS_NO_MEMORY;
                }
 
                /* Work out the remaining bytes. */
                *p_unread = len - STANDARD_WRITE_AND_X_HEADER_SIZE;
-
-               return newlen + 4;
+               *len_ret = newlen + 4;
+               return NT_STATUS_OK;
        }
 
        if (!valid_packet_size(len)) {
-               return -1;
+               return NT_STATUS_INVALID_PARAMETER;
        }
 
        /*
@@ -215,9 +235,7 @@ ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
        if (*buffer == NULL) {
                DEBUG(0, ("Could not allocate inbuf of length %d\n",
                          (int)len+4));
-               if (smb_read_error == 0)
-                       smb_read_error = READ_ERROR;
-               return -1;
+               return NT_STATUS_NO_MEMORY;
        }
 
        /* Copy in what we already read. */
@@ -227,61 +245,49 @@ ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
        toread = len - STANDARD_WRITE_AND_X_HEADER_SIZE;
 
        if(toread > 0) {
-               ret = read_packet_remainder(fd,
-                       (*buffer) + 4 + STANDARD_WRITE_AND_X_HEADER_SIZE,
-                                       timeout,
-                                       toread);
-               if (ret != toread) {
-                       return -1;
+               status = read_packet_remainder(
+                       fd, (*buffer) + 4 + STANDARD_WRITE_AND_X_HEADER_SIZE,
+                       timeout, toread);
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(10, ("receive_smb_raw_talloc_partial_read: %s\n",
+                                  nt_errstr(status)));
+                       return status;
                }
        }
 
-       return len + 4;
+       *len_ret = len + 4;
+       return NT_STATUS_OK;
 }
 
-static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx,
-                                       int fd,
-                                       char **buffer,
-                                       unsigned int timeout,
-                                       size_t *p_unread)
+static NTSTATUS receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, int fd,
+                                      char **buffer, unsigned int timeout,
+                                      size_t *p_unread, size_t *plen)
 {
        char lenbuf[4];
-       ssize_t len,ret;
+       size_t len;
        int min_recv_size = lp_min_receive_file_size();
+       NTSTATUS status;
 
-       smb_read_error = 0;
        *p_unread = 0;
 
-       len = read_smb_length_return_keepalive(fd, lenbuf, timeout);
-       if (len < 0) {
-               DEBUG(10,("receive_smb_raw: length < 0!\n"));
-
-               /*
-                * Correct fix. smb_read_error may have already been
-                * set. Only set it here if not already set. Global
-                * variables still suck :-). JRA.
-                */
-
-               if (smb_read_error == 0)
-                       smb_read_error = READ_ERROR;
-               return -1;
+       status = read_smb_length_return_keepalive(fd, lenbuf, timeout, &len);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("receive_smb_raw: %s\n", nt_errstr(status)));
+               return status;
        }
 
-       if (CVAL(lenbuf,0) != SMBkeepalive &&
+       if (CVAL(lenbuf,0) == 0 &&
                        min_recv_size &&
-                       len > min_recv_size &&
+                       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(mem_ctx,
-                                                       lenbuf,
-                                                       fd,
-                                                       buffer,
-                                                       timeout,
-                                                       p_unread);
+               return receive_smb_raw_talloc_partial_read(
+                       mem_ctx, lenbuf, fd, buffer, timeout, p_unread, plen);
        }
 
        if (!valid_packet_size(len)) {
-               return -1;
+               return NT_STATUS_INVALID_PARAMETER;
        }
 
        /*
@@ -293,43 +299,56 @@ static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx,
        if (*buffer == NULL) {
                DEBUG(0, ("Could not allocate inbuf of length %d\n",
                          (int)len+4));
-               if (smb_read_error == 0)
-                       smb_read_error = READ_ERROR;
-               return -1;
+               return NT_STATUS_NO_MEMORY;
        }
 
        memcpy(*buffer, lenbuf, sizeof(lenbuf));
 
-       ret = read_packet_remainder(fd, (*buffer)+4, timeout, len);
-       if (ret != len) {
-               return -1;
+       status = read_packet_remainder(fd, (*buffer)+4, timeout, len);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       return len + 4;
+       *plen = len + 4;
+       return NT_STATUS_OK;
 }
 
-ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer,
-                          unsigned int timeout, size_t *p_unread)
+static NTSTATUS receive_smb_talloc(TALLOC_CTX *mem_ctx,        int fd,
+                                  char **buffer, unsigned int timeout,
+                                  size_t *p_unread, bool *p_encrypted,
+                                  size_t *p_len)
 {
-       ssize_t len;
+       size_t len = 0;
+       NTSTATUS status;
+
+       *p_encrypted = false;
 
-       len = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout, p_unread);
+       status = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout,
+                                       p_unread, &len);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
-       if (len < 0) {
-               return -1;
+       if (is_encrypted_packet((uint8_t *)*buffer)) {
+               status = srv_decrypt_buffer(*buffer);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("receive_smb_talloc: SMB decryption failed on "
+                               "incoming packet! Error %s\n",
+                               nt_errstr(status) ));
+                       return status;
+               }
+               *p_encrypted = true;
        }
 
        /* Check the incoming SMB signature. */
        if (!srv_check_sign_mac(*buffer, true)) {
                DEBUG(0, ("receive_smb: SMB Signature verification failed on "
                          "incoming packet!\n"));
-               if (smb_read_error == 0) {
-                       smb_read_error = READ_BAD_SIG;
-               }
-               return -1;
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
        }
 
-       return len;
+       *p_len = len;
+       return NT_STATUS_OK;
 }
 
 /*
@@ -338,7 +357,8 @@ ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer,
 
 void init_smb_request(struct smb_request *req,
                        const uint8 *inbuf,
-                       size_t unread_bytes)
+                       size_t unread_bytes,
+                       bool encrypted)
 {
        size_t req_size = smb_len(inbuf) + 4;
        /* Ensure we have at least smb_size bytes. */
@@ -354,6 +374,8 @@ void init_smb_request(struct smb_request *req,
        req->tid    = SVAL(inbuf, smb_tid);
        req->wct    = CVAL(inbuf, smb_wct);
        req->unread_bytes = unread_bytes;
+       req->encrypted = encrypted;
+       req->conn = conn_find(req->tid);
 
        /* Ensure we have at least wct words and 2 bytes of bcc. */
        if (smb_size + req->wct*2 > req_size) {
@@ -411,6 +433,8 @@ 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,
@@ -466,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;
@@ -480,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)
@@ -488,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;
                }
        }
@@ -682,20 +716,18 @@ static int select_on_fd(int fd, int maxfd, fd_set *fds)
 The timeout is in milliseconds
 ****************************************************************************/
 
-static bool receive_message_or_smb(TALLOC_CTX *mem_ctx,
-                               char **buffer,
-                               size_t *buffer_len,
-                               int timeout,
-                               size_t *p_unread)
+static NTSTATUS receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
+                                      size_t *buffer_len, int timeout,
+                                      size_t *p_unread, bool *p_encrypted)
 {
        fd_set r_fds, w_fds;
        int selrtn;
        struct timeval to;
        int maxfd = 0;
-       ssize_t len;
+       size_t len = 0;
+       NTSTATUS status;
 
        *p_unread = 0;
-       smb_read_error = 0;
 
  again:
 
@@ -749,15 +781,19 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx,
                                                        msg->buf.length);
                        if (*buffer == NULL) {
                                DEBUG(0, ("talloc failed\n"));
-                               smb_read_error = READ_ERROR;
-                               return False;
+                               return NT_STATUS_NO_MEMORY;
                        }
                        *buffer_len = msg->buf.length;
+                       *p_encrypted = msg->encrypted;
 
                        /* 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"));
-                       return True;
+
+                       /* Mark the message as processed so this is not
+                        * re-processed in error. */
+                       msg->processed = true;
+                       return NT_STATUS_OK;
                }
        }
 
@@ -843,14 +879,12 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx,
        /* Check if error */
        if (selrtn == -1) {
                /* something is wrong. Maybe the socket is dead? */
-               smb_read_error = READ_ERROR;
-               return False;
-       } 
-    
+               return map_nt_error_from_unix(errno);
+       }
+
        /* Did we timeout ? */
        if (selrtn == 0) {
-               smb_read_error = READ_TIMEOUT;
-               return False;
+               return NT_STATUS_IO_TIMEOUT;
        }
 
        /*
@@ -869,15 +903,25 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx,
                goto again;
        }
 
-       len = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0, p_unread);
+       /*
+        * We've just woken up from a protentially long select sleep.
+        * Ensure we process local messages as we need to synchronously
+        * process any messages from other smbd's to avoid file rename race
+        * conditions. This call is cheap if there are no messages waiting.
+        * JRA.
+        */
+       message_dispatch(smbd_messaging_context());
 
-       if (len == -1) {
-               return False;
+       status = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0,
+                                   p_unread, p_encrypted, &len);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
        }
 
-       *buffer_len = (size_t)len;
+       *buffer_len = len;
 
-       return True;
+       return NT_STATUS_OK;
 }
 
 /*
@@ -949,7 +993,7 @@ force write permissions on print services.
 */
 static const struct smb_message_struct {
        const char *name;
-       void (*fn_new)(connection_struct *conn, struct smb_request *req);
+       void (*fn_new)(struct smb_request *req);
        int flags;
 } smb_messages[256] = {
 
@@ -1004,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 },
@@ -1224,8 +1268,10 @@ void reply_outbuf(struct smb_request *req, uint8 num_words, uint32 num_bytes)
        if ((num_bytes > 0xffffff)
            || ((num_bytes + smb_size + num_words*2) > 0xffffff)) {
                char *msg;
-               asprintf(&msg, "num_bytes too large: %u",
-                        (unsigned)num_bytes);
+               if (asprintf(&msg, "num_bytes too large: %u",
+                            (unsigned)num_bytes) == -1) {
+                       msg = CONST_DISCARD(char *, "num_bytes too large");
+               }
                smb_panic(msg);
        }
 
@@ -1236,7 +1282,7 @@ void reply_outbuf(struct smb_request *req, uint8 num_words, uint32 num_bytes)
        }
 
        construct_reply_common((char *)req->inbuf, (char *)req->outbuf);
-       set_message((char *)req->outbuf, num_words, num_bytes, False);
+       srv_set_message((char *)req->outbuf, num_words, num_bytes, false);
        /*
         * Zero out the word area, the caller has to take care of the bcc area
         * himself
@@ -1256,13 +1302,17 @@ void reply_outbuf(struct smb_request *req, uint8 num_words, uint32 num_bytes)
 static void smb_dump(const char *name, int type, const char *data, ssize_t len)
 {
        int fd, i;
-       pstring fname;
-       if (DEBUGLEVEL < 50) return;
+       char *fname = NULL;
+       if (DEBUGLEVEL < 50) {
+               return;
+       }
 
        if (len < 4) len = smb_len(data)+4;
        for (i=1;i<100;i++) {
-               slprintf(fname,sizeof(fname)-1, "/tmp/%s.%d.%s", name, i,
-                               type ? "req" : "resp");
+               if (asprintf(&fname, "/tmp/%s.%d.%s", name, i,
+                            type ? "req" : "resp") == -1) {
+                       return;
+               }
                fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0644);
                if (fd != -1 || errno != EEXIST) break;
        }
@@ -1273,6 +1323,7 @@ static void smb_dump(const char *name, int type, const char *data, ssize_t len)
                close(fd);
                DEBUG(0,("created %s len %lu\n", fname, (unsigned long)len));
        }
+       SAFE_FREE(fname);
 }
 
 /****************************************************************************
@@ -1288,21 +1339,19 @@ static void smb_dump(const char *name, int type, const char *data, ssize_t len)
  find.
 ****************************************************************************/
 
-static void switch_message(uint8 type, struct smb_request *req, int size)
+static connection_struct *switch_message(uint8 type, struct smb_request *req, int size)
 {
        int flags;
        uint16 session_tag;
-       connection_struct *conn;
+       connection_struct *conn = NULL;
 
        static uint16 last_session_tag = UID_FIELD_INVALID;
 
        errno = 0;
 
-       last_message = type;
-
        /* Make sure this is an SMB packet. smb_size contains NetBIOS header
         * so subtract 4 from it. */
-       if ((strncmp(smb_base(req->inbuf),"\377SMB",4) != 0)
+       if (!valid_smb_header(req->inbuf)
            || (size < (smb_size - 4))) {
                DEBUG(2,("Non-SMB packet of length %d. Terminating server\n",
                         smb_len(req->inbuf)));
@@ -1313,7 +1362,7 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
                DEBUG(0,("Unknown message type %d!\n",type));
                smb_dump("Unknown", 1, (char *)req->inbuf, size);
                reply_unknown_new(req, type);
-               return;
+               return NULL;
        }
 
        flags = smb_messages[type].flags;
@@ -1321,7 +1370,7 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
        /* In share mode security we must ignore the vuid. */
        session_tag = (lp_security() == SEC_SHARE)
                ? UID_FIELD_INVALID : req->vuid;
-       conn = conn_find(req->tid);
+       conn = req->conn;
 
        DEBUG(3,("switch message %s (pid %d) conn 0x%lx\n", smb_fn_name(type),
                 (int)sys_getpid(), (unsigned long)conn));
@@ -1366,12 +1415,12 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
                        } else {
                                reply_doserror(req, ERRSRV, ERRinvnid);
                        }
-                       return;
+                       return NULL;
                }
 
                if (!change_to_user(conn,session_tag)) {
                        reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRbaduid));
-                       return;
+                       return conn;
                }
 
                /* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */
@@ -1379,13 +1428,13 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
                /* Does it need write permission? */
                if ((flags & NEED_WRITE) && !CAN_WRITE(conn)) {
                        reply_nterror(req, NT_STATUS_MEDIA_WRITE_PROTECTED);
-                       return;
+                       return conn;
                }
 
                /* IPC services are limited */
                if (IS_IPC(conn) && !(flags & CAN_IPC)) {
                        reply_doserror(req, ERRSRV,ERRaccess);
-                       return;
+                       return conn;
                }
        } else {
                /* This call needs to be run as root */
@@ -1394,11 +1443,24 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
 
        /* load service specific parameters */
        if (conn) {
+               if (req->encrypted) {
+                       conn->encrypted_tid = true;
+                       /* encrypted required from now on. */
+                       conn->encrypt_level = Required;
+               } else if (ENCRYPTION_REQUIRED(conn)) {
+                       uint8 com = CVAL(req->inbuf,smb_com);
+                       if (com != SMBtrans2 && com != SMBtranss2) {
+                               exit_server_cleanly("encryption required "
+                                       "on connection");
+                               return conn;
+                       }
+               }
+
                if (!set_current_service(conn,SVAL(req->inbuf,smb_flg),
                                         (flags & (AS_USER|DO_CHDIR)
                                          ?True:False))) {
                        reply_doserror(req, ERRSRV, ERRaccess);
-                       return;
+                       return conn;
                }
                conn->num_smb_operations++;
        }
@@ -1409,19 +1471,22 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
                !check_access(smbd_server_fd(), lp_hostsallow(-1),
                              lp_hostsdeny(-1)))) {
                reply_doserror(req, ERRSRV, ERRaccess);
-               return;
+               return conn;
        }
 
-       smb_messages[type].fn_new(conn, req);
+       smb_messages[type].fn_new(req);
+       return req->conn;
 }
 
 /****************************************************************************
  Construct a reply to the incoming packet.
 ****************************************************************************/
 
-static void construct_reply(char *inbuf, int size, size_t unread_bytes)
+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;
 
        chain_size = 0;
@@ -1431,9 +1496,16 @@ static void construct_reply(char *inbuf, int size, size_t unread_bytes)
        if (!(req = talloc(talloc_tos(), struct smb_request))) {
                smb_panic("could not allocate smb_request");
        }
-       init_smb_request(req, (uint8 *)inbuf, unread_bytes);
+       init_smb_request(req, (uint8 *)inbuf, unread_bytes, encrypted);
+
+       conn = switch_message(type, req, size);
 
-       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. */
@@ -1452,8 +1524,10 @@ static void construct_reply(char *inbuf, int size, size_t unread_bytes)
                show_msg((char *)req->outbuf);
        }
 
-       if (!send_smb(smbd_server_fd(), (char *)req->outbuf)) {
-               exit_server_cleanly("construct_reply: send_smb failed.");
+       if (!srv_send_smb(smbd_server_fd(),
+                       (char *)req->outbuf,
+                       IS_CONN_ENCRYPTED(conn)||req->encrypted)) {
+               exit_server_cleanly("construct_reply: srv_send_smb failed.");
        }
 
        TALLOC_FREE(req);
@@ -1465,7 +1539,7 @@ static void construct_reply(char *inbuf, int size, size_t unread_bytes)
  Process an smb from the client
 ****************************************************************************/
 
-static void process_smb(char *inbuf, size_t nread, size_t unread_bytes)
+static void process_smb(char *inbuf, size_t nread, size_t unread_bytes, bool encrypted)
 {
        static int trans_num;
        int msg_type = CVAL(inbuf,0);
@@ -1473,16 +1547,20 @@ static void process_smb(char *inbuf, size_t nread, size_t unread_bytes)
        DO_PROFILE_INC(smb_count);
 
        if (trans_num == 0) {
+               char addr[INET6_ADDRSTRLEN];
+
                /* on the first packet, check the global hosts allow/ hosts
                deny parameters before doing any parsing of the packet
                passed to us by the client.  This prevents attacks on our
                parsing code from hosts not in the hosts allow list */
+
                if (!check_access(smbd_server_fd(), lp_hostsallow(-1),
                                  lp_hostsdeny(-1))) {
                        /* send a negative session response "not listening on calling name" */
                        static unsigned char buf[5] = {0x83, 0, 0, 1, 0x81};
-                       DEBUG( 1, ( "Connection denied from %s\n", client_addr() ) );
-                       (void)send_smb(smbd_server_fd(),(char *)buf);
+                       DEBUG( 1, ( "Connection denied from %s\n",
+                               client_addr(get_client_fd(),addr,sizeof(addr)) ) );
+                       (void)srv_send_smb(smbd_server_fd(),(char *)buf,false);
                        exit_server_cleanly("connection denied");
                }
        }
@@ -1503,7 +1581,7 @@ static void process_smb(char *inbuf, size_t nread, size_t unread_bytes)
 
        show_msg(inbuf);
 
-       construct_reply(inbuf,nread,unread_bytes);
+       construct_reply(inbuf,nread,unread_bytes,encrypted);
 
        trans_num++;
 }
@@ -1540,7 +1618,7 @@ void remove_from_common_flags2(uint32 v)
 
 void construct_reply_common(const char *inbuf, char *outbuf)
 {
-       set_message(outbuf,0,0,False);
+       srv_set_message(outbuf,0,0,false);
        
        SCVAL(outbuf,smb_com,CVAL(inbuf,smb_com));
        SIVAL(outbuf,smb_rcls,0);
@@ -1581,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;
@@ -1619,12 +1698,13 @@ 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
         * stuff after the parameter words
         */
-       chain_size += outsize_padded - smb_wct;
+       chain_size += (outsize_padded - smb_wct);
 
        /*
         * work out pointers into the original packets. The
@@ -1663,7 +1743,7 @@ void chain_reply(struct smb_request *req)
        if (!(req2 = talloc(talloc_tos(), struct smb_request))) {
                smb_panic("could not allocate smb_request");
        }
-       init_smb_request(req2, (uint8 *)inbuf2,0);
+       init_smb_request(req2, (uint8 *)inbuf2,0, req->encrypted);
 
        /* process the request */
        switch_message(smb_com2, req2, new_size);
@@ -1732,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
@@ -1753,6 +1833,12 @@ void chain_reply(struct smb_request *req)
        SAFE_FREE(caller_output);
        TALLOC_FREE(req2);
 
+       /*
+        * Reset the chain_size for our caller's offset calculations
+        */
+
+       chain_size -= (outsize_padded - smb_wct);
+
        return;
 }
 
@@ -1830,27 +1916,11 @@ void check_reload(time_t t)
  Process any timeout housekeeping. Return False if the caller should exit.
 ****************************************************************************/
 
-static bool timeout_processing(int *select_timeout,
+static void timeout_processing(int *select_timeout,
                               time_t *last_timeout_processing_time)
 {
        time_t t;
 
-       if (smb_read_error == READ_EOF) {
-               DEBUG(3,("timeout_processing: End of file from client (client has disconnected).\n"));
-               return False;
-       }
-
-       if (smb_read_error == READ_ERROR) {
-               DEBUG(3,("timeout_processing: receive_smb error (%s) Exiting\n",
-                       strerror(errno)));
-               return False;
-       }
-
-       if (smb_read_error == READ_BAD_SIG) {
-               DEBUG(3,("timeout_processing: receive_smb error bad smb signature. Exiting\n"));
-               return False;
-       }
-
        *last_timeout_processing_time = t = time(NULL);
 
        /* become root again if waiting */
@@ -1866,6 +1936,7 @@ static bool timeout_processing(int *select_timeout,
 
                unsigned char trust_passwd_hash[16];
                time_t lct;
+               void *lock;
 
                /*
                 * We're in domain level security, and the code that
@@ -1877,17 +1948,19 @@ static bool timeout_processing(int *select_timeout,
                 * First, open the machine password file with an exclusive lock.
                 */
 
-               if (secrets_lock_trust_account_password(lp_workgroup(), True) == False) {
+               lock = secrets_get_trust_account_lock(NULL, lp_workgroup());
+
+               if (lock == NULL) {
                        DEBUG(0,("process: unable to lock the machine account password for \
 machine %s in domain %s.\n", global_myname(), lp_workgroup() ));
-                       return True;
+                       return;
                }
 
                if(!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd_hash, &lct, NULL)) {
                        DEBUG(0,("process: unable to read the machine account password for \
 machine %s in domain %s.\n", global_myname(), lp_workgroup()));
-                       secrets_lock_trust_account_password(lp_workgroup(), False);
-                       return True;
+                       TALLOC_FREE(lock);
+                       return;
                }
 
                /*
@@ -1896,15 +1969,15 @@ machine %s in domain %s.\n", global_myname(), lp_workgroup()));
 
                if(t < lct + lp_machine_password_timeout()) {
                        global_machine_password_needs_changing = False;
-                       secrets_lock_trust_account_password(lp_workgroup(), False);
-                       return True;
+                       TALLOC_FREE(lock);
+                       return;
                }
 
                /* always just contact the PDC here */
     
                change_trust_account_password( lp_workgroup(), NULL);
                global_machine_password_needs_changing = False;
-               secrets_lock_trust_account_password(lp_workgroup(), False);
+               TALLOC_FREE(lock);
        }
 
        /* update printer queue caches if necessary */
@@ -1929,7 +2002,7 @@ machine %s in domain %s.\n", global_myname(), lp_workgroup()));
 
        *select_timeout = setup_select_timeout();
 
-       return True;
+       return;
 }
 
 /****************************************************************************
@@ -1947,27 +2020,44 @@ void smbd_process(void)
        while (True) {
                int select_timeout = setup_select_timeout();
                int num_echos;
-               char *inbuf;
-               size_t inbuf_len;
-               TALLOC_CTX *frame = talloc_stackframe();
+               char *inbuf = NULL;
+               size_t inbuf_len = 0;
+               bool encrypted = false;
+               TALLOC_CTX *frame = talloc_stackframe_pool(8192);
 
                errno = 0;
 
                /* Did someone ask for immediate checks on things like blocking locks ? */
                if (select_timeout == 0) {
-                       if(!timeout_processing(&select_timeout,
-                                              &last_timeout_processing_time))
-                               return;
+                       timeout_processing(&select_timeout,
+                                          &last_timeout_processing_time);
                        num_smbs = 0; /* Reset smb counter. */
                }
 
                run_events(smbd_event_context(), 0, NULL, NULL);
 
-               while (!receive_message_or_smb(NULL, &inbuf, &inbuf_len,
-                                              select_timeout, &unread_bytes)) {
-                       if(!timeout_processing(&select_timeout,
-                                              &last_timeout_processing_time))
-                               return;
+               while (True) {
+                       NTSTATUS status;
+
+                       status = receive_message_or_smb(
+                               talloc_tos(), &inbuf, &inbuf_len,
+                               select_timeout, &unread_bytes, &encrypted);
+
+                       if (NT_STATUS_IS_OK(status)) {
+                               break;
+                       }
+
+                       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+                               timeout_processing(
+                                       &select_timeout,
+                                       &last_timeout_processing_time);
+                               continue;
+                       }
+
+                       DEBUG(3, ("receive_message_or_smb failed: %s, "
+                                 "exiting\n", nt_errstr(status)));
+                       return;
+
                        num_smbs = 0; /* Reset smb counter. */
                }
 
@@ -1983,13 +2073,13 @@ void smbd_process(void)
                 */
                num_echos = smb_echo_count;
 
-               process_smb(inbuf, inbuf_len, unread_bytes);
+               process_smb(inbuf, inbuf_len, unread_bytes, encrypted);
 
                TALLOC_FREE(inbuf);
 
                if (smb_echo_count != num_echos) {
-                       if(!timeout_processing( &select_timeout, &last_timeout_processing_time))
-                               return;
+                       timeout_processing(&select_timeout,
+                                          &last_timeout_processing_time);
                        num_smbs = 0; /* Reset smb counter. */
                }
 
@@ -2005,10 +2095,9 @@ void smbd_process(void)
                if ((num_smbs % 200) == 0) {
                        time_t new_check_time = time(NULL);
                        if(new_check_time - last_timeout_processing_time >= (select_timeout/1000)) {
-                               if(!timeout_processing(
-                                          &select_timeout,
-                                          &last_timeout_processing_time))
-                                       return;
+                               timeout_processing(
+                                       &select_timeout,
+                                       &last_timeout_processing_time);
                                num_smbs = 0; /* Reset smb counter. */
                                last_timeout_processing_time = new_check_time; /* Reset time. */
                        }