Fix for CVE-2009-2906.
[samba.git] / source / smbd / process.c
index ed1bf762e99c96b1851f4754edd3522d99223a99..c53bfda2219a6be756479b7afb09e1adff590c02 100644 (file)
 
 #include "includes.h"
 
-extern struct auth_context *negprot_global_auth_context;
 extern int smb_echo_count;
 
-const int total_buffer_size = (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN);
-
-/* 
+/*
  * Size of data we can send to client. Set
  *  by the client for all protocols above CORE.
  *  Set by us for CORE protocol.
@@ -37,18 +34,331 @@ 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;
 
+/* Accessor function for smb_read_error for smbd functions. */
+
+/****************************************************************************
+ Send an smb to a fd.
+****************************************************************************/
+
+bool srv_send_smb(int fd, char *buffer, bool do_encrypt)
+{
+       size_t len;
+       size_t nwritten=0;
+       ssize_t ret;
+       char *buf_out = buffer;
+
+       /* Sign the outgoing packet if required. */
+       srv_calculate_sign_mac(buf_out);
+
+       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;
+
+       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;
+}
+
+/*******************************************************************
+ 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)
+{
+       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);
+}
+
+static bool valid_smb_header(const uint8_t *inbuf)
+{
+       if (is_encrypted_packet(inbuf)) {
+               return true;
+       }
+       return (strncmp(smb_base(inbuf),"\377SMB",4) == 0);
+}
+
+/* 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;
+}
+
+static NTSTATUS read_packet_remainder(int fd, char *buffer,
+                                     unsigned int timeout, ssize_t len)
+{
+       if (len <= 0) {
+               return NT_STATUS_OK;
+       }
+
+       return read_socket_with_timeout(fd, buffer, len, len, timeout, NULL);
+}
+
+/****************************************************************************
+ Attempt a zerocopy writeX read. We know here that len > smb_size-4
+****************************************************************************/
+
+/*
+ * Unfortunately, earlier versions of smbclient/libsmbclient
+ * don't send this "standard" writeX header. I've fixed this
+ * for 3.2 but we'll use the old method with earlier versions.
+ * Windows and CIFSFS at least use this standard size. Not
+ * sure about MacOSX.
+ */
+
+#define STANDARD_WRITE_AND_X_HEADER_SIZE (smb_size - 4 + /* basic header */ \
+                               (2*14) + /* word count (including bcc) */ \
+                               1 /* pad byte */)
+
+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;
+       NTSTATUS status;
+
+       memcpy(writeX_header, lenbuf, 4);
+
+       status = read_socket_with_timeout(
+               fd, writeX_header + 4,
+               STANDARD_WRITE_AND_X_HEADER_SIZE,
+               STANDARD_WRITE_AND_X_HEADER_SIZE,
+               timeout, NULL);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /*
+        * Ok - now try and see if this is a possible
+        * valid writeX call.
+        */
+
+       if (is_valid_writeX_buffer((uint8_t *)writeX_header)) {
+               /*
+                * If the data offset is beyond what
+                * we've read, drain the extra bytes.
+                */
+               uint16_t doff = SVAL(writeX_header,smb_vwv11);
+               ssize_t newlen;
+
+               if (doff > STANDARD_WRITE_AND_X_HEADER_SIZE) {
+                       size_t drain = doff - STANDARD_WRITE_AND_X_HEADER_SIZE;
+                       if (drain_socket(smbd_server_fd(), drain) != drain) {
+                               smb_panic("receive_smb_raw_talloc_partial_read:"
+                                       " failed to drain pending bytes");
+                       }
+               } else {
+                       doff = STANDARD_WRITE_AND_X_HEADER_SIZE;
+               }
+
+               /* Spoof down the length and null out the bcc. */
+               set_message_bcc(writeX_header, 0);
+               newlen = smb_len(writeX_header);
+
+               /* Copy the header we've written. */
+
+               *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)));
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               /* Work out the remaining bytes. */
+               *p_unread = len - STANDARD_WRITE_AND_X_HEADER_SIZE;
+               *len_ret = newlen + 4;
+               return NT_STATUS_OK;
+       }
+
+       if (!valid_packet_size(len)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /*
+        * Not a valid writeX call. Just do the standard
+        * talloc and return.
+        */
+
+       *buffer = TALLOC_ARRAY(mem_ctx, char, len+4);
+
+       if (*buffer == NULL) {
+               DEBUG(0, ("Could not allocate inbuf of length %d\n",
+                         (int)len+4));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* Copy in what we already read. */
+       memcpy(*buffer,
+               writeX_header,
+               4 + STANDARD_WRITE_AND_X_HEADER_SIZE);
+       toread = len - STANDARD_WRITE_AND_X_HEADER_SIZE;
+
+       if(toread > 0) {
+               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;
+               }
+       }
+
+       *len_ret = len + 4;
+       return NT_STATUS_OK;
+}
+
+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];
+       size_t len;
+       int min_recv_size = lp_min_receive_file_size();
+       NTSTATUS status;
+
+       *p_unread = 0;
+
+       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) == 0 &&
+                       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, plen);
+       }
+
+       if (!valid_packet_size(len)) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /*
+        * The +4 here can't wrap, we've checked the length above already.
+        */
+
+       *buffer = TALLOC_ARRAY(mem_ctx, char, len+4);
+
+       if (*buffer == NULL) {
+               DEBUG(0, ("Could not allocate inbuf of length %d\n",
+                         (int)len+4));
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       memcpy(*buffer, lenbuf, sizeof(lenbuf));
+
+       status = read_packet_remainder(fd, (*buffer)+4, timeout, len);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       *plen = len + 4;
+       return NT_STATUS_OK;
+}
+
+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)
+{
+       size_t len = 0;
+       NTSTATUS status;
+
+       *p_encrypted = false;
+
+       status = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout,
+                                       p_unread, &len);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       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"));
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       *p_len = len;
+       return NT_STATUS_OK;
+}
+
 /*
  * Initialize a struct smb_request from an inbuf
  */
 
-void init_smb_request(struct smb_request *req, const uint8 *inbuf)
+void init_smb_request(struct smb_request *req,
+                       const uint8 *inbuf,
+                       size_t unread_bytes,
+                       bool encrypted)
 {
        size_t req_size = smb_len(inbuf) + 4;
        /* Ensure we have at least smb_size bytes. */
@@ -63,6 +373,10 @@ void init_smb_request(struct smb_request *req, const uint8 *inbuf)
        req->vuid   = SVAL(inbuf, smb_uid);
        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) {
                DEBUG(0,("init_smb_request: invalid wct number %u (size %u)\n",
@@ -119,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,
@@ -174,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;
@@ -188,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)
@@ -196,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;
                }
        }
@@ -231,6 +557,14 @@ bool push_deferred_smb_message(struct smb_request *req,
 {
        struct timeval end_time;
 
+       if (req->unread_bytes) {
+               DEBUG(0,("push_deferred_smb_message: logic error ! "
+                       "unread_bytes = %u\n",
+                       (unsigned int)req->unread_bytes ));
+               smb_panic("push_deferred_smb_message: "
+                       "logic error unread_bytes != 0" );
+       }
+
        end_time = timeval_sum(&request_time, &timeout);
 
        DEBUG(10,("push_deferred_open_smb_message: pushing message len %u mid %u "
@@ -382,16 +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)
+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;
 
-       smb_read_error = 0;
+       *p_unread = 0;
 
  again:
 
@@ -445,15 +781,19 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
                                                        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;
                }
        }
 
@@ -539,14 +879,12 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
        /* 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;
        }
 
        /*
@@ -565,15 +903,25 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
                goto again;
        }
 
-       len = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0);
+       /*
+        * 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;
 }
 
 /*
@@ -645,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] = {
 
@@ -700,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 },
@@ -920,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);
        }
 
@@ -932,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
@@ -952,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;
        }
@@ -969,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);
 }
 
 /****************************************************************************
@@ -984,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)));
@@ -1009,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;
@@ -1017,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));
@@ -1062,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. */
@@ -1075,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 */
@@ -1090,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++;
        }
@@ -1105,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)
+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;
@@ -1127,9 +1496,25 @@ static void construct_reply(char *inbuf, int size)
        if (!(req = talloc(talloc_tos(), struct smb_request))) {
                smb_panic("could not allocate smb_request");
        }
-       init_smb_request(req, (uint8 *)inbuf);
+       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. */
+               if (drain_socket(smbd_server_fd(), req->unread_bytes) !=
+                               req->unread_bytes) {
+                       smb_panic("failed to drain pending bytes");
+               }
+               req->unread_bytes = 0;
+       }
 
        if (req->outbuf == NULL) {
                return;
@@ -1139,8 +1524,10 @@ static void construct_reply(char *inbuf, int size)
                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);
@@ -1152,7 +1539,7 @@ static void construct_reply(char *inbuf, int size)
  Process an smb from the client
 ****************************************************************************/
 
-static void process_smb(char *inbuf, size_t nread)
+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);
@@ -1160,23 +1547,29 @@ static void process_smb(char *inbuf, size_t nread)
        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");
                }
        }
 
        DEBUG( 6, ( "got message type 0x%x of len 0x%x\n", msg_type,
                    smb_len(inbuf) ) );
-       DEBUG( 3, ( "Transaction %d of length %d\n", trans_num, (int)nread ) );
+       DEBUG( 3, ( "Transaction %d of length %d (%u toread)\n", trans_num,
+                               (int)nread,
+                               (unsigned int)unread_bytes ));
 
        if (msg_type != 0) {
                /*
@@ -1188,8 +1581,8 @@ static void process_smb(char *inbuf, size_t nread)
 
        show_msg(inbuf);
 
-       construct_reply(inbuf,nread);
-      
+       construct_reply(inbuf,nread,unread_bytes,encrypted);
+
        trans_num++;
 }
 
@@ -1225,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);
@@ -1266,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;
@@ -1304,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
@@ -1348,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);
+       init_smb_request(req2, (uint8 *)inbuf2,0, req->encrypted);
 
        /* process the request */
        switch_message(smb_com2, req2, new_size);
@@ -1417,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
@@ -1438,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;
 }
 
@@ -1515,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 */
@@ -1551,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
@@ -1562,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;
                }
 
                /*
@@ -1581,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 */
@@ -1614,7 +2002,7 @@ machine %s in domain %s.\n", global_myname(), lp_workgroup()));
 
        *select_timeout = setup_select_timeout();
 
-       return True;
+       return;
 }
 
 /****************************************************************************
@@ -1625,33 +2013,51 @@ void smbd_process(void)
 {
        time_t last_timeout_processing_time = time(NULL);
        unsigned int num_smbs = 0;
+       size_t unread_bytes = 0;
 
        max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
 
        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;
 
-               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)) {
-                       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. */
                }
 
@@ -1664,16 +2070,16 @@ void smbd_process(void)
                 * faster than the select timeout, thus starving out the
                 * essential processing (change notify, blocking locks) that
                 * the timeout code does. JRA.
-                */ 
+                */
                num_echos = smb_echo_count;
 
-               process_smb(inbuf, inbuf_len);
+               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. */
                }
 
@@ -1689,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. */
                        }