[GLUE] Rsync SAMBA_3_2_0 SVN r25598 in order to create the v3-2-test branch.
[samba.git] / source / smbd / process.c
index 595c524137034fe9ca21fdaa968acc89211b5f05..7faf26af25e1bce35962f7e31c21bd5e29a5099d 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/CIFS implementation.
    process incoming packets - main loop
    Copyright (C) Andrew Tridgell 1992-1998
-   Copyright (C) Volker Lendecke 2005
+   Copyright (C) Volker Lendecke 2005-2007
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 
-uint16 global_smbpid;
 extern struct auth_context *negprot_global_auth_context;
 extern int smb_echo_count;
 
-static char *InBuffer = NULL;
-static char *OutBuffer = NULL;
-static char *current_inbuf = NULL;
+const int total_buffer_size = (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN);
 
 /* 
  * Size of data we can send to client. Set
@@ -54,10 +50,37 @@ extern int max_send;
 
 void init_smb_request(struct smb_request *req, const uint8 *inbuf)
 {
+       size_t req_size = smb_len(inbuf) + 4;
+       /* Ensure we have at least smb_size bytes. */
+       if (req_size < smb_size) {
+               DEBUG(0,("init_smb_request: invalid request size %u\n",
+                       (unsigned int)req_size ));
+               exit_server_cleanly("Invalid SMB request");
+       }
        req->flags2 = SVAL(inbuf, smb_flg2);
        req->smbpid = SVAL(inbuf, smb_pid);
        req->mid    = SVAL(inbuf, smb_mid);
        req->vuid   = SVAL(inbuf, smb_uid);
+       req->tid    = SVAL(inbuf, smb_tid);
+       req->wct    = CVAL(inbuf, smb_wct);
+       /* 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",
+                       (unsigned int)req->wct,
+                       (unsigned int)req_size));
+               exit_server_cleanly("Invalid SMB request");
+       }
+       /* Ensure bcc is correct. */
+       if (((uint8 *)smb_buf(inbuf)) + smb_buflen(inbuf) > inbuf + req_size) {
+               DEBUG(0,("init_smb_request: invalid bcc number %u "
+                       "(wct = %u, size %u)\n",
+                       (unsigned int)smb_buflen(inbuf),
+                       (unsigned int)req->wct,
+                       (unsigned int)req_size));
+               exit_server_cleanly("Invalid SMB request");
+       }
+       req->inbuf  = inbuf;
+       req->outbuf = NULL;
 }
 
 /****************************************************************************
@@ -68,15 +91,16 @@ void init_smb_request(struct smb_request *req, const uint8 *inbuf)
 static struct pending_message_list *deferred_open_queue;
 
 /****************************************************************************
- Function to push a message onto the tail of a linked list of smb messages
ready for processing.
+ Function to push a message onto the tail of a linked list of smb messages ready
+ for processing.
 ****************************************************************************/
 
-static BOOL push_queued_message(char *buf, int msg_len,
+static BOOL push_queued_message(struct smb_request *req,
                                struct timeval request_time,
                                struct timeval end_time,
                                char *private_data, size_t private_len)
 {
+       int msg_len = smb_len(req->inbuf) + 4;
        struct pending_message_list *msg;
 
        msg = TALLOC_ZERO_P(NULL, struct pending_message_list);
@@ -86,7 +110,7 @@ static BOOL push_queued_message(char *buf, int msg_len,
                return False;
        }
 
-       msg->buf = data_blob_talloc(msg, buf, msg_len);
+       msg->buf = data_blob_talloc(msg, req->inbuf, msg_len);
        if(msg->buf.data == NULL) {
                DEBUG(0,("push_message: malloc fail (2)\n"));
                TALLOC_FREE(msg);
@@ -124,7 +148,7 @@ void remove_deferred_open_smb_message(uint16 mid)
 
        for (pml = deferred_open_queue; pml; pml = pml->next) {
                if (mid == SVAL(pml->buf.data,smb_mid)) {
-                       DEBUG(10,("remove_deferred_open_smb_message: "
+                       DEBUG(10,("remove_sharing_violation_open_smb_message: "
                                  "deleting mid %u len %u\n",
                                  (unsigned int)mid,
                                  (unsigned int)pml->buf.length ));
@@ -147,11 +171,11 @@ void schedule_deferred_open_smb_message(uint16 mid)
 
        for (pml = deferred_open_queue; pml; pml = pml->next) {
                uint16 msg_mid = SVAL(pml->buf.data,smb_mid);
-               DEBUG(10, ("schedule_deferred_open_smb_message: [%d] "
-                          "msg_mid = %u\n", i++, (unsigned int)msg_mid ));
+               DEBUG(10,("schedule_deferred_open_smb_message: [%d] msg_mid = %u\n", i++,
+                       (unsigned int)msg_mid ));
                if (mid == msg_mid) {
-                       DEBUG(10, ("schedule_deferred_open_smb_message: "
-                                  "scheduling mid %u\n", mid));
+                       DEBUG(10,("schedule_deferred_open_smb_message: scheduling mid %u\n",
+                               mid ));
                        pml->end_time.tv_sec = 0;
                        pml->end_time.tv_usec = 0;
                        DLIST_PROMOTE(deferred_open_queue, pml);
@@ -159,8 +183,8 @@ void schedule_deferred_open_smb_message(uint16 mid)
                }
        }
 
-       DEBUG(10, ("schedule_deferred_open_smb_message: failed to find "
-                  "message mid %u\n", mid ));
+       DEBUG(10,("schedule_deferred_open_smb_message: failed to find message mid %u\n",
+               mid ));
 }
 
 /****************************************************************************
@@ -200,7 +224,7 @@ struct pending_message_list *get_open_deferred_message(uint16 mid)
  messages ready for processing.
 ****************************************************************************/
 
-BOOL push_deferred_smb_message(uint16 mid,
+BOOL push_deferred_smb_message(struct smb_request *req,
                               struct timeval request_time,
                               struct timeval timeout,
                               char *private_data, size_t priv_len)
@@ -211,12 +235,11 @@ BOOL push_deferred_smb_message(uint16 mid,
 
        DEBUG(10,("push_deferred_open_smb_message: pushing message len %u mid %u "
                  "timeout time [%u.%06u]\n",
-                 (unsigned int) smb_len(current_inbuf)+4, (unsigned int)mid,
+                 (unsigned int) smb_len(req->inbuf)+4, (unsigned int)req->mid,
                  (unsigned int)end_time.tv_sec,
                  (unsigned int)end_time.tv_usec));
 
-       return push_queued_message(current_inbuf, smb_len(current_inbuf)+4,
-                                  request_time, end_time,
+       return push_queued_message(req, request_time, end_time,
                                   private_data, priv_len);
 }
 
@@ -359,12 +382,14 @@ static int select_on_fd(int fd, int maxfd, fd_set *fds)
 The timeout is in milliseconds
 ****************************************************************************/
 
-static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
+static BOOL receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
+                                  size_t *buffer_len, int timeout)
 {
        fd_set r_fds, w_fds;
        int selrtn;
        struct timeval to;
        int maxfd = 0;
+       ssize_t len;
 
        smb_read_error = 0;
 
@@ -415,8 +440,16 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
                }
 
                if (pop_message) {
-                       memcpy(buffer, msg->buf.data, MIN(buffer_len, msg->buf.length));
-  
+
+                       *buffer = (char *)talloc_memdup(mem_ctx, msg->buf.data,
+                                                       msg->buf.length);
+                       if (*buffer == NULL) {
+                               DEBUG(0, ("talloc failed\n"));
+                               smb_read_error = READ_ERROR;
+                               return False;
+                       }
+                       *buffer_len = msg->buf.length;
+
                        /* 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"));
@@ -532,7 +565,15 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
                goto again;
        }
 
-       return receive_smb(smbd_server_fd(), buffer, 0);
+       len = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0);
+
+       if (len == -1) {
+               return False;
+       }
+
+       *buffer_len = (size_t)len;
+
+       return True;
 }
 
 /*
@@ -604,7 +645,7 @@ force write permissions on print services.
 */
 static const struct smb_message_struct {
        const char *name;
-       int (*fn)(connection_struct *conn, char *, char *, int, int);
+       void (*fn_new)(connection_struct *conn, struct smb_request *req);
        int flags;
 } smb_messages[256] = {
 
@@ -614,7 +655,7 @@ static const struct smb_message_struct {
 /* 0x03 */ { "SMBcreate",reply_mknew,AS_USER},
 /* 0x04 */ { "SMBclose",reply_close,AS_USER | CAN_IPC },
 /* 0x05 */ { "SMBflush",reply_flush,AS_USER},
-/* 0x06 */ { "SMBunlink",reply_unlink,AS_USER | NEED_WRITE }, 
+/* 0x06 */ { "SMBunlink",reply_unlink,AS_USER | NEED_WRITE },
 /* 0x07 */ { "SMBmv",reply_mv,AS_USER | NEED_WRITE },
 /* 0x08 */ { "SMBgetatr",reply_getatr,AS_USER},
 /* 0x09 */ { "SMBsetatr",reply_setatr,AS_USER | NEED_WRITE},
@@ -623,7 +664,7 @@ static const struct smb_message_struct {
 /* 0x0c */ { "SMBlock",reply_lock,AS_USER},
 /* 0x0d */ { "SMBunlock",reply_unlock,AS_USER},
 /* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER },
-/* 0x0f */ { "SMBmknew",reply_mknew,AS_USER}, 
+/* 0x0f */ { "SMBmknew",reply_mknew,AS_USER},
 /* 0x10 */ { "SMBcheckpath",reply_checkpath,AS_USER},
 /* 0x11 */ { "SMBexit",reply_exit,DO_CHDIR},
 /* 0x12 */ { "SMBlseek",reply_lseek,AS_USER},
@@ -636,11 +677,11 @@ static const struct smb_message_struct {
 /* 0x19 */ { NULL, NULL, 0 },
 /* 0x1a */ { "SMBreadbraw",reply_readbraw,AS_USER},
 /* 0x1b */ { "SMBreadBmpx",reply_readbmpx,AS_USER},
-/* 0x1c */ { "SMBreadBs",NULL,0 },
+/* 0x1c */ { "SMBreadBs",reply_readbs,AS_USER },
 /* 0x1d */ { "SMBwritebraw",reply_writebraw,AS_USER},
 /* 0x1e */ { "SMBwriteBmpx",reply_writebmpx,AS_USER},
 /* 0x1f */ { "SMBwriteBs",reply_writebs,AS_USER},
-/* 0x20 */ { "SMBwritec",NULL,0},
+/* 0x20 */ { "SMBwritec", NULL,0},
 /* 0x21 */ { NULL, NULL, 0 },
 /* 0x22 */ { "SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE },
 /* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER },
@@ -648,9 +689,9 @@ static const struct smb_message_struct {
 /* 0x25 */ { "SMBtrans",reply_trans,AS_USER | CAN_IPC },
 /* 0x26 */ { "SMBtranss",reply_transs,AS_USER | CAN_IPC},
 /* 0x27 */ { "SMBioctl",reply_ioctl,0},
-/* 0x28 */ { "SMBioctls",NULL,AS_USER},
+/* 0x28 */ { "SMBioctls", NULL,AS_USER},
 /* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE },
-/* 0x2a */ { "SMBmove",NULL,AS_USER | NEED_WRITE },
+/* 0x2a */ { "SMBmove", NULL,AS_USER | NEED_WRITE },
 /* 0x2b */ { "SMBecho",reply_echo,0},
 /* 0x2c */ { "SMBwriteclose",reply_writeclose,AS_USER},
 /* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER | CAN_IPC },
@@ -658,10 +699,10 @@ static const struct smb_message_struct {
 /* 0x2f */ { "SMBwriteX",reply_write_and_X,AS_USER | CAN_IPC },
 /* 0x30 */ { NULL, NULL, 0 },
 /* 0x31 */ { NULL, NULL, 0 },
-/* 0x32 */ { "SMBtrans2", reply_trans2, AS_USER | CAN_IPC },
-/* 0x33 */ { "SMBtranss2", reply_transs2, AS_USER},
-/* 0x34 */ { "SMBfindclose", reply_findclose,AS_USER},
-/* 0x35 */ { "SMBfindnclose", reply_findnclose, AS_USER},
+/* 0x32 */ { "SMBtrans2",reply_trans2, AS_USER | CAN_IPC },
+/* 0x33 */ { "SMBtranss2",reply_transs2, AS_USER},
+/* 0x34 */ { "SMBfindclose",reply_findclose,AS_USER},
+/* 0x35 */ { "SMBfindnclose",reply_findnclose,AS_USER},
 /* 0x36 */ { NULL, NULL, 0 },
 /* 0x37 */ { NULL, NULL, 0 },
 /* 0x38 */ { NULL, NULL, 0 },
@@ -724,7 +765,7 @@ static const struct smb_message_struct {
 /* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR},
 /* 0x72 */ { "SMBnegprot",reply_negprot,0},
 /* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
-/* 0x74 */ { "SMBulogoffX", reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
+/* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
 /* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
 /* 0x76 */ { NULL, NULL, 0 },
 /* 0x77 */ { NULL, NULL, 0 },
@@ -768,12 +809,12 @@ static const struct smb_message_struct {
 /* 0x9d */ { NULL, NULL, 0 },
 /* 0x9e */ { NULL, NULL, 0 },
 /* 0x9f */ { NULL, NULL, 0 },
-/* 0xa0 */ { "SMBnttrans", reply_nttrans, AS_USER | CAN_IPC },
-/* 0xa1 */ { "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC },
-/* 0xa2 */ { "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC },
+/* 0xa0 */ { "SMBnttrans",reply_nttrans, AS_USER | CAN_IPC },
+/* 0xa1 */ { "SMBnttranss",reply_nttranss, AS_USER | CAN_IPC },
+/* 0xa2 */ { "SMBntcreateX",reply_ntcreate_and_X, AS_USER | CAN_IPC },
 /* 0xa3 */ { NULL, NULL, 0 },
-/* 0xa4 */ { "SMBntcancel", reply_ntcancel, 0 },
-/* 0xa5 */ { "SMBntrename", reply_ntrename, AS_USER | NEED_WRITE },
+/* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 },
+/* 0xa5 */ { "SMBntrename",reply_ntrename, AS_USER | NEED_WRITE },
 /* 0xa6 */ { NULL, NULL, 0 },
 /* 0xa7 */ { NULL, NULL, 0 },
 /* 0xa8 */ { NULL, NULL, 0 },
@@ -817,10 +858,10 @@ static const struct smb_message_struct {
 /* 0xce */ { NULL, NULL, 0 },
 /* 0xcf */ { NULL, NULL, 0 },
 /* 0xd0 */ { "SMBsends",reply_sends,AS_GUEST},
-/* 0xd1 */ { "SMBsendb",NULL,AS_GUEST},
-/* 0xd2 */ { "SMBfwdname",NULL,AS_GUEST},
-/* 0xd3 */ { "SMBcancelf",NULL,AS_GUEST},
-/* 0xd4 */ { "SMBgetmac",NULL,AS_GUEST},
+/* 0xd1 */ { "SMBsendb", NULL,AS_GUEST},
+/* 0xd2 */ { "SMBfwdname", NULL,AS_GUEST},
+/* 0xd3 */ { "SMBcancelf", NULL,AS_GUEST},
+/* 0xd4 */ { "SMBgetmac", NULL,AS_GUEST},
 /* 0xd5 */ { "SMBsendstrt",reply_sendstrt,AS_GUEST},
 /* 0xd6 */ { "SMBsendend",reply_sendend,AS_GUEST},
 /* 0xd7 */ { "SMBsendtxt",reply_sendtxt,AS_GUEST},
@@ -867,11 +908,48 @@ static const struct smb_message_struct {
 
 };
 
+/*******************************************************************
+ allocate and initialize a reply packet
+********************************************************************/
+
+void reply_outbuf(struct smb_request *req, uint8 num_words, uint32 num_bytes)
+{
+       /*
+         * Protect against integer wrap
+         */
+       if ((num_bytes > 0xffffff)
+           || ((num_bytes + smb_size + num_words*2) > 0xffffff)) {
+               char *msg;
+               asprintf(&msg, "num_bytes too large: %u",
+                        (unsigned)num_bytes);
+               smb_panic(msg);
+       }
+
+       if (!(req->outbuf = TALLOC_ARRAY(
+                     req, uint8,
+                     smb_size + num_words*2 + num_bytes))) {
+               smb_panic("could not allocate output buffer\n");
+       }
+
+       construct_reply_common((char *)req->inbuf, (char *)req->outbuf);
+       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
+        */
+       if (num_words != 0) {
+               memset(req->outbuf + smb_vwv0, 0, num_words*2);
+       }
+
+       return;
+}
+
+
 /*******************************************************************
  Dump a packet to a file.
 ********************************************************************/
 
-static void smb_dump(const char *name, int type, char *data, ssize_t len)
+static void smb_dump(const char *name, int type, const char *data, ssize_t len)
 {
        int fd, i;
        pstring fname;
@@ -893,171 +971,191 @@ static void smb_dump(const char *name, int type, char *data, ssize_t len)
        }
 }
 
-
 /****************************************************************************
- Do a switch on the message type, and return the response size
+ Prepare everything for calling the actual request function, and potentially
+ call the request function via the "new" interface.
+
+ Return False if the "legacy" function needs to be called, everything is
+ prepared.
+
+ Return True if we're done.
+
+ I know this API sucks, but it is the one with the least code change I could
+ find.
 ****************************************************************************/
 
-static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize)
+static void switch_message(uint8 type, struct smb_request *req, int size)
 {
-       static pid_t pid= (pid_t)-1;
-       int outsize = 0;
-
-       type &= 0xff;
+       int flags;
+       uint16 session_tag;
+       connection_struct *conn;
 
-       if (pid == (pid_t)-1)
-               pid = sys_getpid();
+       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(inbuf),"\377SMB",4) != 0) || (size < (smb_size - 4))) {
-               DEBUG(2,("Non-SMB packet of length %d. Terminating server\n",smb_len(inbuf)));
+       /* 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)
+           || (size < (smb_size - 4))) {
+               DEBUG(2,("Non-SMB packet of length %d. Terminating server\n",
+                        smb_len(req->inbuf)));
                exit_server_cleanly("Non-SMB packet");
-               return(-1);
        }
 
-       /* yuck! this is an interim measure before we get rid of our
-               current inbuf/outbuf system */
-       global_smbpid = SVAL(inbuf,smb_pid);
-
-       if (smb_messages[type].fn == NULL) {
+       if (smb_messages[type].fn_new == NULL) {
                DEBUG(0,("Unknown message type %d!\n",type));
-               smb_dump("Unknown", 1, inbuf, size);
-               outsize = reply_unknown(inbuf,outbuf);
-       } else {
-               int flags = smb_messages[type].flags;
-               static uint16 last_session_tag = UID_FIELD_INVALID;
-               /* In share mode security we must ignore the vuid. */
-               uint16 session_tag = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(inbuf,smb_uid);
-               connection_struct *conn = conn_find(SVAL(inbuf,smb_tid));
+               smb_dump("Unknown", 1, (char *)req->inbuf, size);
+               reply_unknown_new(req, type);
+               return;
+       }
 
-               DEBUG(3,("switch message %s (pid %d) conn 0x%lx\n",smb_fn_name(type),(int)pid,(unsigned long)conn));
+       flags = smb_messages[type].flags;
 
-               smb_dump(smb_fn_name(type), 1, inbuf, 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);
 
-               /* Ensure this value is replaced in the incoming packet. */
-               SSVAL(inbuf,smb_uid,session_tag);
+       DEBUG(3,("switch message %s (pid %d) conn 0x%lx\n", smb_fn_name(type),
+                (int)sys_getpid(), (unsigned long)conn));
 
-               /*
-                * Ensure the correct username is in current_user_info.
-                * This is a really ugly bugfix for problems with
-                * multiple session_setup_and_X's being done and
-                * allowing %U and %G substitutions to work correctly.
-                * There is a reason this code is done here, don't
-                * move it unless you know what you're doing... :-).
-                * JRA.
-                */
+       smb_dump(smb_fn_name(type), 1, (char *)req->inbuf, size);
 
-               if (session_tag != last_session_tag) {
-                       user_struct *vuser = NULL;
+       /* Ensure this value is replaced in the incoming packet. */
+       SSVAL(req->inbuf,smb_uid,session_tag);
 
-                       last_session_tag = session_tag;
-                       if(session_tag != UID_FIELD_INVALID) {
-                               vuser = get_valid_user_struct(session_tag);           
-                               if (vuser) {
-                                       set_current_user_info(&vuser->user);
-                               }
-                       }
-               }
+       /*
+        * Ensure the correct username is in current_user_info.  This is a
+        * really ugly bugfix for problems with multiple session_setup_and_X's
+        * being done and allowing %U and %G substitutions to work correctly.
+        * There is a reason this code is done here, don't move it unless you
+        * know what you're doing... :-).
+        * JRA.
+        */
 
-               /* Does this call need to be run as the connected user? */
-               if (flags & AS_USER) {
-
-                       /* Does this call need a valid tree connection? */
-                       if (!conn) {
-                               /* Amazingly, the error code depends on the command (from Samba4). */
-                               if (type == SMBntcreateX) {
-                                       return ERROR_NT(NT_STATUS_INVALID_HANDLE);
-                               } else {
-                                       return ERROR_DOS(ERRSRV, ERRinvnid);
-                               }
-                       }
+       if (session_tag != last_session_tag) {
+               user_struct *vuser = NULL;
 
-                       if (!change_to_user(conn,session_tag)) {
-                               return(ERROR_NT(NT_STATUS_DOS(ERRSRV,ERRbaduid)));
+               last_session_tag = session_tag;
+               if(session_tag != UID_FIELD_INVALID) {
+                       vuser = get_valid_user_struct(session_tag);
+                       if (vuser) {
+                               set_current_user_info(&vuser->user);
                        }
+               }
+       }
 
-                       /* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */
-
-                       /* Does it need write permission? */
-                       if ((flags & NEED_WRITE) && !CAN_WRITE(conn)) {
-                               return ERROR_NT(NT_STATUS_MEDIA_WRITE_PROTECTED);
+       /* Does this call need to be run as the connected user? */
+       if (flags & AS_USER) {
+
+               /* Does this call need a valid tree connection? */
+               if (!conn) {
+                       /*
+                        * Amazingly, the error code depends on the command
+                        * (from Samba4).
+                        */
+                       if (type == SMBntcreateX) {
+                               reply_nterror(req, NT_STATUS_INVALID_HANDLE);
+                       } else {
+                               reply_doserror(req, ERRSRV, ERRinvnid);
                        }
+                       return;
+               }
 
-                       /* IPC services are limited */
-                       if (IS_IPC(conn) && !(flags & CAN_IPC)) {
-                               return(ERROR_DOS(ERRSRV,ERRaccess));
-                       }
-               } else {
-                       /* This call needs to be run as root */
-                       change_to_root_user();
+               if (!change_to_user(conn,session_tag)) {
+                       reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRbaduid));
+                       return;
                }
 
-               /* load service specific parameters */
-               if (conn) {
-                       if (!set_current_service(conn,SVAL(inbuf,smb_flg),(flags & (AS_USER|DO_CHDIR)?True:False))) {
-                               return(ERROR_DOS(ERRSRV,ERRaccess));
-                       }
-                       conn->num_smb_operations++;
+               /* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */
+
+               /* Does it need write permission? */
+               if ((flags & NEED_WRITE) && !CAN_WRITE(conn)) {
+                       reply_nterror(req, NT_STATUS_MEDIA_WRITE_PROTECTED);
+                       return;
                }
 
-               /* does this protocol need to be run as guest? */
-               if ((flags & AS_GUEST) && (!change_to_guest() || 
-                               !check_access(smbd_server_fd(), lp_hostsallow(-1), lp_hostsdeny(-1)))) {
-                       return(ERROR_DOS(ERRSRV,ERRaccess));
+               /* IPC services are limited */
+               if (IS_IPC(conn) && !(flags & CAN_IPC)) {
+                       reply_doserror(req, ERRSRV,ERRaccess);
+                       return;
                }
+       } else {
+               /* This call needs to be run as root */
+               change_to_root_user();
+       }
 
-               current_inbuf = inbuf; /* In case we need to defer this message in open... */
-               outsize = smb_messages[type].fn(conn, inbuf,outbuf,size,bufsize);
+       /* load service specific parameters */
+       if (conn) {
+               if (!set_current_service(conn,SVAL(req->inbuf,smb_flg),
+                                        (flags & (AS_USER|DO_CHDIR)
+                                         ?True:False))) {
+                       reply_doserror(req, ERRSRV, ERRaccess);
+                       return;
+               }
+               conn->num_smb_operations++;
        }
 
-       smb_dump(smb_fn_name(type), 0, outbuf, outsize);
+       /* does this protocol need to be run as guest? */
+       if ((flags & AS_GUEST)
+           && (!change_to_guest() ||
+               !check_access(smbd_server_fd(), lp_hostsallow(-1),
+                             lp_hostsdeny(-1)))) {
+               reply_doserror(req, ERRSRV, ERRaccess);
+               return;
+       }
 
-       return(outsize);
+       smb_messages[type].fn_new(conn, req);
 }
 
 /****************************************************************************
  Construct a reply to the incoming packet.
 ****************************************************************************/
 
-static int construct_reply(char *inbuf,char *outbuf,int size,int bufsize)
+static void construct_reply(char *inbuf, int size)
 {
-       int type = CVAL(inbuf,smb_com);
-       int outsize = 0;
-       int msg_type = CVAL(inbuf,0);
+       uint8 type = CVAL(inbuf,smb_com);
+       struct smb_request *req;
 
        chain_size = 0;
        file_chain_reset();
        reset_chain_p();
 
-       if (msg_type != 0)
-               return(reply_special(inbuf,outbuf));  
+       if (!(req = talloc(talloc_tos(), struct smb_request))) {
+               smb_panic("could not allocate smb_request");
+       }
+       init_smb_request(req, (uint8 *)inbuf);
 
-       construct_reply_common(inbuf, outbuf);
+       switch_message(type, req, size);
 
-       outsize = switch_message(type,inbuf,outbuf,size,bufsize);
+       if (req->outbuf == NULL) {
+               return;
+       }
 
-       outsize += chain_size;
+       if (CVAL(req->outbuf,0) == 0) {
+               show_msg((char *)req->outbuf);
+       }
 
-       if(outsize > 4) {
-               smb_setlen(inbuf,outbuf,outsize - 4);
+       if (!send_smb(smbd_server_fd(), (char *)req->outbuf)) {
+               exit_server_cleanly("construct_reply: send_smb failed.");
        }
-       return(outsize);
+
+       TALLOC_FREE(req);
+
+       return;
 }
 
 /****************************************************************************
  Process an smb from the client
 ****************************************************************************/
 
-static void process_smb(char *inbuf, char *outbuf)
+static void process_smb(char *inbuf, size_t nread)
 {
        static int trans_num;
        int msg_type = CVAL(inbuf,0);
-       int32 len = smb_len(inbuf);
-       int nread = len + 4;
 
        DO_PROFILE_INC(smb_count);
 
@@ -1076,27 +1174,22 @@ static void process_smb(char *inbuf, char *outbuf)
                }
        }
 
-       DEBUG( 6, ( "got message type 0x%x of len 0x%x\n", msg_type, len ) );
-       DEBUG( 3, ( "Transaction %d of length %d\n", trans_num, nread ) );
+       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 ) );
 
-       if (msg_type == 0)
-               show_msg(inbuf);
-       else if(msg_type == SMBkeepalive)
-               return; /* Keepalive packet. */
+       if (msg_type != 0) {
+               /*
+                * NetBIOS session request, keepalive, etc.
+                */
+               reply_special(inbuf);
+               return;
+       }
 
-       nread = construct_reply(inbuf,outbuf,nread,max_send);
+       show_msg(inbuf);
+
+       construct_reply(inbuf,nread);
       
-       if(nread > 0) {
-               if (CVAL(outbuf,0) == 0)
-                       show_msg(outbuf);
-       
-               if (nread != smb_len(outbuf) + 4) {
-                       DEBUG(0,("ERROR: Invalid message response size! %d %d\n",
-                               nread, smb_len(outbuf)));
-               } else if (!send_smb(smbd_server_fd(),outbuf)) {
-                       exit_server_cleanly("process_smb: send_smb failed.");
-               }
-       }
        trans_num++;
 }
 
@@ -1132,7 +1225,7 @@ void remove_from_common_flags2(uint32 v)
 
 void construct_reply_common(const char *inbuf, char *outbuf)
 {
-       set_message(inbuf,outbuf,0,0,False);
+       set_message(outbuf,0,0,False);
        
        SCVAL(outbuf,smb_com,CVAL(inbuf,smb_com));
        SIVAL(outbuf,smb_rcls,0);
@@ -1152,29 +1245,56 @@ void construct_reply_common(const char *inbuf, char *outbuf)
  Construct a chained reply and add it to the already made reply
 ****************************************************************************/
 
-int chain_reply(char *inbuf,char *outbuf,int size,int bufsize)
+void chain_reply(struct smb_request *req)
 {
        static char *orig_inbuf;
-       static char *orig_outbuf;
+
+       /*
+        * Dirty little const_discard: We mess with req->inbuf, which is
+        * declared as const. If maybe at some point this routine gets
+        * rewritten, this const_discard could go away.
+        */
+       char *inbuf = CONST_DISCARD(char *, req->inbuf);
+       int size = smb_len(req->inbuf)+4;
+
        int smb_com1, smb_com2 = CVAL(inbuf,smb_vwv0);
        unsigned smb_off2 = SVAL(inbuf,smb_vwv1);
-       char *inbuf2, *outbuf2;
+       char *inbuf2;
        int outsize2;
        int new_size;
        char inbuf_saved[smb_wct];
-       char outbuf_saved[smb_wct];
-       int outsize = smb_len(outbuf) + 4;
+       char *outbuf = (char *)req->outbuf;
+       size_t outsize = smb_len(outbuf) + 4;
+       size_t outsize_padded;
+       size_t ofs, to_move;
+
+       struct smb_request *req2;
+       size_t caller_outputlen;
+       char *caller_output;
 
        /* Maybe its not chained, or it's an error packet. */
        if (smb_com2 == 0xFF || SVAL(outbuf,smb_rcls) != 0) {
                SCVAL(outbuf,smb_vwv0,0xFF);
-               return outsize;
+               return;
        }
 
        if (chain_size == 0) {
                /* this is the first part of the chain */
                orig_inbuf = inbuf;
-               orig_outbuf = outbuf;
+       }
+
+       /*
+        * We need to save the output the caller added to the chain so that we
+        * can splice it into the final output buffer later.
+        */
+
+       caller_outputlen = outsize - smb_wct;
+
+       caller_output = (char *)memdup(outbuf + smb_wct, caller_outputlen);
+
+       if (caller_output == NULL) {
+               /* TODO: NT_STATUS_NO_MEMORY */
+               smb_panic("could not dup outbuf");
        }
 
        /*
@@ -1183,27 +1303,25 @@ int chain_reply(char *inbuf,char *outbuf,int size,int bufsize)
         * 4 byte aligned. JRA.
         */
 
-       outsize = (outsize + 3) & ~3;
+       outsize_padded = (outsize + 3) & ~3;
 
-       /* we need to tell the client where the next part of the reply will be */
-       SSVAL(outbuf,smb_vwv1,smb_offset(outbuf+outsize,outbuf));
-       SCVAL(outbuf,smb_vwv0,smb_com2);
-
-       /* remember how much the caller added to the chain, only counting stuff
-               after the parameter words */
-       chain_size += outsize - smb_wct;
+       /*
+        * remember how much the caller added to the chain, only counting
+        * stuff after the parameter words
+        */
+       chain_size += outsize_padded - smb_wct;
 
-       /* work out pointers into the original packets. The
-               headers on these need to be filled in */
+       /*
+        * work out pointers into the original packets. The
+        * headers on these need to be filled in
+        */
        inbuf2 = orig_inbuf + smb_off2 + 4 - smb_wct;
-       outbuf2 = orig_outbuf + SVAL(outbuf,smb_vwv1) + 4 - smb_wct;
 
        /* remember the original command type */
        smb_com1 = CVAL(orig_inbuf,smb_com);
 
        /* save the data which will be overwritten by the new headers */
        memcpy(inbuf_saved,inbuf2,smb_wct);
-       memcpy(outbuf_saved,outbuf2,smb_wct);
 
        /* give the new packet the same header as the last part of the SMB */
        memmove(inbuf2,inbuf,smb_wct);
@@ -1218,40 +1336,109 @@ int chain_reply(char *inbuf,char *outbuf,int size,int bufsize)
                         "(orig size = %d, offset = %d)\n",
                         size, (int)(inbuf2 - inbuf) ));
                exit_server_cleanly("Bad chained packet");
-               return(-1);
+               return;
        }
 
        /* And set it in the header. */
-       smb_setlen(inbuf, inbuf2, new_size);
-
-       /* create the out buffer */
-       construct_reply_common(inbuf2, outbuf2);
+       smb_setlen(inbuf2, new_size - 4);
 
        DEBUG(3,("Chained message\n"));
        show_msg(inbuf2);
 
+       if (!(req2 = talloc(talloc_tos(), struct smb_request))) {
+               smb_panic("could not allocate smb_request");
+       }
+       init_smb_request(req2, (uint8 *)inbuf2);
+
        /* process the request */
-       outsize2 = switch_message(smb_com2,inbuf2,outbuf2,new_size,
-                               bufsize-chain_size);
+       switch_message(smb_com2, req2, new_size);
 
-       /* copy the new reply and request headers over the old ones, but
-               preserve the smb_com field */
-       memmove(orig_outbuf,outbuf2,smb_wct);
-       SCVAL(orig_outbuf,smb_com,smb_com1);
+       /*
+        * We don't accept deferred operations in chained requests.
+        */
+       SMB_ASSERT(req2->outbuf != NULL);
+       outsize2 = smb_len(req2->outbuf)+4;
 
-       /* restore the saved data, being careful not to overwrite any
-               data from the reply header */
-       memcpy(inbuf2,inbuf_saved,smb_wct);
+       /*
+        * Move away the new command output so that caller_output fits in,
+        * copy in the caller_output saved above.
+        */
 
-       {
-               int ofs = smb_wct - PTR_DIFF(outbuf2,orig_outbuf);
-               if (ofs < 0) {
-                       ofs = 0;
-               }
-               memmove(outbuf2+ofs,outbuf_saved+ofs,smb_wct-ofs);
+       SMB_ASSERT(outsize_padded >= smb_wct);
+
+       /*
+        * "ofs" is the space we need for caller_output. Equal to
+        * caller_outputlen plus the padding.
+        */
+
+       ofs = outsize_padded - smb_wct;
+
+       /*
+        * "to_move" is the amount of bytes the secondary routine gave us
+        */
+
+       to_move = outsize2 - smb_wct;
+
+       if (to_move + ofs + smb_wct + chain_size > max_send) {
+               smb_panic("replies too large -- would have to cut");
+       }
+
+       /*
+        * In the "new" API "outbuf" is allocated via reply_outbuf, just for
+        * the first request in the chain. So we have to re-allocate it. In
+        * the "old" API the only outbuf ever used is the global OutBuffer
+        * which is always large enough.
+        */
+
+       outbuf = TALLOC_REALLOC_ARRAY(NULL, outbuf, char,
+                                     to_move + ofs + smb_wct);
+       if (outbuf == NULL) {
+               smb_panic("could not realloc outbuf");
        }
 
-       return outsize2;
+       req->outbuf = (uint8 *)outbuf;
+
+       memmove(outbuf + smb_wct + ofs, req2->outbuf + smb_wct, to_move);
+       memcpy(outbuf + smb_wct, caller_output, caller_outputlen);
+
+       /*
+        * copy the new reply header over the old one but preserve the smb_com
+        * field
+        */
+       memmove(outbuf, req2->outbuf, smb_wct);
+       SCVAL(outbuf, smb_com, smb_com1);
+
+       /*
+        * We've just copied in the whole "wct" area from the secondary
+        * function. Fix up the chaining: com2 and the offset need to be
+        * readjusted.
+        */
+
+       SCVAL(outbuf, smb_vwv0, smb_com2);
+       SSVAL(outbuf, smb_vwv1, chain_size + smb_wct - 4);
+
+       if (outsize_padded > outsize) {
+
+               /*
+                * Due to padding we have some uninitialized bytes after the
+                * caller's output
+                */
+
+               memset(outbuf + outsize, 0, outsize_padded - outsize);
+       }
+
+       smb_setlen(outbuf, outsize2 + chain_size - 4);
+
+       /*
+        * restore the saved data, being careful not to overwrite any data
+        * from the reply header
+        */
+       memcpy(inbuf2,inbuf_saved,smb_wct);
+
+       SAFE_FREE(caller_output);
+       TALLOC_FREE(req2);
+
+       return;
 }
 
 /****************************************************************************
@@ -1430,62 +1617,6 @@ machine %s in domain %s.\n", global_myname(), lp_workgroup()));
        return True;
 }
 
-/****************************************************************************
- Accessor functions for InBuffer, OutBuffer.
-****************************************************************************/
-
-char *get_InBuffer(void)
-{
-       return InBuffer;
-}
-
-char *get_OutBuffer(void)
-{
-       return OutBuffer;
-}
-
-const int total_buffer_size = (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN);
-
-/****************************************************************************
- Allocate a new InBuffer. Returns the new and old ones.
-****************************************************************************/
-
-static char *NewInBuffer(char **old_inbuf)
-{
-       char *new_inbuf = (char *)SMB_MALLOC(total_buffer_size);
-       if (!new_inbuf) {
-               return NULL;
-       }
-       if (old_inbuf) {
-               *old_inbuf = InBuffer;
-       }
-       InBuffer = new_inbuf;
-#if defined(DEVELOPER)
-       clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, InBuffer, total_buffer_size);
-#endif
-       return InBuffer;
-}
-
-/****************************************************************************
- Allocate a new OutBuffer. Returns the new and old ones.
-****************************************************************************/
-
-static char *NewOutBuffer(char **old_outbuf)
-{
-       char *new_outbuf = (char *)SMB_MALLOC(total_buffer_size);
-       if (!new_outbuf) {
-               return NULL;
-       }
-       if (old_outbuf) {
-               *old_outbuf = OutBuffer;
-       }
-       OutBuffer = new_outbuf;
-#if defined(DEVELOPER)
-       clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, OutBuffer, total_buffer_size);
-#endif
-       return OutBuffer;
-}
-
 /****************************************************************************
  Process commands from the client
 ****************************************************************************/
@@ -1495,23 +1626,17 @@ void smbd_process(void)
        time_t last_timeout_processing_time = time(NULL);
        unsigned int num_smbs = 0;
 
-       /* Allocate the primary Inbut/Output buffers. */
-
-       if ((NewInBuffer(NULL) == NULL) || (NewOutBuffer(NULL) == NULL)) 
-               return;
-
        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();
 
                errno = 0;      
                
-               /* free up temporary memory */
-               lp_TALLOC_FREE();
-               main_loop_TALLOC_FREE();
-
                /* Did someone ask for immediate checks on things like blocking locks ? */
                if (select_timeout == 0) {
                        if(!timeout_processing(&select_timeout,
@@ -1522,17 +1647,15 @@ void smbd_process(void)
 
                run_events(smbd_event_context(), 0, NULL, NULL);
 
-#if defined(DEVELOPER)
-               clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, InBuffer, total_buffer_size);
-#endif
-
-               while (!receive_message_or_smb(InBuffer,BUFFER_SIZE+LARGE_WRITEX_HDR_SIZE,select_timeout)) {
+               while (!receive_message_or_smb(NULL, &inbuf, &inbuf_len,
+                                              select_timeout)) {
                        if(!timeout_processing(&select_timeout,
                                               &last_timeout_processing_time))
                                return;
                        num_smbs = 0; /* Reset smb counter. */
                }
 
+
                /*
                 * Ensure we do timeout processing if the SMB we just got was
                 * only an echo request. This allows us to set the select
@@ -1544,9 +1667,9 @@ void smbd_process(void)
                 */ 
                num_echos = smb_echo_count;
 
-               clobber_region(SAFE_STRING_FUNCTION_NAME, SAFE_STRING_LINE, OutBuffer, total_buffer_size);
+               process_smb(inbuf, inbuf_len);
 
-               process_smb(InBuffer, OutBuffer);
+               TALLOC_FREE(inbuf);
 
                if (smb_echo_count != num_echos) {
                        if(!timeout_processing( &select_timeout, &last_timeout_processing_time))
@@ -1586,5 +1709,6 @@ void smbd_process(void)
                        change_to_root_user();
                        check_log_size();
                }
+               TALLOC_FREE(frame);
        }
 }