s4:smb_server/smb2: use helper variable smb2srv_chain_reply()
[amitay/samba.git] / source4 / smb_server / smb2 / receive.c
index 5a857e133f0fc71ea65d0f9e130079586a92fb7d..b8aba71aaa777c4b3748cd1c88e54b50156ca826 100644 (file)
 #include "libcli/smb2/smb2.h"
 #include "libcli/smb2/smb2_calls.h"
 #include "smb_server/smb_server.h"
-#include "smb_server/service_smb_proto.h"
 #include "smb_server/smb2/smb2_server.h"
 #include "smbd/service_stream.h"
 #include "lib/stream/packet.h"
 #include "ntvfs/ntvfs.h"
 #include "param/param.h"
-#include "auth/gensec/gensec.h"
 #include "auth/auth.h"
 
 
@@ -71,6 +69,9 @@ struct smb2srv_request *smb2srv_init_request(struct smbsrv_connection *smb_conn)
 
        req->smb_conn = smb_conn;
 
+       req->chained_session_id = UINT64_MAX;
+       req->chained_tree_id = UINT32_MAX;
+
        talloc_set_destructor(req, smb2srv_request_destructor);
 
        return req;
@@ -79,14 +80,22 @@ struct smb2srv_request *smb2srv_init_request(struct smbsrv_connection *smb_conn)
 NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_size,
                             bool body_dynamic_present, uint32_t body_dynamic_size)
 {
-       uint32_t flags = SMB2_HDR_FLAG_REDIRECT;
+       uint32_t flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
        uint32_t pid = IVAL(req->in.hdr, SMB2_HDR_PID);
        uint32_t tid = IVAL(req->in.hdr, SMB2_HDR_TID);
+       uint16_t credits = SVAL(req->in.hdr, SMB2_HDR_CREDIT);
+
+       if (credits == 0) {
+               credits = 1;
+       }
+
+       flags |= SMB2_HDR_FLAG_REDIRECT;
 
        if (req->pending_id) {
                flags |= SMB2_HDR_FLAG_ASYNC;
                pid = req->pending_id;
                tid = 0;
+               credits = 0;
        }
 
        if (body_dynamic_present) {
@@ -112,17 +121,19 @@ NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_si
 
        SIVAL(req->out.hdr, 0,                          SMB2_MAGIC);
        SSVAL(req->out.hdr, SMB2_HDR_LENGTH,            SMB2_HDR_BODY);
-       SSVAL(req->out.hdr, SMB2_HDR_EPOCH,             0);
+       SSVAL(req->out.hdr, SMB2_HDR_CREDIT_CHARGE,
+             SVAL(req->in.hdr, SMB2_HDR_CREDIT_CHARGE));
        SIVAL(req->out.hdr, SMB2_HDR_STATUS,            NT_STATUS_V(req->status));
        SSVAL(req->out.hdr, SMB2_HDR_OPCODE,            SVAL(req->in.hdr, SMB2_HDR_OPCODE));
-       SSVAL(req->out.hdr, SMB2_HDR_CREDIT,            0x0001);
+       SSVAL(req->out.hdr, SMB2_HDR_CREDIT,            credits);
        SIVAL(req->out.hdr, SMB2_HDR_FLAGS,             flags);
        SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND,      0);
        SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID,        req->seqnum);
        SIVAL(req->out.hdr, SMB2_HDR_PID,               pid);
        SIVAL(req->out.hdr, SMB2_HDR_TID,               tid);
        SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID,        BVAL(req->in.hdr, SMB2_HDR_SESSION_ID));
-       memset(req->out.hdr+SMB2_HDR_SIGNATURE, 0, 16);
+       memcpy(req->out.hdr+SMB2_HDR_SIGNATURE,
+              req->in.hdr+SMB2_HDR_SIGNATURE, 16);
 
        /* set the length of the fixed body part and +1 if there's a dynamic part also */
        SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0));
@@ -144,33 +155,38 @@ static NTSTATUS smb2srv_reply(struct smb2srv_request *req);
 static void smb2srv_chain_reply(struct smb2srv_request *p_req)
 {
        NTSTATUS status;
+       struct smbsrv_connection *smb_conn = p_req->smb_conn;
        struct smb2srv_request *req;
        uint32_t chain_offset;
        uint32_t protocol_version;
        uint16_t buffer_code;
        uint32_t dynamic_size;
+       uint32_t flags;
+       uint32_t last_hdr_offset;
+
+       last_hdr_offset = p_req->in.hdr - p_req->in.buffer;
 
        chain_offset = p_req->chain_offset;
        p_req->chain_offset = 0;
 
-       if (p_req->in.size < (NBT_HDR_SIZE + chain_offset + SMB2_MIN_SIZE_NO_BODY)) {
-               DEBUG(2,("Invalid SMB2 chained packet at offset 0x%X\n",
-                       chain_offset));
-               smbsrv_terminate_connection(p_req->smb_conn, "Invalid SMB2 chained packet");
+       if (p_req->in.size < (last_hdr_offset + chain_offset + SMB2_MIN_SIZE_NO_BODY)) {
+               DEBUG(2,("Invalid SMB2 chained packet at offset 0x%X from last hdr 0x%X\n",
+                       chain_offset, last_hdr_offset));
+               smbsrv_terminate_connection(smb_conn, "Invalid SMB2 chained packet");
                return;
        }
 
-       protocol_version = IVAL(p_req->in.buffer, NBT_HDR_SIZE + chain_offset);
+       protocol_version = IVAL(p_req->in.buffer, last_hdr_offset + chain_offset);
        if (protocol_version != SMB2_MAGIC) {
                DEBUG(2,("Invalid SMB chained packet: protocol prefix: 0x%08X\n",
                         protocol_version));
-               smbsrv_terminate_connection(p_req->smb_conn, "NON-SMB2 chained packet");
+               smbsrv_terminate_connection(smb_conn, "NON-SMB2 chained packet");
                return;
        }
 
-       req = smb2srv_init_request(p_req->smb_conn);
+       req = smb2srv_init_request(smb_conn);
        if (!req) {
-               smbsrv_terminate_connection(p_req->smb_conn, "SMB2 chained packet - no memory");
+               smbsrv_terminate_connection(smb_conn, "SMB2 chained packet - no memory");
                return;
        }
 
@@ -179,9 +195,9 @@ static void smb2srv_chain_reply(struct smb2srv_request *p_req)
        req->request_time       = p_req->request_time;
        req->in.allocated       = req->in.size;
 
-       req->in.hdr             = req->in.buffer+ NBT_HDR_SIZE + chain_offset;
+       req->in.hdr             = req->in.buffer+ last_hdr_offset + chain_offset;
        req->in.body            = req->in.hdr   + SMB2_HDR_BODY;
-       req->in.body_size       = req->in.size  - (NBT_HDR_SIZE+ chain_offset + SMB2_HDR_BODY);
+       req->in.body_size       = req->in.size  - (last_hdr_offset+ chain_offset + SMB2_HDR_BODY);
        req->in.dynamic         = NULL;
 
        req->seqnum             = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
@@ -191,7 +207,7 @@ static void smb2srv_chain_reply(struct smb2srv_request *p_req)
                   other packet types */
                uint16_t opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
                if (opcode == SMB2_OP_NEGPROT) {
-                       smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot");                    
+                       smbsrv_terminate_connection(smb_conn, "Bad body size in SMB2 negprot");
                } else {
                        smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
                }
@@ -213,11 +229,17 @@ static void smb2srv_chain_reply(struct smb2srv_request *p_req)
 
        smb2srv_setup_bufinfo(req);
 
-       if (p_req->chained_file_handle) {
-               memcpy(req->_chained_file_handle,
-                      p_req->_chained_file_handle,
-                      sizeof(req->_chained_file_handle));
-               req->chained_file_handle = req->_chained_file_handle;
+       flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+       if (flags & SMB2_HDR_FLAG_CHAINED) {
+               if (p_req->chained_file_handle) {
+                       memcpy(req->_chained_file_handle,
+                              p_req->_chained_file_handle,
+                              sizeof(req->_chained_file_handle));
+                       req->chained_file_handle = req->_chained_file_handle;
+               }
+               req->chained_session_id = p_req->chained_session_id;
+               req->chained_tree_id = p_req->chained_tree_id;
+               req->chain_status = p_req->chain_status;
        }
 
        /* 
@@ -227,7 +249,7 @@ static void smb2srv_chain_reply(struct smb2srv_request *p_req)
 
        status = smb2srv_reply(req);
        if (!NT_STATUS_IS_OK(status)) {
-               smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+               smbsrv_terminate_connection(smb_conn, nt_errstr(status));
                talloc_free(req);
                return;
        }
@@ -245,7 +267,7 @@ void smb2srv_send_reply(struct smb2srv_request *req)
        }
 
        if (req->out.size > NBT_HDR_SIZE) {
-               _smb2_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+               _smb_setlen_tcp(req->out.buffer, req->out.size - NBT_HDR_SIZE);
        }
 
        /* if signing is active on the session then sign the packet */
@@ -293,6 +315,8 @@ void smb2srv_send_error(struct smb2srv_request *req, NTSTATUS error)
        SSVAL(req->out.body, 0x02, 0);
        SIVAL(req->out.body, 0x04, 0);
 
+       req->chain_status = NT_STATUS_INVALID_PARAMETER;
+
        smb2srv_send_reply(req);
 }
 
@@ -314,16 +338,27 @@ static NTSTATUS smb2srv_reply(struct smb2srv_request *req)
        uid                     = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
        flags                   = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
 
-       if (req->smb_conn->highest_smb2_seqnum != 0 &&
+       if (opcode != SMB2_OP_CANCEL &&
+           req->smb_conn->highest_smb2_seqnum != 0 &&
            req->seqnum <= req->smb_conn->highest_smb2_seqnum) {
                smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 sequence number");
                return NT_STATUS_INVALID_PARAMETER;
        }
-       req->smb_conn->highest_smb2_seqnum = req->seqnum;
-       
+       if (opcode != SMB2_OP_CANCEL) {
+               req->smb_conn->highest_smb2_seqnum = req->seqnum;
+       }
+
+       if (flags & SMB2_HDR_FLAG_CHAINED) {
+               uid = req->chained_session_id;
+               tid = req->chained_tree_id;
+       }
+
        req->session    = smbsrv_session_find(req->smb_conn, uid, req->request_time);
        req->tcon       = smbsrv_smb2_tcon_find(req->session, tid, req->request_time);
 
+       req->chained_session_id = uid;
+       req->chained_tree_id = tid;
+
        errno = 0;
 
        /* supporting signing is mandatory in SMB2, and is per-packet. So we 
@@ -347,7 +382,10 @@ static NTSTATUS smb2srv_reply(struct smb2srv_request *req)
                return NT_STATUS_OK;                                    
        }
 
-       /* TODO: check the seqnum */
+       if (!NT_STATUS_IS_OK(req->chain_status)) {
+               smb2srv_send_error(req, req->chain_status);
+               return NT_STATUS_OK;
+       }
 
        switch (opcode) {
        case SMB2_OP_NEGPROT:
@@ -457,6 +495,7 @@ NTSTATUS smbsrv_recv_smb2_request(void *private_data, DATA_BLOB blob)
        uint32_t protocol_version;
        uint16_t buffer_code;
        uint32_t dynamic_size;
+       uint32_t flags;
 
        smb_conn->statistics.last_request_time = cur_time;
 
@@ -502,8 +541,10 @@ NTSTATUS smbsrv_recv_smb2_request(void *private_data, DATA_BLOB blob)
                uint16_t opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
                if (opcode == SMB2_OP_NEGPROT) {
                        smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot");                    
+                       return NT_STATUS_OK;
                } else {
                        smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+                       return NT_STATUS_OK;
                }
        }
 
@@ -528,6 +569,12 @@ NTSTATUS smbsrv_recv_smb2_request(void *private_data, DATA_BLOB blob)
         *       - make sure it's a request
         */
 
+       flags = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+       /* the first request should never have the related flag set */
+       if (flags & SMB2_HDR_FLAG_CHAINED) {
+               req->chain_status = NT_STATUS_INVALID_PARAMETER;
+       }
+
        return smb2srv_reply(req);
 }
 
@@ -546,11 +593,21 @@ NTSTATUS smb2srv_queue_pending(struct smb2srv_request *req)
        NTSTATUS status;
        bool signing_used = false;
        int id;
+       uint16_t credits = SVAL(req->in.hdr, SMB2_HDR_CREDIT);
+
+       if (credits == 0) {
+               credits = 1;
+       }
 
        if (req->pending_id) {
                return NT_STATUS_INTERNAL_ERROR;
        }
 
+       if (req->smb_conn->connection->event.fde == NULL) {
+               /* the socket has been destroyed - no point trying to send an error! */
+               return NT_STATUS_REMOTE_DISCONNECT;
+       }
+
        id = idr_get_new_above(req->smb_conn->requests2.idtree_req, req, 
                               1, req->smb_conn->requests2.idtree_limit);
        if (id == -1) {
@@ -560,11 +617,6 @@ NTSTATUS smb2srv_queue_pending(struct smb2srv_request *req)
        DLIST_ADD_END(req->smb_conn->requests2.list, req, struct smb2srv_request *);
        req->pending_id = id;
 
-       if (req->smb_conn->connection->event.fde == NULL) {
-               /* the socket has been destroyed - no point trying to send an error! */
-               return NT_STATUS_REMOTE_DISCONNECT;
-       }
-
        talloc_set_destructor(req, smb2srv_request_deny_destructor);
 
        status = smb2srv_setup_reply(req, 8, true, 0);
@@ -573,6 +625,7 @@ NTSTATUS smb2srv_queue_pending(struct smb2srv_request *req)
        }
 
        SIVAL(req->out.hdr, SMB2_HDR_STATUS, NT_STATUS_V(STATUS_PENDING));
+       SSVAL(req->out.hdr, SMB2_HDR_CREDIT, credits);
 
        SSVAL(req->out.body, 0x02, 0);
        SIVAL(req->out.body, 0x04, 0);
@@ -636,11 +689,10 @@ NTSTATUS smbsrv_init_smb2_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->lp_ctx);
+       smb_conn->negotiate.max_recv = lpcfg_max_xmit(smb_conn->lp_ctx);
 
        smb_conn->negotiate.zone_offset = get_time_zone(time(NULL));
 
-       smb_conn->config.security = SEC_USER;
        smb_conn->config.nt_status_support = true;
 
        status = smbsrv_init_sessions(smb_conn, UINT64_MAX);