dlist: remove unneeded type argument from DLIST_ADD_END()
[samba.git] / source4 / smb_server / smb2 / receive.c
index 0f8eb101cf77b8a7162ffe635d7e1e21224c8de2..3e5dc4de173b43b0966825c26a292f17b581f584 100644 (file)
@@ -6,7 +6,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,
@@ -15,8 +15,7 @@
    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 "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/auth.h"
 
 
-static struct smb2srv_request *smb2srv_init_request(struct smbsrv_connection *smb_conn)
+/* fill in the bufinfo */
+void smb2srv_setup_bufinfo(struct smb2srv_request *req)
+{
+       req->in.bufinfo.mem_ctx    = req;
+       req->in.bufinfo.flags      = BUFINFO_FLAG_UNICODE | BUFINFO_FLAG_SMB2;
+       req->in.bufinfo.align_base = req->in.buffer;
+       if (req->in.dynamic) {
+               req->in.bufinfo.data       = req->in.dynamic;
+               req->in.bufinfo.data_size  = req->in.body_size - req->in.body_fixed;
+       } else {
+               req->in.bufinfo.data       = NULL;
+               req->in.bufinfo.data_size  = 0;
+       }
+}
+
+static int smb2srv_request_destructor(struct smb2srv_request *req)
+{
+       DLIST_REMOVE(req->smb_conn->requests2.list, req);
+       if (req->pending_id) {
+               idr_remove(req->smb_conn->requests2.idtree_req, req->pending_id);
+       }
+       return 0;
+}
+
+static int smb2srv_request_deny_destructor(struct smb2srv_request *req)
+{
+       return -1;
+}
+
+struct smb2srv_request *smb2srv_init_request(struct smbsrv_connection *smb_conn)
 {
        struct smb2srv_request *req;
 
@@ -39,12 +69,35 @@ static struct smb2srv_request *smb2srv_init_request(struct smbsrv_connection *sm
 
        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;
 }
 
 NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_size,
-                            BOOL body_dynamic_present, uint32_t body_dynamic_size)
+                            bool body_dynamic_present, uint32_t body_dynamic_size)
 {
+       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) {
                if (body_dynamic_size == 0) {
                        body_dynamic_size = 1;
@@ -56,7 +109,8 @@ NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_si
        req->out.size           = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size;
 
        req->out.allocated      = req->out.size + body_dynamic_size;
-       req->out.buffer         = talloc_size(req, req->out.allocated);
+       req->out.buffer         = talloc_array(req, uint8_t, 
+                                              req->out.allocated);
        NT_STATUS_HAVE_NO_MEMORY(req->out.buffer);
 
        req->out.hdr            = req->out.buffer       + NBT_HDR_SIZE;
@@ -65,19 +119,21 @@ NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_si
        req->out.body_size      = body_fixed_size;
        req->out.dynamic        = (body_dynamic_size ? req->out.body + body_fixed_size : NULL);
 
-       SIVAL(req->out.hdr, 0,                SMB2_MAGIC);
-       SSVAL(req->out.hdr, SMB2_HDR_LENGTH,  SMB2_HDR_BODY);
-       SSVAL(req->out.hdr, SMB2_HDR_PAD1,    0);
-       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_UNKNOWN1,0x0001);
-       SIVAL(req->out.hdr, SMB2_HDR_FLAGS,   0x00000001);
-       SIVAL(req->out.hdr, SMB2_HDR_UNKNOWN2,0);
-       SBVAL(req->out.hdr, SMB2_HDR_SEQNUM,  req->seqnum);
-       SIVAL(req->out.hdr, SMB2_HDR_PID,     IVAL(req->in.hdr, SMB2_HDR_PID));
-       SIVAL(req->out.hdr, SMB2_HDR_TID,     IVAL(req->in.hdr, SMB2_HDR_TID));
-       SBVAL(req->out.hdr, SMB2_HDR_UID,     BVAL(req->in.hdr, SMB2_HDR_UID));
-       memset(req->out.hdr+SMB2_HDR_SIG, 0, 16);
+       SIVAL(req->out.hdr, 0,                          SMB2_MAGIC);
+       SSVAL(req->out.hdr, SMB2_HDR_LENGTH,            SMB2_HDR_BODY);
+       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,            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));
+       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));
@@ -94,6 +150,114 @@ NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_si
        return NT_STATUS_OK;
 }
 
+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 < (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, 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(smb_conn, "NON-SMB2 chained packet");
+               return;
+       }
+
+       req = smb2srv_init_request(smb_conn);
+       if (!req) {
+               smbsrv_terminate_connection(smb_conn, "SMB2 chained packet - no memory");
+               return;
+       }
+
+       talloc_steal(req, p_req);
+
+       req->in.buffer          = talloc_steal(req, p_req->in.buffer);
+       req->in.size            = p_req->in.size;
+       req->request_time       = p_req->request_time;
+       req->in.allocated       = req->in.size;
+
+       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  - (last_hdr_offset+ chain_offset + SMB2_HDR_BODY);
+       req->in.dynamic         = NULL;
+
+       req->seqnum             = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
+
+       if (req->in.body_size < 2) {
+               /* error handling for this is different for negprot to 
+                  other packet types */
+               uint16_t opcode = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
+               if (opcode == SMB2_OP_NEGPROT) {
+                       smbsrv_terminate_connection(smb_conn, "Bad body size in SMB2 negprot");
+                       return;
+               } else {
+                       smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+                       return;
+               }
+       }
+
+       buffer_code             = SVAL(req->in.body, 0);
+       req->in.body_fixed      = (buffer_code & ~1);
+       dynamic_size            = req->in.body_size - req->in.body_fixed;
+
+       if (dynamic_size != 0 && (buffer_code & 1)) {
+               req->in.dynamic = req->in.body + req->in.body_fixed;
+               if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
+                       DEBUG(1,("SMB2 chained request invalid dynamic size 0x%x\n", 
+                                dynamic_size));
+                       smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+                       return;
+               }
+       }
+
+       smb2srv_setup_bufinfo(req);
+
+       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;
+       }
+
+       /* 
+        * TODO: - make sure the length field is 64
+        *       - make sure it's a request
+        */
+
+       status = smb2srv_reply(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               smbsrv_terminate_connection(smb_conn, nt_errstr(status));
+               return;
+       }
+}
+
 void smb2srv_send_reply(struct smb2srv_request *req)
 {
        DATA_BLOB blob;
@@ -106,13 +270,29 @@ 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 */
+       if (req->is_signed) {
+               status = smb2_sign_message(&req->out, 
+                                          req->session->session_info->session_key);
+               if (!NT_STATUS_IS_OK(status)) {
+                       smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+                       return;
+               }               
+       }
+
+
        blob = data_blob_const(req->out.buffer, req->out.size);
        status = packet_send(req->smb_conn->packet, blob);
        if (!NT_STATUS_IS_OK(status)) {
                smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+               return;
+       }
+       if (req->chain_offset) {
+               smb2srv_chain_reply(req);
+               return;
        }
        talloc_free(req);
 }
@@ -127,7 +307,7 @@ void smb2srv_send_error(struct smb2srv_request *req, NTSTATUS error)
                return;
        }
 
-       status = smb2srv_setup_reply(req, 8, True, 0);
+       status = smb2srv_setup_reply(req, 8, true, 0);
        if (!NT_STATUS_IS_OK(status)) {
                smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
                talloc_free(req);
@@ -139,6 +319,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);
 }
 
@@ -147,18 +329,67 @@ static NTSTATUS smb2srv_reply(struct smb2srv_request *req)
        uint16_t opcode;
        uint32_t tid;
        uint64_t uid;
+       uint32_t flags;
+
+       if (SVAL(req->in.hdr, SMB2_HDR_LENGTH) != SMB2_HDR_BODY) {
+               smbsrv_terminate_connection(req->smb_conn, "Invalid SMB2 header length");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       opcode                  = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
+       req->chain_offset       = IVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND);
+       req->seqnum             = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
+       tid                     = IVAL(req->in.hdr, SMB2_HDR_TID);
+       uid                     = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
+       flags                   = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+
+       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;
+       }
+       if (opcode != SMB2_OP_CANCEL) {
+               req->smb_conn->highest_smb2_seqnum = req->seqnum;
+       }
 
-       opcode          = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
-       req->seqnum     = BVAL(req->in.hdr, SMB2_HDR_SEQNUM);
-       tid             = IVAL(req->in.hdr, SMB2_HDR_TID);
-       uid             = BVAL(req->in.hdr, SMB2_HDR_UID);
+       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;
 
-       /* TODO: check the seqnum */
+       /* supporting signing is mandatory in SMB2, and is per-packet. So we 
+          should check the signature on any incoming packet that is signed, and 
+          should give a signed reply to any signed request */
+       if (flags & SMB2_HDR_FLAG_SIGNED) {
+               NTSTATUS status;
+
+               if (!req->session) goto nosession;
+
+               req->is_signed = true;
+               status = smb2_check_signature(&req->in, 
+                                             req->session->session_info->session_key);
+               if (!NT_STATUS_IS_OK(status)) {
+                       smb2srv_send_error(req, status);
+                       return NT_STATUS_OK;                    
+               }
+       } else if (req->session && req->session->smb2_signing.active) {
+               /* we require signing and this request was not signed */
+               smb2srv_send_error(req, NT_STATUS_ACCESS_DENIED);
+               return NT_STATUS_OK;                                    
+       }
+
+       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:
@@ -216,14 +447,12 @@ static NTSTATUS smb2srv_reply(struct smb2srv_request *req)
                smb2srv_ioctl_recv(req);
                return NT_STATUS_OK;
        case SMB2_OP_CANCEL:
-               if (!req->session) goto nosession;
-               if (!req->tcon) goto notcon;
                smb2srv_cancel_recv(req);
                return NT_STATUS_OK;
        case SMB2_OP_KEEPALIVE:
                smb2srv_keepalive_recv(req);
                return NT_STATUS_OK;
-       case SMB2_OP_FIND:
+       case SMB2_OP_QUERY_DIRECTORY:
                if (!req->session) goto nosession;
                if (!req->tcon) goto notcon;
                smb2srv_find_recv(req);
@@ -262,14 +491,15 @@ notcon:
        return NT_STATUS_OK;
 }
 
-NTSTATUS smbsrv_recv_smb2_request(void *private, DATA_BLOB blob)
+NTSTATUS smbsrv_recv_smb2_request(void *private_data, DATA_BLOB blob)
 {
-       struct smbsrv_connection *smb_conn = talloc_get_type(private, struct smbsrv_connection);
+       struct smbsrv_connection *smb_conn = talloc_get_type(private_data, struct smbsrv_connection);
        struct smb2srv_request *req;
        struct timeval cur_time = timeval_current();
        uint32_t protocol_version;
        uint16_t buffer_code;
        uint32_t dynamic_size;
+       uint32_t flags;
 
        smb_conn->statistics.last_request_time = cur_time;
 
@@ -280,16 +510,16 @@ NTSTATUS smbsrv_recv_smb2_request(void *private, DATA_BLOB blob)
                return NT_STATUS_OK;
        }
 
-       if (blob.length < (NBT_HDR_SIZE + SMB2_MIN_SIZE)) {
+       if (blob.length < (NBT_HDR_SIZE + SMB2_MIN_SIZE_NO_BODY)) {
                DEBUG(2,("Invalid SMB2 packet length count %ld\n", (long)blob.length));
                smbsrv_terminate_connection(smb_conn, "Invalid SMB2 packet");
                return NT_STATUS_OK;
        }
 
        protocol_version = IVAL(blob.data, NBT_HDR_SIZE);
-
        if (protocol_version != SMB2_MAGIC) {
-               DEBUG(2,("Invalid SMB packet: protocl prefix: 0x%08X\n", protocol_version));
+               DEBUG(2,("Invalid SMB packet: protocol prefix: 0x%08X\n",
+                        protocol_version));
                smbsrv_terminate_connection(smb_conn, "NON-SMB2 packet");
                return NT_STATUS_OK;
        }
@@ -307,6 +537,21 @@ NTSTATUS smbsrv_recv_smb2_request(void *private, DATA_BLOB blob)
        req->in.body_size       = req->in.size  - (SMB2_HDR_BODY+NBT_HDR_SIZE);
        req->in.dynamic         = NULL;
 
+       req->seqnum             = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
+
+       if (req->in.body_size < 2) {
+               /* error handling for this is different for negprot to 
+                  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");                    
+                       return NT_STATUS_OK;
+               } else {
+                       smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER);
+                       return NT_STATUS_OK;
+               }
+       }
+
        buffer_code             = SVAL(req->in.body, 0);
        req->in.body_fixed      = (buffer_code & ~1);
        dynamic_size            = req->in.body_size - req->in.body_fixed;
@@ -321,14 +566,121 @@ NTSTATUS smbsrv_recv_smb2_request(void *private, DATA_BLOB blob)
                }
        }
 
+       smb2srv_setup_bufinfo(req);
+
        /* 
         * TODO: - make sure the length field is 64
         *       - 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);
 }
 
+static NTSTATUS smb2srv_init_pending(struct smbsrv_connection *smb_conn)
+{
+       smb_conn->requests2.idtree_req = idr_init(smb_conn);
+       NT_STATUS_HAVE_NO_MEMORY(smb_conn->requests2.idtree_req);
+       smb_conn->requests2.idtree_limit        = 0x00FFFFFF & (UINT32_MAX - 1);
+       smb_conn->requests2.list                = NULL;
+
+       return NT_STATUS_OK;
+}
+
+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) {
+               return NT_STATUS_INSUFFICIENT_RESOURCES;
+       }
+
+       DLIST_ADD_END(req->smb_conn->requests2.list, req);
+       req->pending_id = id;
+
+       talloc_set_destructor(req, smb2srv_request_deny_destructor);
+
+       status = smb2srv_setup_reply(req, 8, true, 0);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       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);
+
+       /* if the real reply will be signed set the signed flags, but don't sign */
+       if (req->is_signed) {
+               SIVAL(req->out.hdr, SMB2_HDR_FLAGS, IVAL(req->out.hdr, SMB2_HDR_FLAGS) | SMB2_HDR_FLAG_SIGNED);
+               signing_used = req->is_signed;
+               req->is_signed = false;
+       }
+
+       smb2srv_send_reply(req);
+
+       req->is_signed = signing_used;
+
+       talloc_set_destructor(req, smb2srv_request_destructor);
+       return NT_STATUS_OK;
+}
+
+void smb2srv_cancel_recv(struct smb2srv_request *req)
+{
+       uint32_t pending_id;
+       uint32_t flags;
+       void *p;
+       struct smb2srv_request *r;
+
+       if (!req->session) goto done;
+
+       flags           = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
+       pending_id      = IVAL(req->in.hdr, SMB2_HDR_PID);
+
+       if (!(flags & SMB2_HDR_FLAG_ASYNC)) {
+               /* TODO: what to do here? */
+               goto done;
+       }
+       p = idr_find(req->smb_conn->requests2.idtree_req, pending_id);
+       if (!p) goto done;
+
+       r = talloc_get_type(p, struct smb2srv_request);
+       if (!r) goto done;
+
+       if (!r->ntvfs) goto done;
+
+       ntvfs_cancel(r->ntvfs);
+
+done:
+       /* we never generate a reply for a SMB2 Cancel */
+       talloc_free(req);
+}
+
 /*
  * init the SMB2 protocol related stuff
  */
@@ -341,16 +693,18 @@ 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->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;
+       smb_conn->config.nt_status_support = true;
 
        status = smbsrv_init_sessions(smb_conn, UINT64_MAX);
        NT_STATUS_NOT_OK_RETURN(status);
 
+       status = smb2srv_init_pending(smb_conn);
+       NT_STATUS_NOT_OK_RETURN(status);
+
        return NT_STATUS_OK;
        
 }