s4:smb_server/smb: remove a request from the list before adding the next one in a...
[samba.git] / source4 / smb_server / smb / receive.c
index 73c2ccb5b48ae248f0869ec6a0befffea68fe201..b100757b551e5382aa6782205c2c51b0d832053f 100644 (file)
@@ -7,7 +7,7 @@
    
    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
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    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"
 #include "system/time.h"
 #include "smbd/service_stream.h"
 #include "smb_server/smb_server.h"
-#include "ntvfs/ntvfs.h"
-
+#include "system/filesys.h"
+#include "param/param.h"
+#include "cluster/cluster.h"
 
 /*
   send an oplock break request to a client
 */
-NTSTATUS smbsrv_send_oplock_break(void *p, uint16_t fnum, uint8_t level)
+NTSTATUS smbsrv_send_oplock_break(void *p, struct ntvfs_handle *ntvfs, uint8_t level)
 {
        struct smbsrv_tcon *tcon = talloc_get_type(p, struct smbsrv_tcon);
        struct smbsrv_request *req;
@@ -50,7 +50,7 @@ NTSTATUS smbsrv_send_oplock_break(void *p, uint16_t fnum, uint8_t level)
 
        SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE);
        SSVAL(req->out.vwv, VWV(1), 0);
-       SSVAL(req->out.vwv, VWV(2), fnum);
+       smbsrv_push_fnum(req->out.vwv, VWV(2), ntvfs);
        SCVAL(req->out.vwv, VWV(3), LOCKING_ANDX_OPLOCK_RELEASE);
        SCVAL(req->out.vwv, VWV(3)+1, level);
        SIVAL(req->out.vwv, VWV(4), 0);
@@ -63,104 +63,16 @@ NTSTATUS smbsrv_send_oplock_break(void *p, uint16_t fnum, uint8_t level)
 
 static void switch_message(int type, struct smbsrv_request *req);
 
-/****************************************************************************
-receive a SMB request header from the wire, forming a request_context
-from the result
-****************************************************************************/
-NTSTATUS smbsrv_recv_smb_request(void *private, DATA_BLOB blob)
-{
-       struct smbsrv_connection *smb_conn = talloc_get_type(private, struct smbsrv_connection);
-       struct smbsrv_request *req;
-       uint8_t command;
-
-       /* see if its a special NBT packet */
-       if (CVAL(blob.data, 0) != 0) {
-               req = smbsrv_init_request(smb_conn);
-               NT_STATUS_HAVE_NO_MEMORY(req);
-
-               ZERO_STRUCT(req->in);
-
-               req->in.buffer = talloc_steal(req, blob.data);
-               req->in.size = blob.length;
-               req->request_time = timeval_current();
-
-               smbsrv_reply_special(req);
-               return NT_STATUS_OK;
-       }
-
-       if ((NBT_HDR_SIZE + MIN_SMB_SIZE) > blob.length) {
-               DEBUG(2,("Invalid SMB packet: length %ld\n", (long)blob.length));
-               smbsrv_terminate_connection(smb_conn, "Invalid SMB packet");
-               return NT_STATUS_OK;
-       }
-
-       /* Make sure this is an SMB packet */
-       if (IVAL(blob.data, NBT_HDR_SIZE) != SMB_MAGIC) {
-               DEBUG(2,("Non-SMB packet of length %ld. Terminating connection\n",
-                        (long)blob.length));
-               smbsrv_terminate_connection(smb_conn, "Non-SMB packet");
-               return NT_STATUS_OK;
-       }
-
-       req = smbsrv_init_request(smb_conn);
-       NT_STATUS_HAVE_NO_MEMORY(req);
-
-       req->in.buffer = talloc_steal(req, blob.data);
-       req->in.size = blob.length;
-       req->request_time = timeval_current();
-       req->chained_fnum = -1;
-       req->in.allocated = req->in.size;
-       req->in.hdr = req->in.buffer + NBT_HDR_SIZE;
-       req->in.vwv = req->in.hdr + HDR_VWV;
-       req->in.wct = CVAL(req->in.hdr, HDR_WCT);
-       if (req->in.vwv + VWV(req->in.wct) <= req->in.buffer + req->in.size) {
-               req->in.data = req->in.vwv + VWV(req->in.wct) + 2;
-               req->in.data_size = SVAL(req->in.vwv, VWV(req->in.wct));
-
-               /* the bcc length is only 16 bits, but some packets
-                  (such as SMBwriteX) can be much larger than 64k. We
-                  detect this by looking for a large non-chained NBT
-                  packet (at least 64k bigger than what is
-                  specified). If it is detected then the NBT size is
-                  used instead of the bcc size */
-               if (req->in.data_size + 0x10000 <= 
-                   req->in.size - PTR_DIFF(req->in.data, req->in.buffer) &&
-                   (req->in.wct < 1 || SVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE)) {
-                       /* its an oversized packet! fun for all the family */
-                       req->in.data_size = req->in.size - PTR_DIFF(req->in.data,req->in.buffer);
-               }
-       }
-
-       if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct > req->in.size) {
-               DEBUG(2,("Invalid SMB word count %d\n", req->in.wct));
-               smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet");
-               return NT_STATUS_OK;
-       }
-       if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct + req->in.data_size > req->in.size) {
-               DEBUG(2,("Invalid SMB buffer length count %d\n", req->in.data_size));
-               smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet");
-               return NT_STATUS_OK;
-       }
-
-       req->flags2     = SVAL(req->in.hdr, HDR_FLG2);
-
-       if (!smbsrv_signing_check_incoming(req)) {
-               smbsrv_send_error(req, NT_STATUS_ACCESS_DENIED);
-               return NT_STATUS_OK;
-       }
-
-       command = CVAL(req->in.hdr, HDR_COM);
-       switch_message(command, req);
-       return NT_STATUS_OK;
-}
-
 /*
   These flags determine some of the permissions required to do an operation 
 */
 #define NEED_SESS              (1<<0)
 #define NEED_TCON              (1<<1)
 #define SIGNING_NO_REPLY       (1<<2)
+/* does VWV(0) of the request hold chaining information */
+#define AND_X                  (1<<3)
+/* The 64Kb question: are requests > 64K valid? */
+#define LARGE_REQUEST  (1<<4)
 
 /* 
    define a list of possible SMB messages and their corresponding
@@ -171,6 +83,7 @@ static const struct smb_message_struct
 {
        const char *name;
        void (*fn)(struct smbsrv_request *);
+#define message_flags(type) smb_messages[(type) & 0xff].flags
        int flags;
 }
  smb_messages[256] = {
@@ -190,7 +103,7 @@ static const struct smb_message_struct
 /* 0x0d */ { "SMBunlock",      smbsrv_reply_unlock,            NEED_SESS|NEED_TCON },
 /* 0x0e */ { "SMBctemp",       smbsrv_reply_ctemp,             NEED_SESS|NEED_TCON },
 /* 0x0f */ { "SMBmknew",       smbsrv_reply_mknew,             NEED_SESS|NEED_TCON }, 
-/* 0x10 */ { "SMBchkpth",      smbsrv_reply_chkpth,            NEED_SESS|NEED_TCON },
+/* 0x10 */ { "SMBcheckpath",   smbsrv_reply_chkpth,            NEED_SESS|NEED_TCON },
 /* 0x11 */ { "SMBexit",                smbsrv_reply_exit,              NEED_SESS },
 /* 0x12 */ { "SMBlseek",       smbsrv_reply_lseek,             NEED_SESS|NEED_TCON },
 /* 0x13 */ { "SMBlockread",    smbsrv_reply_lockread,          NEED_SESS|NEED_TCON },
@@ -210,7 +123,7 @@ static const struct smb_message_struct
 /* 0x21 */ { NULL, NULL, 0 },
 /* 0x22 */ { "SMBsetattrE",    smbsrv_reply_setattrE,          NEED_SESS|NEED_TCON },
 /* 0x23 */ { "SMBgetattrE",    smbsrv_reply_getattrE,          NEED_SESS|NEED_TCON },
-/* 0x24 */ { "SMBlockingX",    smbsrv_reply_lockingX,          NEED_SESS|NEED_TCON },
+/* 0x24 */ { "SMBlockingX",    smbsrv_reply_lockingX,          NEED_SESS|NEED_TCON|AND_X },
 /* 0x25 */ { "SMBtrans",       smbsrv_reply_trans,             NEED_SESS|NEED_TCON },
 /* 0x26 */ { "SMBtranss",      smbsrv_reply_transs,            NEED_SESS|NEED_TCON },
 /* 0x27 */ { "SMBioctl",       smbsrv_reply_ioctl,             NEED_SESS|NEED_TCON },
@@ -219,9 +132,9 @@ static const struct smb_message_struct
 /* 0x2a */ { "SMBmove",                NULL,                           NEED_SESS|NEED_TCON },
 /* 0x2b */ { "SMBecho",                smbsrv_reply_echo,              0 },
 /* 0x2c */ { "SMBwriteclose",  smbsrv_reply_writeclose,        NEED_SESS|NEED_TCON },
-/* 0x2d */ { "SMBopenX",       smbsrv_reply_open_and_X,        NEED_SESS|NEED_TCON },
-/* 0x2e */ { "SMBreadX",       smbsrv_reply_read_and_X,        NEED_SESS|NEED_TCON },
-/* 0x2f */ { "SMBwriteX",      smbsrv_reply_write_and_X,       NEED_SESS|NEED_TCON},
+/* 0x2d */ { "SMBopenX",       smbsrv_reply_open_and_X,        NEED_SESS|NEED_TCON|AND_X },
+/* 0x2e */ { "SMBreadX",       smbsrv_reply_read_and_X,        NEED_SESS|NEED_TCON|AND_X },
+/* 0x2f */ { "SMBwriteX",      smbsrv_reply_write_and_X,       NEED_SESS|NEED_TCON|AND_X|LARGE_REQUEST},
 /* 0x30 */ { NULL, NULL, 0 },
 /* 0x31 */ { NULL, NULL, 0 },
 /* 0x32 */ { "SMBtrans2",      smbsrv_reply_trans2,            NEED_SESS|NEED_TCON },
@@ -287,11 +200,11 @@ static const struct smb_message_struct
 /* 0x6e */ { NULL, NULL, 0 },
 /* 0x6f */ { NULL, NULL, 0 },
 /* 0x70 */ { "SMBtcon",                smbsrv_reply_tcon,              NEED_SESS },
-/* 0x71 */ { "SMBtdis",                smbsrv_reply_tdis,              NEED_SESS },
+/* 0x71 */ { "SMBtdis",                smbsrv_reply_tdis,              NEED_TCON },
 /* 0x72 */ { "SMBnegprot",     smbsrv_reply_negprot,           0 },
-/* 0x73 */ { "SMBsesssetupX",  smbsrv_reply_sesssetup,         0 },
-/* 0x74 */ { "SMBulogoffX",    smbsrv_reply_ulogoffX,          NEED_SESS }, /* ulogoff doesn't give a valid TID */
-/* 0x75 */ { "SMBtconX",       smbsrv_reply_tcon_and_X,        NEED_SESS },
+/* 0x73 */ { "SMBsesssetupX",  smbsrv_reply_sesssetup,         AND_X },
+/* 0x74 */ { "SMBulogoffX",    smbsrv_reply_ulogoffX,          NEED_SESS|AND_X }, /* ulogoff doesn't give a valid TID */
+/* 0x75 */ { "SMBtconX",       smbsrv_reply_tcon_and_X,        NEED_SESS|AND_X },
 /* 0x76 */ { NULL, NULL, 0 },
 /* 0x77 */ { NULL, NULL, 0 },
 /* 0x78 */ { NULL, NULL, 0 },
@@ -334,9 +247,9 @@ static const struct smb_message_struct
 /* 0x9d */ { NULL, NULL, 0 },
 /* 0x9e */ { NULL, NULL, 0 },
 /* 0x9f */ { NULL, NULL, 0 },
-/* 0xa0 */ { "SMBnttrans",     smbsrv_reply_nttrans,           NEED_SESS|NEED_TCON },
+/* 0xa0 */ { "SMBnttrans",     smbsrv_reply_nttrans,           NEED_SESS|NEED_TCON|LARGE_REQUEST },
 /* 0xa1 */ { "SMBnttranss",    smbsrv_reply_nttranss,          NEED_SESS|NEED_TCON },
-/* 0xa2 */ { "SMBntcreateX",   smbsrv_reply_ntcreate_and_X,    NEED_SESS|NEED_TCON },
+/* 0xa2 */ { "SMBntcreateX",   smbsrv_reply_ntcreate_and_X,    NEED_SESS|NEED_TCON|AND_X },
 /* 0xa3 */ { NULL, NULL, 0 },
 /* 0xa4 */ { "SMBntcancel",    smbsrv_reply_ntcancel,          NEED_SESS|NEED_TCON|SIGNING_NO_REPLY },
 /* 0xa5 */ { "SMBntrename",    smbsrv_reply_ntrename,          NEED_SESS|NEED_TCON },
@@ -432,6 +345,106 @@ static const struct smb_message_struct
 /* 0xff */ { NULL, NULL, 0 }
 };
 
+/****************************************************************************
+receive a SMB request header from the wire, forming a request_context
+from the result
+****************************************************************************/
+NTSTATUS smbsrv_recv_smb_request(void *private_data, DATA_BLOB blob)
+{
+       struct smbsrv_connection *smb_conn = talloc_get_type(private_data, struct smbsrv_connection);
+       struct smbsrv_request *req;
+       struct timeval cur_time = timeval_current();
+       uint8_t command;
+
+       smb_conn->statistics.last_request_time = cur_time;
+
+       /* see if its a special NBT packet */
+       if (CVAL(blob.data, 0) != 0) {
+               req = smbsrv_init_request(smb_conn);
+               NT_STATUS_HAVE_NO_MEMORY(req);
+
+               ZERO_STRUCT(req->in);
+
+               req->in.buffer = talloc_steal(req, blob.data);
+               req->in.size = blob.length;
+               req->request_time = cur_time;
+
+               smbsrv_reply_special(req);
+               return NT_STATUS_OK;
+       }
+
+       if ((NBT_HDR_SIZE + MIN_SMB_SIZE) > blob.length) {
+               DEBUG(2,("Invalid SMB packet: length %ld\n", (long)blob.length));
+               smbsrv_terminate_connection(smb_conn, "Invalid SMB packet");
+               return NT_STATUS_OK;
+       }
+
+       /* Make sure this is an SMB packet */
+       if (IVAL(blob.data, NBT_HDR_SIZE) != SMB_MAGIC) {
+               DEBUG(2,("Non-SMB packet of length %ld. Terminating connection\n",
+                        (long)blob.length));
+               smbsrv_terminate_connection(smb_conn, "Non-SMB packet");
+               return NT_STATUS_OK;
+       }
+
+       req = smbsrv_init_request(smb_conn);
+       NT_STATUS_HAVE_NO_MEMORY(req);
+
+       req->in.buffer = talloc_steal(req, blob.data);
+       req->in.size = blob.length;
+       req->request_time = cur_time;
+       req->chained_fnum = -1;
+       req->in.allocated = req->in.size;
+       req->in.hdr = req->in.buffer + NBT_HDR_SIZE;
+       req->in.vwv = req->in.hdr + HDR_VWV;
+       req->in.wct = CVAL(req->in.hdr, HDR_WCT);
+
+       command = CVAL(req->in.hdr, HDR_COM);
+
+       if (req->in.vwv + VWV(req->in.wct) <= req->in.buffer + req->in.size) {
+               req->in.data = req->in.vwv + VWV(req->in.wct) + 2;
+               req->in.data_size = SVAL(req->in.vwv, VWV(req->in.wct));
+
+               /* special handling for oversize calls. Windows seems
+                  to take the maximum of the BCC value and the
+                  computed buffer size. This handles oversized writeX
+                  calls, and possibly oversized SMBtrans calls */
+               if ((message_flags(command) & LARGE_REQUEST) &&
+                   ( !(message_flags(command) & AND_X) ||
+                     (req->in.wct < 1 || SVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE)) &&
+                   req->in.data_size < req->in.size - PTR_DIFF(req->in.data,req->in.buffer)) {
+                       req->in.data_size = req->in.size - PTR_DIFF(req->in.data,req->in.buffer);
+               }
+       }
+
+       if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct > req->in.size) {
+               DEBUG(2,("Invalid SMB word count %d\n", req->in.wct));
+               smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet");
+               return NT_STATUS_OK;
+       }
+       if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct + req->in.data_size > req->in.size) {
+               DEBUG(2,("Invalid SMB buffer length count %d\n", 
+                        (int)req->in.data_size));
+               smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet");
+               return NT_STATUS_OK;
+       }
+
+       req->flags2     = SVAL(req->in.hdr, HDR_FLG2);
+
+       /* fix the bufinfo */
+       smbsrv_setup_bufinfo(req);
+
+       if (!smbsrv_signing_check_incoming(req)) {
+               smbsrv_send_error(req, NT_STATUS_ACCESS_DENIED);
+               return NT_STATUS_OK;
+       }
+
+       command = CVAL(req->in.hdr, HDR_COM);
+       switch_message(command, req);
+       return NT_STATUS_OK;
+}
+
 /****************************************************************************
 return a string containing the function name of a SMB command
 ****************************************************************************/
@@ -458,6 +471,7 @@ static void switch_message(int type, struct smbsrv_request *req)
        int flags;
        struct smbsrv_connection *smb_conn = req->smb_conn;
        NTSTATUS status;
+       char *task_id;
 
        type &= 0xff;
 
@@ -471,24 +485,20 @@ static void switch_message(int type, struct smbsrv_request *req)
 
        flags = smb_messages[type].flags;
 
-       req->tcon = smbsrv_smb_tcon_find(smb_conn, SVAL(req->in.hdr,HDR_TID));
+       req->tcon = smbsrv_smb_tcon_find(smb_conn, SVAL(req->in.hdr,HDR_TID), req->request_time);
 
        if (!req->session) {
                /* setup the user context for this request if it
                   hasn't already been initialised (to cope with SMB
                   chaining) */
 
-               /* In share mode security we must ignore the vuid. */
-               if (smb_conn->config.security == SEC_SHARE) {
-                       if (req->tcon) {
-                               req->session = req->tcon->sec_share.session;
-                       }
-               } else {
-                       req->session = smbsrv_session_find(req->smb_conn, SVAL(req->in.hdr,HDR_UID));
-               }
+               req->session = smbsrv_session_find(req->smb_conn, SVAL(req->in.hdr,HDR_UID), req->request_time);
        }
 
-       DEBUG(5,("switch message %s (task_id %d)\n",smb_fn_name(type), req->smb_conn->connection->server_id));
+       task_id = server_id_str(NULL, &req->smb_conn->connection->server_id);
+       DEBUG(5,("switch message %s (task_id %s)\n",
+                smb_fn_name(type), task_id));
+       talloc_free(task_id);
 
        /* this must be called before we do any reply */
        if (flags & SIGNING_NO_REPLY) {
@@ -497,16 +507,19 @@ static void switch_message(int type, struct smbsrv_request *req)
 
        /* see if the vuid is valid */
        if ((flags & NEED_SESS) && !req->session) {
+               status = NT_STATUS_DOS(ERRSRV, ERRbaduid);
                /* amazingly, the error code depends on the command */
                switch (type) {
-                       case SMBntcreateX:
-                       case SMBntcancel:
-                       case SMBulogoffX:
-                               status = NT_STATUS_DOS(ERRSRV, ERRbaduid);
-                               break;
-                       default:
+               case SMBntcreateX:
+               case SMBntcancel:
+               case SMBulogoffX:
+                       break;
+               default:
+                       if (req->smb_conn->config.nt_status_support &&
+                           req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
                                status = NT_STATUS_INVALID_HANDLE;
-                               break;
+                       }
+                       break;
                }
                /* 
                 * TODO:
@@ -526,15 +539,19 @@ static void switch_message(int type, struct smbsrv_request *req)
 
        /* does this protocol need a valid tree connection? */
        if ((flags & NEED_TCON) && !req->tcon) {
+               status = NT_STATUS_DOS(ERRSRV, ERRinvnid);
                /* amazingly, the error code depends on the command */
                switch (type) {
-                       case SMBntcreateX:
-                       case SMBntcancel:
-                               status = NT_STATUS_DOS(ERRSRV, ERRinvnid);
-                               break;
-                       default:
+               case SMBntcreateX:
+               case SMBntcancel:
+               case SMBtdis:
+                       break;
+               default:
+                       if (req->smb_conn->config.nt_status_support &&
+                           req->smb_conn->negotiate.client_caps & CAP_STATUS32) {
                                status = NT_STATUS_INVALID_HANDLE;
-                               break;
+                       }
+                       break;
                }
                /* 
                 * TODO:
@@ -607,12 +624,16 @@ void smbsrv_chain_reply(struct smbsrv_request *req)
        req->in.data_size = data_size;
        req->in.ptr = data;
 
+       /* fix the bufinfo */
+       smbsrv_setup_bufinfo(req);
+
        req->chain_count++;
 
        SSVAL(req->out.vwv, VWV(0), chain_cmd);
        SSVAL(req->out.vwv, VWV(1), req->out.size - NBT_HDR_SIZE);
 
        /* cleanup somestuff for the next request */
+       DLIST_REMOVE(req->smb_conn->requests, req);
        talloc_free(req->ntvfs);
        req->ntvfs = NULL;
        talloc_free(req->io_ptr);
@@ -630,7 +651,7 @@ error:
 /*
  * init the SMB protocol related stuff
  */
-NTSTATUS smbsrv_init_smb_connection(struct smbsrv_connection *smb_conn)
+NTSTATUS smbsrv_init_smb_connection(struct smbsrv_connection *smb_conn, struct loadparm_context *lp_ctx)
 {
        NTSTATUS status;
 
@@ -639,12 +660,11 @@ NTSTATUS smbsrv_init_smb_connection(struct smbsrv_connection *smb_conn)
 
        /* this is the size that w2k uses, and it appears to be important for
           good performance */
-       smb_conn->negotiate.max_recv = lp_max_xmit();
+       smb_conn->negotiate.max_recv = lpcfg_max_xmit(lp_ctx);
 
        smb_conn->negotiate.zone_offset = get_time_zone(time(NULL));
 
-       smb_conn->config.security = lp_security();
-       smb_conn->config.nt_status_support = lp_nt_status_support();
+       smb_conn->config.nt_status_support = lpcfg_nt_status_support(lp_ctx);
 
        status = smbsrv_init_sessions(smb_conn, UINT16_MAX);
        NT_STATUS_NOT_OK_RETURN(status);