Add extra fields into struct smbd_smb2_request_read_state to support receivefile.
[obnox/samba/samba-obnox.git] / source3 / smbd / smb2_server.c
index 4e3259af944a40d44b3009d35bb5e35c61efaf91..e1671a7473e4b7cabc8b65fd67122ff7fa45a2c6 100644 (file)
@@ -38,6 +38,8 @@ static const struct smbd_smb2_dispatch_table {
        bool need_session;
        bool need_tcon;
        bool as_root;
+       uint16_t fileid_ofs;
+       bool allow_invalid_fileid;
 } smbd_smb2_table[] = {
 #define _OP(o) .opcode = o, .name = #o
        {
@@ -74,26 +76,33 @@ static const struct smbd_smb2_dispatch_table {
                _OP(SMB2_OP_CLOSE),
                .need_session = true,
                .need_tcon = true,
+               .fileid_ofs = 0x08,
        },{
                _OP(SMB2_OP_FLUSH),
                .need_session = true,
                .need_tcon = true,
+               .fileid_ofs = 0x08,
        },{
                _OP(SMB2_OP_READ),
                .need_session = true,
                .need_tcon = true,
+               .fileid_ofs = 0x10,
        },{
                _OP(SMB2_OP_WRITE),
                .need_session = true,
                .need_tcon = true,
+               .fileid_ofs = 0x10,
        },{
                _OP(SMB2_OP_LOCK),
                .need_session = true,
                .need_tcon = true,
+               .fileid_ofs = 0x08,
        },{
                _OP(SMB2_OP_IOCTL),
                .need_session = true,
                .need_tcon = true,
+               .fileid_ofs = 0x08,
+               .allow_invalid_fileid = true,
        },{
                _OP(SMB2_OP_CANCEL),
                .as_root = true,
@@ -104,22 +113,32 @@ static const struct smbd_smb2_dispatch_table {
                _OP(SMB2_OP_FIND),
                .need_session = true,
                .need_tcon = true,
+               .fileid_ofs = 0x08,
        },{
                _OP(SMB2_OP_NOTIFY),
                .need_session = true,
                .need_tcon = true,
+               .fileid_ofs = 0x08,
        },{
                _OP(SMB2_OP_GETINFO),
                .need_session = true,
                .need_tcon = true,
+               .fileid_ofs = 0x18,
        },{
                _OP(SMB2_OP_SETINFO),
                .need_session = true,
                .need_tcon = true,
+               .fileid_ofs = 0x10,
        },{
                _OP(SMB2_OP_BREAK),
                .need_session = true,
                .need_tcon = true,
+               /*
+                * we do not set
+                * .fileid_ofs here
+                * as LEASE breaks does not
+                * have a file id
+                */
        }
 };
 
@@ -146,7 +165,7 @@ static const struct smbd_smb2_dispatch_table *smbd_smb2_call(uint16_t opcode)
        return ret;
 }
 
-static void print_req_vectors(struct smbd_smb2_request *req)
+static void print_req_vectors(const struct smbd_smb2_request *req)
 {
        int i;
 
@@ -235,6 +254,13 @@ static void smb2_setup_nbt_length(struct iovec *vector, int count)
        _smb2_setlen(vector[0].iov_base, len);
 }
 
+static int smbd_smb2_request_destructor(struct smbd_smb2_request *req)
+{
+       data_blob_clear_free(&req->first_key);
+       data_blob_clear_free(&req->last_key);
+       return 0;
+}
+
 static struct smbd_smb2_request *smbd_smb2_request_allocate(TALLOC_CTX *mem_ctx)
 {
        TALLOC_CTX *mem_pool;
@@ -261,6 +287,8 @@ static struct smbd_smb2_request *smbd_smb2_request_allocate(TALLOC_CTX *mem_ctx)
        req->last_session_id = UINT64_MAX;
        req->last_tid = UINT32_MAX;
 
+       talloc_set_destructor(req, smbd_smb2_request_destructor);
+
        return req;
 }
 
@@ -276,6 +304,9 @@ static NTSTATUS smbd_smb2_inbuf_parse_compound(struct smbXsrv_connection *conn,
        int num_iov = 1;
        size_t taken = 0;
        uint8_t *first_hdr = buf;
+       size_t verified_buflen = 0;
+       uint8_t *tf = NULL;
+       size_t tf_len = 0;
 
        /*
         * Note: index '0' is reserved for the transport protocol
@@ -292,8 +323,92 @@ static NTSTATUS smbd_smb2_inbuf_parse_compound(struct smbXsrv_connection *conn,
                size_t full_size;
                size_t next_command_ofs;
                uint16_t body_size;
+               uint8_t *body = NULL;
+               uint32_t dyn_size;
+               uint8_t *dyn = NULL;
                struct iovec *iov_tmp;
 
+               if (verified_buflen > taken) {
+                       len = verified_buflen - taken;
+               } else {
+                       tf = NULL;
+                       tf_len = 0;
+               }
+
+               if (len < 4) {
+                       DEBUG(10, ("%d bytes left, expected at least %d\n",
+                                  (int)len, 4));
+                       goto inval;
+               }
+               if (IVAL(hdr, 0) == SMB2_TF_MAGIC) {
+                       struct smbXsrv_session *s = NULL;
+                       uint64_t uid;
+                       struct iovec tf_iov[2];
+                       NTSTATUS status;
+                       size_t enc_len;
+
+                       if (conn->protocol < PROTOCOL_SMB2_24) {
+                               DEBUG(10, ("Got SMB2_TRANSFORM header, "
+                                          "but dialect[0x%04X] is used\n",
+                                          conn->smb2.server.dialect));
+                               goto inval;
+                       }
+
+                       if (!(conn->smb2.server.capabilities & SMB2_CAP_ENCRYPTION)) {
+                               DEBUG(10, ("Got SMB2_TRANSFORM header, "
+                                          "but not negotiated "
+                                          "client[0x%08X] server[0x%08X]\n",
+                                          conn->smb2.client.capabilities,
+                                          conn->smb2.server.capabilities));
+                               goto inval;
+                       }
+
+                       if (len < SMB2_TF_HDR_SIZE) {
+                               DEBUG(1, ("%d bytes left, expected at least %d\n",
+                                          (int)len, SMB2_TF_HDR_SIZE));
+                               goto inval;
+                       }
+                       tf = hdr;
+                       tf_len = SMB2_TF_HDR_SIZE;
+                       taken += tf_len;
+
+                       hdr = first_hdr + taken;
+                       enc_len = IVAL(tf, SMB2_TF_MSG_SIZE);
+                       uid = BVAL(tf, SMB2_TF_SESSION_ID);
+
+                       if (len < SMB2_TF_HDR_SIZE + enc_len) {
+                               DEBUG(1, ("%d bytes left, expected at least %d\n",
+                                          (int)len,
+                                          (int)(SMB2_TF_HDR_SIZE + enc_len)));
+                               goto inval;
+                       }
+
+                       status = smb2srv_session_lookup(conn, uid, now, &s);
+                       if (s == NULL) {
+                               DEBUG(1, ("invalid session[%llu] in "
+                                         "SMB2_TRANSFORM header\n",
+                                          (unsigned long long)uid));
+                               TALLOC_FREE(iov);
+                               return NT_STATUS_USER_SESSION_DELETED;
+                       }
+
+                       tf_iov[0].iov_base = (void *)tf;
+                       tf_iov[0].iov_len = tf_len;
+                       tf_iov[1].iov_base = (void *)hdr;
+                       tf_iov[1].iov_len = enc_len;
+
+                       status = smb2_signing_decrypt_pdu(s->global->decryption_key,
+                                                         conn->protocol,
+                                                         tf_iov, 2);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               TALLOC_FREE(iov);
+                               return status;
+                       }
+
+                       verified_buflen = taken + enc_len;
+                       len = enc_len;
+               }
+
                /*
                 * We need the header plus the body length field
                 */
@@ -338,23 +453,28 @@ static NTSTATUS smbd_smb2_inbuf_parse_compound(struct smbXsrv_connection *conn,
                         */
                        body_size = full_size - SMB2_HDR_BODY;
                }
+               body = hdr + SMB2_HDR_BODY;
+               dyn = body + body_size;
+               dyn_size = full_size - (SMB2_HDR_BODY + body_size);
 
                iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
-                                        num_iov + 3);
+                                        num_iov + SMBD_SMB2_NUM_IOV_PER_REQ);
                if (iov_tmp == NULL) {
                        TALLOC_FREE(iov);
                        return NT_STATUS_NO_MEMORY;
                }
                iov = iov_tmp;
                cur = &iov[num_iov];
-               num_iov += 3;
+               num_iov += SMBD_SMB2_NUM_IOV_PER_REQ;
 
-               cur[0].iov_base = hdr;
-               cur[0].iov_len  = SMB2_HDR_BODY;
-               cur[1].iov_base = hdr + SMB2_HDR_BODY;
-               cur[1].iov_len  = body_size;
-               cur[2].iov_base = hdr + SMB2_HDR_BODY + body_size;
-               cur[2].iov_len  = full_size - (SMB2_HDR_BODY + body_size);
+               cur[SMBD_SMB2_TF_IOV_OFS].iov_base   = tf;
+               cur[SMBD_SMB2_TF_IOV_OFS].iov_len    = tf_len;
+               cur[SMBD_SMB2_HDR_IOV_OFS].iov_base  = hdr;
+               cur[SMBD_SMB2_HDR_IOV_OFS].iov_len   = SMB2_HDR_BODY;
+               cur[SMBD_SMB2_BODY_IOV_OFS].iov_base = body;
+               cur[SMBD_SMB2_BODY_IOV_OFS].iov_len  = body_size;
+               cur[SMBD_SMB2_DYN_IOV_OFS].iov_base  = dyn;
+               cur[SMBD_SMB2_DYN_IOV_OFS].iov_len   = dyn_size;
 
                taken += full_size;
        }
@@ -512,7 +632,7 @@ static bool smb2_validate_message_id(struct smbd_server_connection *sconn,
                                const uint8_t *inhdr)
 {
        uint64_t message_id = BVAL(inhdr, SMB2_HDR_MESSAGE_ID);
-       uint16_t opcode = IVAL(inhdr, SMB2_HDR_OPCODE);
+       uint16_t opcode = SVAL(inhdr, SMB2_HDR_OPCODE);
        uint16_t credit_charge = 1;
        uint64_t i;
 
@@ -584,24 +704,25 @@ static NTSTATUS smbd_smb2_request_validate(struct smbd_smb2_request *req)
 
        count = req->in.vector_count;
 
-       if (count < 4) {
+       if (count < 1 + SMBD_SMB2_NUM_IOV_PER_REQ) {
                /* It's not a SMB2 request */
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       for (idx=1; idx < count; idx += 3) {
+       for (idx=1; idx < count; idx += SMBD_SMB2_NUM_IOV_PER_REQ) {
+               struct iovec *hdr = SMBD_SMB2_IDX_HDR_IOV(req,in,idx);
+               struct iovec *body = SMBD_SMB2_IDX_BODY_IOV(req,in,idx);
                const uint8_t *inhdr = NULL;
-               uint32_t flags;
 
-               if (req->in.vector[idx].iov_len != SMB2_HDR_BODY) {
+               if (hdr->iov_len != SMB2_HDR_BODY) {
                        return NT_STATUS_INVALID_PARAMETER;
                }
 
-               if (req->in.vector[idx+1].iov_len < 2) {
+               if (body->iov_len < 2) {
                        return NT_STATUS_INVALID_PARAMETER;
                }
 
-               inhdr = (const uint8_t *)req->in.vector[idx].iov_base;
+               inhdr = (const uint8_t *)hdr->iov_base;
 
                /* Check the SMB2 header */
                if (IVAL(inhdr, SMB2_HDR_PROTOCOL_ID) != SMB2_MAGIC) {
@@ -611,50 +732,6 @@ static NTSTATUS smbd_smb2_request_validate(struct smbd_smb2_request *req)
                if (!smb2_validate_message_id(req->sconn, inhdr)) {
                        return NT_STATUS_INVALID_PARAMETER;
                }
-
-               flags = IVAL(inhdr, SMB2_HDR_FLAGS);
-               if (idx == 1) {
-                       /*
-                        * the 1st request should never have the
-                        * SMB2_HDR_FLAG_CHAINED flag set
-                        */
-                       if (flags & SMB2_HDR_FLAG_CHAINED) {
-                               req->next_status = NT_STATUS_INVALID_PARAMETER;
-                               return NT_STATUS_OK;
-                       }
-               } else if (idx == 4) {
-                       /*
-                        * the 2nd request triggers related vs. unrelated
-                        * compounded requests
-                        */
-                       if (flags & SMB2_HDR_FLAG_CHAINED) {
-                               req->compound_related = true;
-                       }
-               } else if (idx > 4) {
-#if 0
-                       /*
-                        * It seems the this tests are wrong
-                        * see the SMB2-COMPOUND test
-                        */
-
-                       /*
-                        * all other requests should match the 2nd one
-                        */
-                       if (flags & SMB2_HDR_FLAG_CHAINED) {
-                               if (!req->compound_related) {
-                                       req->next_status =
-                                               NT_STATUS_INVALID_PARAMETER;
-                                       return NT_STATUS_OK;
-                               }
-                       } else {
-                               if (req->compound_related) {
-                                       req->next_status =
-                                               NT_STATUS_INVALID_PARAMETER;
-                                       return NT_STATUS_OK;
-                               }
-                       }
-#endif
-               }
        }
 
        return NT_STATUS_OK;
@@ -680,9 +757,9 @@ static void smb2_set_operation_credit(struct smbd_server_connection *sconn,
         *
         * Windows also starts with the 1/16th and then grants
         * more later. I was only able to trigger higher
-        * values, when using a verify high credit charge.
+        * values, when using a very high credit charge.
         *
-        * TODO: scale up depending one load, free memory
+        * TODO: scale up depending on load, free memory
         *       or other stuff.
         *       Maybe also on the relationship between number
         *       of requests and the used sequence number.
@@ -703,7 +780,12 @@ static void smb2_set_operation_credit(struct smbd_server_connection *sconn,
        out_status = NT_STATUS(IVAL(outhdr, SMB2_HDR_STATUS));
 
        SMB_ASSERT(sconn->smb2.max_credits >= sconn->smb2.credits_granted);
-       SMB_ASSERT(sconn->smb2.max_credits >= credit_charge);
+
+       if (sconn->smb2.max_credits < credit_charge) {
+               smbd_server_connection_terminate(sconn,
+                       "client error: credit charge > max credits\n");
+               return;
+       }
 
        if (out_flags & SMB2_HDR_FLAG_ASYNC) {
                /*
@@ -799,16 +881,18 @@ static void smb2_calculate_credits(const struct smbd_smb2_request *inreq,
 
        count = outreq->out.vector_count;
 
-       for (idx=1; idx < count; idx += 3) {
-               uint8_t *outhdr = (uint8_t *)outreq->out.vector[idx].iov_base;
-               smb2_set_operation_credit(outreq->sconn,
-                       &inreq->in.vector[idx],
-                       &outreq->out.vector[idx]);
+       for (idx=1; idx < count; idx += SMBD_SMB2_NUM_IOV_PER_REQ) {
+               struct iovec *inhdr_v = SMBD_SMB2_IDX_HDR_IOV(inreq,in,idx);
+               struct iovec *outhdr_v = SMBD_SMB2_IDX_HDR_IOV(outreq,out,idx);
+               uint8_t *outhdr = (uint8_t *)outhdr_v->iov_base;
+
+               smb2_set_operation_credit(outreq->sconn, inhdr_v, outhdr_v);
+
                /* To match Windows, count up what we
                   just granted. */
                total_credits += SVAL(outhdr, SMB2_HDR_CREDIT);
                /* Set to zero in all but the last reply. */
-               if (idx + 3 < count) {
+               if (idx + SMBD_SMB2_NUM_IOV_PER_REQ < count) {
                        SSVAL(outhdr, SMB2_HDR_CREDIT, 0);
                } else {
                        SSVAL(outhdr, SMB2_HDR_CREDIT, total_credits);
@@ -832,21 +916,20 @@ static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req)
        vector[0].iov_len       = 4;
        SIVAL(req->out.nbt_hdr, 0, 0);
 
-       for (idx=1; idx < count; idx += 3) {
-               const uint8_t *inhdr = NULL;
+       for (idx=1; idx < count; idx += SMBD_SMB2_NUM_IOV_PER_REQ) {
+               struct iovec *inhdr_v = SMBD_SMB2_IDX_HDR_IOV(req,in,idx);
+               const uint8_t *inhdr = (const uint8_t *)inhdr_v->iov_base;
                uint8_t *outhdr = NULL;
                uint8_t *outbody = NULL;
                uint32_t next_command_ofs = 0;
                struct iovec *current = &vector[idx];
 
-               if ((idx + 3) < count) {
+               if ((idx + SMBD_SMB2_NUM_IOV_PER_REQ) < count) {
                        /* we have a next command -
                         * setup for the error case. */
                        next_command_ofs = SMB2_HDR_BODY + 9;
                }
 
-               inhdr = (const uint8_t *)req->in.vector[idx].iov_base;
-
                outhdr = talloc_zero_array(vector, uint8_t,
                                      OUTVEC_ALLOC_SIZE);
                if (outhdr == NULL) {
@@ -855,14 +938,20 @@ static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req)
 
                outbody = outhdr + SMB2_HDR_BODY;
 
-               current[0].iov_base     = (void *)outhdr;
-               current[0].iov_len      = SMB2_HDR_BODY;
+               /*
+                * SMBD_SMB2_TF_IOV_OFS might be used later
+                */
+               current[SMBD_SMB2_TF_IOV_OFS].iov_base   = NULL;
+               current[SMBD_SMB2_TF_IOV_OFS].iov_len    = 0;
+
+               current[SMBD_SMB2_HDR_IOV_OFS].iov_base  = (void *)outhdr;
+               current[SMBD_SMB2_HDR_IOV_OFS].iov_len   = SMB2_HDR_BODY;
 
-               current[1].iov_base     = (void *)outbody;
-               current[1].iov_len      = 8;
+               current[SMBD_SMB2_BODY_IOV_OFS].iov_base = (void *)outbody;
+               current[SMBD_SMB2_BODY_IOV_OFS].iov_len  = 8;
 
-               current[2].iov_base     = NULL;
-               current[2].iov_len      = 0;
+               current[SMBD_SMB2_DYN_IOV_OFS].iov_base  = NULL;
+               current[SMBD_SMB2_DYN_IOV_OFS].iov_len   = 0;
 
                /* setup the SMB2 header */
                SIVAL(outhdr, SMB2_HDR_PROTOCOL_ID,     SMB2_MAGIC);
@@ -913,72 +1002,102 @@ void smbd_server_connection_terminate_ex(struct smbd_server_connection *sconn,
        exit_server_cleanly(reason);
 }
 
-static bool dup_smb2_vec3(TALLOC_CTX *ctx,
+static bool dup_smb2_vec4(TALLOC_CTX *ctx,
                        struct iovec *outvec,
                        const struct iovec *srcvec)
 {
-       /* vec[0] is always boilerplate and must
+       const uint8_t *srctf;
+       size_t srctf_len;
+       const uint8_t *srchdr;
+       size_t srchdr_len;
+       const uint8_t *srcbody;
+       size_t srcbody_len;
+       const uint8_t *expected_srcbody;
+       const uint8_t *srcdyn;
+       size_t srcdyn_len;
+       const uint8_t *expected_srcdyn;
+       uint8_t *dsttf;
+       uint8_t *dsthdr;
+       uint8_t *dstbody;
+       uint8_t *dstdyn;
+
+       srctf  = (const uint8_t *)srcvec[SMBD_SMB2_TF_IOV_OFS].iov_base;
+       srctf_len = srcvec[SMBD_SMB2_TF_IOV_OFS].iov_len;
+       srchdr  = (const uint8_t *)srcvec[SMBD_SMB2_HDR_IOV_OFS].iov_base;
+       srchdr_len = srcvec[SMBD_SMB2_HDR_IOV_OFS].iov_len;
+       srcbody = (const uint8_t *)srcvec[SMBD_SMB2_BODY_IOV_OFS].iov_base;
+       srcbody_len = srcvec[SMBD_SMB2_BODY_IOV_OFS].iov_len;
+       expected_srcbody = srchdr + SMB2_HDR_BODY;
+       srcdyn  = (const uint8_t *)srcvec[SMBD_SMB2_DYN_IOV_OFS].iov_base;
+       srcdyn_len = srcvec[SMBD_SMB2_DYN_IOV_OFS].iov_len;
+       expected_srcdyn = srcbody + 8;
+
+       if ((srctf_len != SMB2_TF_HDR_SIZE) && (srctf_len != 0)) {
+               return false;
+       }
+
+       if (srchdr_len != SMB2_HDR_BODY) {
+               return false;
+       }
+
+       if (srctf_len == SMB2_TF_HDR_SIZE) {
+               dsttf = talloc_memdup(ctx, srctf, SMB2_TF_HDR_SIZE);
+               if (dsttf == NULL) {
+                       return false;
+               }
+       } else {
+               dsttf = NULL;
+       }
+       outvec[SMBD_SMB2_TF_IOV_OFS].iov_base = (void *)dsttf;
+       outvec[SMBD_SMB2_TF_IOV_OFS].iov_len = srctf_len;
+
+       /* vec[SMBD_SMB2_HDR_IOV_OFS] is always boilerplate and must
         * be allocated with size OUTVEC_ALLOC_SIZE. */
 
-       outvec[0].iov_base = talloc_memdup(ctx,
-                               srcvec[0].iov_base,
-                               OUTVEC_ALLOC_SIZE);
-       if (!outvec[0].iov_base) {
+       dsthdr = talloc_memdup(ctx, srchdr, OUTVEC_ALLOC_SIZE);
+       if (dsthdr == NULL) {
                return false;
        }
-       outvec[0].iov_len = SMB2_HDR_BODY;
+       outvec[SMBD_SMB2_HDR_IOV_OFS].iov_base = (void *)dsthdr;
+       outvec[SMBD_SMB2_HDR_IOV_OFS].iov_len = SMB2_HDR_BODY;
 
        /*
-        * If this is a "standard" vec[1] of length 8,
-        * pointing to srcvec[0].iov_base + SMB2_HDR_BODY,
+        * If this is a "standard" vec[SMBD_SMB2_BOFY_IOV_OFS] of length 8,
+        * pointing to srcvec[SMBD_SMB2_HDR_IOV_OFS].iov_base + SMB2_HDR_BODY,
         * then duplicate this. Else use talloc_memdup().
         */
 
-       if (srcvec[1].iov_len == 8 &&
-                       srcvec[1].iov_base ==
-                               ((uint8_t *)srcvec[0].iov_base) +
-                                       SMB2_HDR_BODY) {
-               outvec[1].iov_base = ((uint8_t *)outvec[0].iov_base) +
-                                       SMB2_HDR_BODY;
-               outvec[1].iov_len = 8;
+       if ((srcbody == expected_srcbody) && (srcbody_len == 8)) {
+               dstbody = dsthdr + SMB2_HDR_BODY;
        } else {
-               outvec[1].iov_base = talloc_memdup(ctx,
-                               srcvec[1].iov_base,
-                               srcvec[1].iov_len);
-               if (!outvec[1].iov_base) {
+               dstbody = talloc_memdup(ctx, srcbody, srcbody_len);
+               if (dstbody == NULL) {
                        return false;
                }
-               outvec[1].iov_len = srcvec[1].iov_len;
        }
+       outvec[SMBD_SMB2_BODY_IOV_OFS].iov_base = (void *)dstbody;
+       outvec[SMBD_SMB2_BODY_IOV_OFS].iov_len = srcbody_len;
 
        /*
-        * If this is a "standard" vec[2] of length 1,
-        * pointing to srcvec[0].iov_base + (OUTVEC_ALLOC_SIZE - 1)
+        * If this is a "standard" vec[SMBD_SMB2_DYN_IOV_OFS] of length 1,
+        * pointing to
+        * srcvec[SMBD_SMB2_HDR_IOV_OFS].iov_base + 8
         * then duplicate this. Else use talloc_memdup().
         */
 
-       if (srcvec[2].iov_base &&
-                       srcvec[2].iov_len) {
-               if (srcvec[2].iov_base ==
-                               ((uint8_t *)srcvec[0].iov_base) +
-                                       (OUTVEC_ALLOC_SIZE - 1) &&
-                               srcvec[2].iov_len == 1) {
-                       /* Common SMB2 error packet case. */
-                       outvec[2].iov_base = ((uint8_t *)outvec[0].iov_base) +
-                               (OUTVEC_ALLOC_SIZE - 1);
-               } else {
-                       outvec[2].iov_base = talloc_memdup(ctx,
-                                       srcvec[2].iov_base,
-                                       srcvec[2].iov_len);
-                       if (!outvec[2].iov_base) {
-                               return false;
-                       }
-               }
-               outvec[2].iov_len = srcvec[2].iov_len;
+       if ((srcdyn == expected_srcdyn) && (srcdyn_len == 1)) {
+               dstdyn = dsthdr + SMB2_HDR_BODY + 8;
+       } else if (srcdyn == NULL) {
+               dstdyn = NULL;
        } else {
-               outvec[2].iov_base = NULL;
-               outvec[2].iov_len = 0;
+               dstdyn = talloc_memdup(ctx, srcdyn, srcdyn_len);
+               if (dstdyn == NULL) {
+                       return false;
+               }
        }
+       outvec[SMBD_SMB2_DYN_IOV_OFS].iov_base = (void *)dstdyn;
+       outvec[SMBD_SMB2_DYN_IOV_OFS].iov_len = srcdyn_len;
+
        return true;
 }
 
@@ -996,6 +1115,7 @@ static struct smbd_smb2_request *dup_smb2_req(const struct smbd_smb2_request *re
 
        newreq->sconn = req->sconn;
        newreq->session = req->session;
+       newreq->do_encryption = req->do_encryption;
        newreq->do_signing = req->do_signing;
        newreq->current_idx = req->current_idx;
 
@@ -1013,8 +1133,8 @@ static struct smbd_smb2_request *dup_smb2_req(const struct smbd_smb2_request *re
        memcpy(newreq->out.nbt_hdr, req->out.nbt_hdr, 4);
 
        /* Setup the vectors identically to the ones in req. */
-       for (i = 1; i < count; i += 3) {
-               if (!dup_smb2_vec3(outvec, &outvec[i], &req->out.vector[i])) {
+       for (i = 1; i < count; i += SMBD_SMB2_NUM_IOV_PER_REQ) {
+               if (!dup_smb2_vec4(outvec, &outvec[i], &req->out.vector[i])) {
                        break;
                }
        }
@@ -1035,9 +1155,13 @@ static void smbd_smb2_request_writev_done(struct tevent_req *subreq);
 
 static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request *req)
 {
-       int i = 0;
+       struct smbXsrv_connection *conn = req->sconn->conn;
+       int first_idx = 1;
+       struct iovec *firsttf = NULL;
+       struct iovec *outhdr_v = NULL;
        uint8_t *outhdr = NULL;
        struct smbd_smb2_request *nreq = NULL;
+       NTSTATUS status;
 
        /* Create a new smb2 request we'll use
           for the interim return. */
@@ -1046,36 +1170,24 @@ static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request
                return NT_STATUS_NO_MEMORY;
        }
 
-       /* Lose the last 3 out vectors. They're the
+       /* Lose the last X out vectors. They're the
           ones we'll be using for the async reply. */
-       nreq->out.vector_count -= 3;
+       nreq->out.vector_count -= SMBD_SMB2_NUM_IOV_PER_REQ;
 
        smb2_setup_nbt_length(nreq->out.vector,
                nreq->out.vector_count);
 
        /* Step back to the previous reply. */
-       i = nreq->current_idx - 3;
-       outhdr = (uint8_t *)nreq->out.vector[i].iov_base;
+       nreq->current_idx -= SMBD_SMB2_NUM_IOV_PER_REQ;
+       firsttf = SMBD_SMB2_IDX_TF_IOV(nreq,out,first_idx);
+       outhdr_v = SMBD_SMB2_OUT_HDR_IOV(nreq);
+       outhdr = SMBD_SMB2_OUT_HDR_PTR(nreq);
        /* And end the chain. */
        SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND, 0);
 
        /* Calculate outgoing credits */
        smb2_calculate_credits(req, nreq);
 
-       /* Re-sign if needed. */
-       if (nreq->do_signing) {
-               NTSTATUS status;
-               struct smbXsrv_session *x = nreq->session;
-               struct smbXsrv_connection *conn = x->connection;
-               DATA_BLOB signing_key = x->global->channels[0].signing_key;
-
-               status = smb2_signing_sign_pdu(signing_key,
-                                              conn->protocol,
-                                              &nreq->out.vector[i], 3);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
-       }
        if (DEBUGLEVEL >= 10) {
                dbgtext("smb2_send_async_interim_response: nreq->current_idx = %u\n",
                        (unsigned int)nreq->current_idx );
@@ -1083,6 +1195,29 @@ static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request
                        (unsigned int)nreq->out.vector_count );
                print_req_vectors(nreq);
        }
+
+       /*
+        * As we have changed the header (SMB2_HDR_NEXT_COMMAND),
+        * we need to sign/encrypt here with the last/first key we remembered
+        */
+       if (firsttf->iov_len == SMB2_TF_HDR_SIZE) {
+               status = smb2_signing_encrypt_pdu(req->first_key,
+                                       conn->protocol,
+                                       firsttf,
+                                       nreq->out.vector_count - first_idx);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       } else if (req->last_key.length > 0) {
+               status = smb2_signing_sign_pdu(req->last_key,
+                                              conn->protocol,
+                                              outhdr_v,
+                                              SMBD_SMB2_NUM_IOV_PER_REQ - 1);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       }
+
        nreq->subreq = tstream_writev_queue_send(nreq,
                                        nreq->sconn->ev_ctx,
                                        nreq->sconn->smb2.stream,
@@ -1103,8 +1238,8 @@ static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request
 
 struct smbd_smb2_request_pending_state {
         struct smbd_server_connection *sconn;
-        uint8_t buf[4 + SMB2_HDR_BODY + 0x08 + 1];
-        struct iovec vector[3];
+        uint8_t buf[NBT_HDR_SIZE + SMB2_TF_HDR_SIZE + SMB2_HDR_BODY + 0x08 + 1];
+        struct iovec vector[1 + SMBD_SMB2_NUM_IOV_PER_REQ];
 };
 
 static void smbd_smb2_request_pending_writev_done(struct tevent_req *subreq)
@@ -1137,7 +1272,7 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req,
                                         uint32_t defer_time)
 {
        NTSTATUS status;
-       int i = req->current_idx;
+       int idx = req->current_idx;
        struct timeval defer_endtime;
        uint8_t *outhdr = NULL;
        uint32_t flags;
@@ -1154,22 +1289,26 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req,
                return NT_STATUS_OK;
        }
 
-       outhdr = (uint8_t *)req->out.vector[i].iov_base;
+       outhdr = SMBD_SMB2_OUT_HDR_PTR(req);
        flags = IVAL(outhdr, SMB2_HDR_FLAGS);
        if (flags & SMB2_HDR_FLAG_ASYNC) {
                /* We're already async. */
                return NT_STATUS_OK;
        }
 
-       if (req->in.vector_count > i + 3) {
+       if (req->in.vector_count > idx + SMBD_SMB2_NUM_IOV_PER_REQ) {
                /*
                 * We're trying to go async in a compound
                 * request chain. This is not allowed.
                 * Cancel the outstanding request.
                 */
-               tevent_req_cancel(req->subreq);
+               bool ok = tevent_req_cancel(req->subreq);
+               if (ok) {
+                       return NT_STATUS_OK;
+               }
+               TALLOC_FREE(req->subreq);
                return smbd_smb2_request_error(req,
-                       NT_STATUS_INSUFFICIENT_RESOURCES);
+                       NT_STATUS_INTERNAL_ERROR);
        }
 
        if (DEBUGLEVEL >= 10) {
@@ -1178,10 +1317,9 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req,
                print_req_vectors(req);
        }
 
-       if (req->out.vector_count > 4) {
-               struct iovec *outvec = NULL;
-
-               /* This is a compound reply. We
+       if (req->out.vector_count >= (2*SMBD_SMB2_NUM_IOV_PER_REQ)) {
+               /*
+                * This is a compound reply. We
                 * must do an interim response
                 * followed by the async response
                 * to match W2K8R2.
@@ -1190,6 +1328,7 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req,
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
+               data_blob_clear_free(&req->first_key);
 
                /*
                 * We're splitting off the last SMB2
@@ -1205,43 +1344,25 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req,
                req->compound_related = false;
                req->sconn->smb2.compound_related_in_progress = false;
 
-               /* Re-arrange the in.vectors. */
-               req->in.vector[1] = req->in.vector[i];
-               req->in.vector[2] = req->in.vector[i+1];
-               req->in.vector[3] = req->in.vector[i+2];
-               req->in.vector_count = 4;
-
-               /* Reset the new in size. */
-               smb2_setup_nbt_length(req->in.vector, 4);
-
-               /* Now recreate the out.vectors. */
-               outvec = talloc_zero_array(req, struct iovec, 4);
-               if (!outvec) {
-                       return NT_STATUS_NO_MEMORY;
-               }
-
-               /* 0 is always boilerplate and must
-                * be of size 4 for the length field. */
-
-               outvec[0].iov_base = req->out.nbt_hdr;
-               outvec[0].iov_len = 4;
-               SIVAL(req->out.nbt_hdr, 0, 0);
-
-               if (!dup_smb2_vec3(outvec, &outvec[1], &req->out.vector[i])) {
-                       return NT_STATUS_NO_MEMORY;
-               }
-
-               TALLOC_FREE(req->out.vector);
-
-               req->out.vector = outvec;
-
                req->current_idx = 1;
-               req->out.vector_count = 4;
 
-               outhdr = (uint8_t *)req->out.vector[1].iov_base;
+               /* Re-arrange the in.vectors. */
+               memmove(&req->in.vector[req->current_idx],
+                       &req->in.vector[idx],
+                       sizeof(req->in.vector[0])*SMBD_SMB2_NUM_IOV_PER_REQ);
+               req->in.vector_count = req->current_idx + SMBD_SMB2_NUM_IOV_PER_REQ;
+
+               /* Re-arrange the out.vectors. */
+               memmove(&req->out.vector[req->current_idx],
+                       &req->out.vector[idx],
+                       sizeof(req->out.vector[0])*SMBD_SMB2_NUM_IOV_PER_REQ);
+               req->out.vector_count = req->current_idx + SMBD_SMB2_NUM_IOV_PER_REQ;
+
+               outhdr = SMBD_SMB2_OUT_HDR_PTR(req);
                flags = (IVAL(outhdr, SMB2_HDR_FLAGS) & ~SMB2_HDR_FLAG_CHAINED);
                SIVAL(outhdr, SMB2_HDR_FLAGS, flags);
        }
+       data_blob_clear_free(&req->last_key);
 
        defer_endtime = timeval_current_ofs_usec(defer_time);
        req->async_te = tevent_add_timer(req->sconn->ev_ctx,
@@ -1266,10 +1387,16 @@ static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
        struct smbd_smb2_request_pending_state *state = NULL;
        uint8_t *outhdr = NULL;
        const uint8_t *inhdr = NULL;
+       uint8_t *tf = NULL;
+       size_t tf_len = 0;
        uint8_t *hdr = NULL;
        uint8_t *body = NULL;
+       uint8_t *dyn = NULL;
        uint32_t flags = 0;
+       uint64_t session_id = 0;
        uint64_t message_id = 0;
+       uint64_t nonce_high = 0;
+       uint64_t nonce_low = 0;
        uint64_t async_id = 0;
        struct tevent_req *subreq = NULL;
 
@@ -1280,6 +1407,7 @@ static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
        outhdr = SMBD_SMB2_OUT_HDR_PTR(req);
        flags = IVAL(outhdr, SMB2_HDR_FLAGS);
        message_id = BVAL(outhdr, SMB2_HDR_MESSAGE_ID);
+       session_id = BVAL(outhdr, SMB2_HDR_SESSION_ID);
 
        async_id = message_id; /* keep it simple for now... */
 
@@ -1288,7 +1416,7 @@ static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
 
        DEBUG(10,("smbd_smb2_request_pending_queue: opcode[%s] mid %llu "
                "going async\n",
-               smb2_opcode_name((uint16_t)IVAL(inhdr, SMB2_HDR_OPCODE)),
+               smb2_opcode_name(SVAL(inhdr, SMB2_HDR_OPCODE)),
                (unsigned long long)async_id ));
 
        /*
@@ -1305,19 +1433,30 @@ static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
        }
        state->sconn = req->sconn;
 
-       state->vector[0].iov_base = (void *)state->buf;
-       state->vector[0].iov_len = 4;
+       tf = state->buf + NBT_HDR_SIZE;
+       tf_len = SMB2_TF_HDR_SIZE;
 
-       state->vector[1].iov_base = state->buf + 4;
-       state->vector[1].iov_len = SMB2_HDR_BODY;
+       hdr = tf + SMB2_TF_HDR_SIZE;
+       body = hdr + SMB2_HDR_BODY;
+       dyn = body + 8;
+
+       if (req->do_encryption) {
+               struct smbXsrv_session *x = req->session;
 
-       state->vector[2].iov_base = state->buf + 4 + SMB2_HDR_BODY;
-       state->vector[2].iov_len = 9;
+               nonce_high = x->nonce_high;
+               nonce_low = x->nonce_low;
 
-       smb2_setup_nbt_length(state->vector, 3);
+               x->nonce_low += 1;
+               if (x->nonce_low == 0) {
+                       x->nonce_low += 1;
+                       x->nonce_high += 1;
+               }
+       }
 
-       hdr = (uint8_t *)state->vector[1].iov_base;
-       body = (uint8_t *)state->vector[2].iov_base;
+       SIVAL(tf, SMB2_TF_PROTOCOL_ID, SMB2_TF_MAGIC);
+       SBVAL(tf, SMB2_TF_NONCE+0, nonce_low);
+       SBVAL(tf, SMB2_TF_NONCE+8, nonce_high);
+       SBVAL(tf, SMB2_TF_SESSION_ID, session_id);
 
        SIVAL(hdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC);
        SSVAL(hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
@@ -1340,26 +1479,75 @@ static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
        SCVAL(body, 0x03, 0);
        SIVAL(body, 0x04, 0);
        /* Match W2K8R2... */
-       SCVAL(body, 0x08, 0x21);
+       SCVAL(dyn,  0x00, 0x21);
+
+       state->vector[0].iov_base = (void *)state->buf;
+       state->vector[0].iov_len = NBT_HDR_SIZE;
+
+       if (req->do_encryption) {
+               state->vector[1+SMBD_SMB2_TF_IOV_OFS].iov_base   = tf;
+               state->vector[1+SMBD_SMB2_TF_IOV_OFS].iov_len    = tf_len;
+       } else {
+               state->vector[1+SMBD_SMB2_TF_IOV_OFS].iov_base   = NULL;
+               state->vector[1+SMBD_SMB2_TF_IOV_OFS].iov_len    = 0;
+       }
+
+       state->vector[1+SMBD_SMB2_HDR_IOV_OFS].iov_base  = hdr;
+       state->vector[1+SMBD_SMB2_HDR_IOV_OFS].iov_len   = SMB2_HDR_BODY;
+
+       state->vector[1+SMBD_SMB2_BODY_IOV_OFS].iov_base = body;
+       state->vector[1+SMBD_SMB2_BODY_IOV_OFS].iov_len  = 8;
+
+       state->vector[1+SMBD_SMB2_DYN_IOV_OFS].iov_base  = dyn;
+       state->vector[1+SMBD_SMB2_DYN_IOV_OFS].iov_len   = 1;
+
+       smb2_setup_nbt_length(state->vector, 1 + SMBD_SMB2_NUM_IOV_PER_REQ);
 
        /* Ensure we correctly go through crediting. Grant
           the credits now, and zero credits on the final
           response. */
        smb2_set_operation_credit(req->sconn,
                        SMBD_SMB2_IN_HDR_IOV(req),
-                       &state->vector[1]);
+                       &state->vector[1+SMBD_SMB2_HDR_IOV_OFS]);
 
        SIVAL(hdr, SMB2_HDR_FLAGS, flags | SMB2_HDR_FLAG_ASYNC);
 
-       if (req->do_signing) {
+       if (DEBUGLVL(10)) {
+               int i;
+
+               for (i = 0; i < ARRAY_SIZE(state->vector); i++) {
+                       dbgtext("\tstate->vector[%u/%u].iov_len = %u\n",
+                               (unsigned int)i,
+                               (unsigned int)ARRAY_SIZE(state->vector),
+                               (unsigned int)state->vector[i].iov_len);
+               }
+       }
+
+       if (req->do_encryption) {
+               NTSTATUS status;
+               struct smbXsrv_session *x = req->session;
+               struct smbXsrv_connection *conn = x->connection;
+               DATA_BLOB encryption_key = x->global->encryption_key;
+
+               status = smb2_signing_encrypt_pdu(encryption_key,
+                                       conn->protocol,
+                                       &state->vector[1+SMBD_SMB2_TF_IOV_OFS],
+                                       SMBD_SMB2_NUM_IOV_PER_REQ);
+               if (!NT_STATUS_IS_OK(status)) {
+                       smbd_server_connection_terminate(req->sconn,
+                                               nt_errstr(status));
+                       return;
+               }
+       } else if (req->do_signing) {
                NTSTATUS status;
                struct smbXsrv_session *x = req->session;
                struct smbXsrv_connection *conn = x->connection;
                DATA_BLOB signing_key = x->global->channels[0].signing_key;
 
                status = smb2_signing_sign_pdu(signing_key,
-                                              conn->protocol,
-                                              &state->vector[1], 2);
+                                       conn->protocol,
+                                       &state->vector[1+SMBD_SMB2_HDR_IOV_OFS],
+                                       SMBD_SMB2_NUM_IOV_PER_REQ - 1);
                if (!NT_STATUS_IS_OK(status)) {
                        smbd_server_connection_terminate(req->sconn,
                                                nt_errstr(status));
@@ -1372,7 +1560,7 @@ static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
                                        state->sconn->smb2.stream,
                                        state->sconn->smb2.send_queue,
                                        state->vector,
-                                       3);
+                                       ARRAY_SIZE(state->vector));
        if (subreq == NULL) {
                smbd_server_connection_terminate(state->sconn,
                                                 nt_errstr(NT_STATUS_NO_MEMORY));
@@ -1433,7 +1621,7 @@ static NTSTATUS smbd_smb2_request_process_cancel(struct smbd_smb2_request *req)
                inhdr = SMBD_SMB2_IN_HDR_PTR(cur);
                DEBUG(10,("smbd_smb2_request_process_cancel: attempting to "
                        "cancel opcode[%s] mid %llu\n",
-                       smb2_opcode_name((uint16_t)IVAL(inhdr, SMB2_HDR_OPCODE)),
+                       smb2_opcode_name(SVAL(inhdr, SMB2_HDR_OPCODE)),
                         (unsigned long long)found_id ));
                tevent_req_cancel(cur->subreq);
        }
@@ -1467,6 +1655,8 @@ static NTSTATUS smbd_smb2_request_check_tcon(struct smbd_smb2_request *req)
                in_tid = req->last_tid;
        }
 
+       req->last_tid = 0;
+
        status = smb2srv_tcon_lookup(req->session,
                                     in_tid, now, &tcon);
        if (!NT_STATUS_IS_OK(status)) {
@@ -1509,13 +1699,15 @@ static NTSTATUS smbd_smb2_request_check_session(struct smbd_smb2_request *req)
        inhdr = SMBD_SMB2_IN_HDR_PTR(req);
 
        in_flags = IVAL(inhdr, SMB2_HDR_FLAGS);
-       in_opcode = IVAL(inhdr, SMB2_HDR_OPCODE);
+       in_opcode = SVAL(inhdr, SMB2_HDR_OPCODE);
        in_session_id = BVAL(inhdr, SMB2_HDR_SESSION_ID);
 
        if (in_flags & SMB2_HDR_FLAG_CHAINED) {
                in_session_id = req->last_session_id;
        }
 
+       req->last_session_id = 0;
+
        /* lookup an existing session */
        status = smb2srv_session_lookup(req->sconn->conn,
                                        in_session_id, now,
@@ -1598,27 +1790,33 @@ NTSTATUS smbd_smb2_request_verify_creditcharge(struct smbd_smb2_request *req,
 NTSTATUS smbd_smb2_request_verify_sizes(struct smbd_smb2_request *req,
                                        size_t expected_body_size)
 {
+       struct iovec *inhdr_v;
        const uint8_t *inhdr;
        uint16_t opcode;
        const uint8_t *inbody;
-       int i = req->current_idx;
        size_t body_size;
        size_t min_dyn_size = expected_body_size & 0x00000001;
+       int max_idx = req->in.vector_count - SMBD_SMB2_NUM_IOV_PER_REQ;
 
        /*
         * The following should be checked already.
         */
-       if ((i+2) > req->in.vector_count) {
+       if (req->in.vector_count < SMBD_SMB2_NUM_IOV_PER_REQ) {
                return NT_STATUS_INTERNAL_ERROR;
        }
-       if (req->in.vector[i+0].iov_len != SMB2_HDR_BODY) {
+       if (req->current_idx > max_idx) {
                return NT_STATUS_INTERNAL_ERROR;
        }
-       if (req->in.vector[i+1].iov_len < 2) {
+
+       inhdr_v = SMBD_SMB2_IN_HDR_IOV(req);
+       if (inhdr_v->iov_len != SMB2_HDR_BODY) {
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+       if (SMBD_SMB2_IN_BODY_LEN(req) < 2) {
                return NT_STATUS_INTERNAL_ERROR;
        }
 
-       inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
+       inhdr = SMBD_SMB2_IN_HDR_PTR(req);
        opcode = SVAL(inhdr, SMB2_HDR_OPCODE);
 
        switch (opcode) {
@@ -1633,14 +1831,14 @@ NTSTATUS smbd_smb2_request_verify_sizes(struct smbd_smb2_request *req,
         * where the last byte might be in the
         * dynamic section..
         */
-       if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
+       if (SMBD_SMB2_IN_BODY_LEN(req) != (expected_body_size & 0xFFFFFFFE)) {
                return NT_STATUS_INVALID_PARAMETER;
        }
-       if (req->in.vector[i+2].iov_len < min_dyn_size) {
+       if (SMBD_SMB2_IN_DYN_LEN(req) < min_dyn_size) {
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
+       inbody = SMBD_SMB2_IN_BODY_PTR(req);
 
        body_size = SVAL(inbody, 0x00);
        if (body_size != expected_body_size) {
@@ -1654,6 +1852,7 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
 {
        struct smbXsrv_connection *conn = req->sconn->conn;
        const struct smbd_smb2_dispatch_table *call = NULL;
+       const struct iovec *intf_v = SMBD_SMB2_IN_TF_IOV(req);
        const uint8_t *inhdr;
        uint16_t opcode;
        uint32_t flags;
@@ -1664,13 +1863,14 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
        NTSTATUS return_value;
        struct smbXsrv_session *x = NULL;
        bool signing_required = false;
+       bool encryption_required = false;
 
        inhdr = SMBD_SMB2_IN_HDR_PTR(req);
 
        /* TODO: verify more things */
 
        flags = IVAL(inhdr, SMB2_HDR_FLAGS);
-       opcode = IVAL(inhdr, SMB2_HDR_OPCODE);
+       opcode = SVAL(inhdr, SMB2_HDR_OPCODE);
        mid = BVAL(inhdr, SMB2_HDR_MESSAGE_ID);
        DEBUG(10,("smbd_smb2_request_dispatch: opcode[%s] mid = %llu\n",
                smb2_opcode_name(opcode),
@@ -1696,21 +1896,6 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
                }
        }
 
-       call = smbd_smb2_call(opcode);
-       if (call == NULL) {
-               return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
-       }
-
-       allowed_flags = SMB2_HDR_FLAG_CHAINED |
-                       SMB2_HDR_FLAG_SIGNED |
-                       SMB2_HDR_FLAG_DFS;
-       if (opcode == SMB2_OP_CANCEL) {
-               allowed_flags |= SMB2_HDR_FLAG_ASYNC;
-       }
-       if ((flags & ~allowed_flags) != 0) {
-               return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
-       }
-
        /*
         * Check if the client provided a valid session id,
         * if so smbd_smb2_request_check_session() calls
@@ -1721,9 +1906,9 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
         */
        session_status = smbd_smb2_request_check_session(req);
        x = req->session;
-
        if (x != NULL) {
                signing_required = x->global->signing_required;
+               encryption_required = x->global->encryption_required;
 
                if (opcode == SMB2_OP_SESSSETUP &&
                    x->global->channels[0].signing_key.length) {
@@ -1732,7 +1917,66 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
        }
 
        req->do_signing = false;
-       if (flags & SMB2_HDR_FLAG_SIGNED) {
+       req->do_encryption = false;
+       if (intf_v->iov_len == SMB2_TF_HDR_SIZE) {
+               const uint8_t *intf = SMBD_SMB2_IN_TF_PTR(req);
+               uint64_t tf_session_id = BVAL(intf, SMB2_TF_SESSION_ID);
+
+               if (x != NULL && x->global->session_wire_id != tf_session_id) {
+                       DEBUG(0,("smbd_smb2_request_dispatch: invalid session_id"
+                                "in SMB2_HDR[%llu], SMB2_TF[%llu]\n",
+                                (unsigned long long)x->global->session_wire_id,
+                                (unsigned long long)tf_session_id));
+                       /*
+                        * TODO: windows allows this...
+                        * should we drop the connection?
+                        *
+                        * For now we just return ACCESS_DENIED
+                        * (Windows clients never trigger this)
+                        * and wait for an update of [MS-SMB2].
+                        */
+                       return smbd_smb2_request_error(req,
+                                       NT_STATUS_ACCESS_DENIED);
+               }
+
+               req->do_encryption = true;
+       }
+
+       if (encryption_required && !req->do_encryption) {
+               return smbd_smb2_request_error(req,
+                               NT_STATUS_ACCESS_DENIED);
+       }
+
+       call = smbd_smb2_call(opcode);
+       if (call == NULL) {
+               return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
+       }
+
+       allowed_flags = SMB2_HDR_FLAG_CHAINED |
+                       SMB2_HDR_FLAG_SIGNED |
+                       SMB2_HDR_FLAG_DFS;
+       if (opcode == SMB2_OP_CANCEL) {
+               allowed_flags |= SMB2_HDR_FLAG_ASYNC;
+       }
+       if ((flags & ~allowed_flags) != 0) {
+               return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
+       }
+
+       if (flags & SMB2_HDR_FLAG_CHAINED) {
+               /*
+                * This check is mostly for giving the correct error code
+                * for compounded requests.
+                */
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
+               }
+       } else {
+               req->compat_chain_fsp = NULL;
+       }
+
+       if (req->do_encryption) {
+               signing_required = false;
+       } else if (flags & SMB2_HDR_FLAG_SIGNED) {
                DATA_BLOB signing_key;
 
                if (x == NULL) {
@@ -1742,39 +1986,44 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
 
                signing_key = x->global->channels[0].signing_key;
 
-               if (!NT_STATUS_IS_OK(session_status)) {
-                       return smbd_smb2_request_error(req, session_status);
+               /*
+                * If we have a signing key, we should
+                * sign the response
+                */
+               if (signing_key.length > 0) {
+                       req->do_signing = true;
                }
 
-               req->do_signing = true;
                status = smb2_signing_check_pdu(signing_key,
                                                conn->protocol,
-                                               SMBD_SMB2_IN_HDR_IOV(req), 3);
+                                               SMBD_SMB2_IN_HDR_IOV(req),
+                                               SMBD_SMB2_NUM_IOV_PER_REQ - 1);
                if (!NT_STATUS_IS_OK(status)) {
                        return smbd_smb2_request_error(req, status);
                }
+
+               /*
+                * Now that we know the request was correctly signed
+                * we have to sign the response too.
+                */
+               req->do_signing = true;
+
+               if (!NT_STATUS_IS_OK(session_status)) {
+                       return smbd_smb2_request_error(req, session_status);
+               }
        } else if (opcode == SMB2_OP_CANCEL) {
                /* Cancel requests are allowed to skip the signing */
        } else if (signing_required) {
-               return smbd_smb2_request_error(req, NT_STATUS_ACCESS_DENIED);
-       }
-
-       if (flags & SMB2_HDR_FLAG_CHAINED) {
                /*
-                * This check is mostly for giving the correct error code
-                * for compounded requests.
-                *
-                * TODO: we may need to move this after the session
-                *       and tcon checks.
+                * If signing is required we try to sign
+                * a possible error response
                 */
-               if (!NT_STATUS_IS_OK(req->next_status)) {
-                       return smbd_smb2_request_error(req, req->next_status);
-               }
-       } else {
-               req->compat_chain_fsp = NULL;
+               req->do_signing = true;
+               return smbd_smb2_request_error(req, NT_STATUS_ACCESS_DENIED);
        }
 
-       if (req->compound_related) {
+       if (flags & SMB2_HDR_FLAG_CHAINED) {
+               req->compound_related = true;
                req->sconn->smb2.compound_related_in_progress = true;
        }
 
@@ -1797,9 +2046,53 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
                if (!NT_STATUS_IS_OK(status)) {
                        return smbd_smb2_request_error(req, status);
                }
+               if (req->tcon->global->encryption_required) {
+                       encryption_required = true;
+               }
+               if (encryption_required && !req->do_encryption) {
+                       return smbd_smb2_request_error(req,
+                               NT_STATUS_ACCESS_DENIED);
+               }
+       }
+
+       if (call->fileid_ofs != 0) {
+               size_t needed = call->fileid_ofs + 16;
+               const uint8_t *body = SMBD_SMB2_IN_BODY_PTR(req);
+               size_t body_size = SMBD_SMB2_IN_BODY_LEN(req);
+               uint64_t file_id_persistent;
+               uint64_t file_id_volatile;
+               struct files_struct *fsp;
+
+               SMB_ASSERT(call->need_tcon);
+
+               if (needed > body_size) {
+                       return smbd_smb2_request_error(req,
+                                       NT_STATUS_INVALID_PARAMETER);
+               }
+
+               file_id_persistent      = BVAL(body, call->fileid_ofs + 0);
+               file_id_volatile        = BVAL(body, call->fileid_ofs + 8);
+
+               fsp = file_fsp_smb2(req, file_id_persistent, file_id_volatile);
+               if (fsp == NULL) {
+                       if (!call->allow_invalid_fileid) {
+                               return smbd_smb2_request_error(req,
+                                               NT_STATUS_FILE_CLOSED);
+                       }
+
+                       if (file_id_persistent != UINT64_MAX) {
+                               return smbd_smb2_request_error(req,
+                                               NT_STATUS_FILE_CLOSED);
+                       }
+                       if (file_id_volatile != UINT64_MAX) {
+                               return smbd_smb2_request_error(req,
+                                               NT_STATUS_FILE_CLOSED);
+                       }
+               }
        }
 
        if (call->as_root) {
+               SMB_ASSERT(call->fileid_ofs == 0);
                /* This call needs to be run as root */
                change_to_root_user();
        } else {
@@ -1968,13 +2261,93 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
 
 static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
 {
+       struct smbXsrv_connection *conn = req->sconn->conn;
        struct tevent_req *subreq;
-       int i = req->current_idx;
+       int first_idx = 1;
+       struct iovec *firsttf = SMBD_SMB2_IDX_TF_IOV(req,out,first_idx);
+       struct iovec *outhdr = SMBD_SMB2_OUT_HDR_IOV(req);
+       struct iovec *outdyn = SMBD_SMB2_OUT_DYN_IOV(req);
 
        req->subreq = NULL;
        TALLOC_FREE(req->async_te);
 
-       req->current_idx += 3;
+       if (req->do_encryption &&
+           (firsttf->iov_len == 0) &&
+           (req->first_key.length == 0) &&
+           (req->session != NULL) &&
+           (req->session->global->encryption_key.length != 0))
+       {
+               DATA_BLOB encryption_key = req->session->global->encryption_key;
+               uint8_t *tf;
+               uint64_t session_id = req->session->global->session_wire_id;
+               struct smbXsrv_session *x = req->session;
+               uint64_t nonce_high;
+               uint64_t nonce_low;
+
+               nonce_high = x->nonce_high;
+               nonce_low = x->nonce_low;
+
+               x->nonce_low += 1;
+               if (x->nonce_low == 0) {
+                       x->nonce_low += 1;
+                       x->nonce_high += 1;
+               }
+
+               /*
+                * We need to place the SMB2_TRANSFORM header before the
+                * first SMB2 header
+                */
+
+               /*
+                * we need to remember the encryption key
+                * and defer the signing/encryption until
+                * we are sure that we do not change
+                * the header again.
+                */
+               req->first_key = data_blob_dup_talloc(req, encryption_key);
+               if (req->first_key.data == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               tf = talloc_zero_array(req->out.vector, uint8_t,
+                                      SMB2_TF_HDR_SIZE);
+               if (tf == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               SIVAL(tf, SMB2_TF_PROTOCOL_ID, SMB2_TF_MAGIC);
+               SBVAL(tf, SMB2_TF_NONCE+0, nonce_low);
+               SBVAL(tf, SMB2_TF_NONCE+8, nonce_high);
+               SBVAL(tf, SMB2_TF_SESSION_ID, session_id);
+
+               firsttf->iov_base = (void *)tf;
+               firsttf->iov_len = SMB2_TF_HDR_SIZE;
+       }
+
+       if ((req->current_idx > SMBD_SMB2_NUM_IOV_PER_REQ) &&
+           (req->last_key.length > 0) &&
+           (firsttf->iov_len == 0))
+       {
+               int last_idx = req->current_idx - SMBD_SMB2_NUM_IOV_PER_REQ;
+               struct iovec *lasthdr = SMBD_SMB2_IDX_HDR_IOV(req,out,last_idx);
+               NTSTATUS status;
+
+               /*
+                * As we are sure the header of the last request in the
+                * compound chain will not change, we can to sign here
+                * with the last signing key we remembered.
+                */
+               status = smb2_signing_sign_pdu(req->last_key,
+                                              conn->protocol,
+                                              lasthdr,
+                                              SMBD_SMB2_NUM_IOV_PER_REQ - 1);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       }
+       data_blob_clear_free(&req->last_key);
+
+       req->current_idx += SMBD_SMB2_NUM_IOV_PER_REQ;
 
        if (req->current_idx < req->out.vector_count) {
                /*
@@ -1989,6 +2362,23 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
                if (!im) {
                        return NT_STATUS_NO_MEMORY;
                }
+
+               if (req->do_signing && firsttf->iov_len == 0) {
+                       struct smbXsrv_session *x = req->session;
+                       DATA_BLOB signing_key = x->global->channels[0].signing_key;
+
+                       /*
+                        * we need to remember the signing key
+                        * and defer the signing until
+                        * we are sure that we do not change
+                        * the header again.
+                        */
+                       req->last_key = data_blob_dup_talloc(req, signing_key);
+                       if (req->last_key.data == NULL) {
+                               return NT_STATUS_NO_MEMORY;
+                       }
+               }
+
                tevent_schedule_immediate(im,
                                        req->sconn->ev_ctx,
                                        smbd_smb2_request_dispatch_immediate,
@@ -1997,6 +2387,7 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
        }
 
        if (req->compound_related) {
+               req->compound_related = false;
                req->sconn->smb2.compound_related_in_progress = false;
        }
 
@@ -2006,29 +2397,37 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
           is a final reply for an async operation). */
        smb2_calculate_credits(req, req);
 
-       if (req->do_signing) {
+       /*
+        * now check if we need to sign the current response
+        */
+       if (firsttf->iov_len == SMB2_TF_HDR_SIZE) {
+               NTSTATUS status;
+
+               status = smb2_signing_encrypt_pdu(req->first_key,
+                                       conn->protocol,
+                                       firsttf,
+                                       req->out.vector_count - first_idx);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       } else if (req->do_signing) {
                NTSTATUS status;
                struct smbXsrv_session *x = req->session;
-               struct smbXsrv_connection *conn = x->connection;
                DATA_BLOB signing_key = x->global->channels[0].signing_key;
 
                status = smb2_signing_sign_pdu(signing_key,
                                               conn->protocol,
-                                              &req->out.vector[i], 3);
+                                              outhdr,
+                                              SMBD_SMB2_NUM_IOV_PER_REQ - 1);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
        }
-
-       if (DEBUGLEVEL >= 10) {
-               dbgtext("smbd_smb2_request_reply: sending...\n");
-               print_req_vectors(req);
-       }
+       data_blob_clear_free(&req->first_key);
 
        /* I am a sick, sick man... :-). Sendfile hack ... JRA. */
-       if (req->out.vector_count == 4 &&
-                       req->out.vector[3].iov_base == NULL &&
-                       req->out.vector[3].iov_len != 0) {
+       if (req->out.vector_count < (2*SMBD_SMB2_NUM_IOV_PER_REQ) &&
+           outdyn->iov_base == NULL && outdyn->iov_len != 0) {
                /* Dynamic part is NULL. Chop it off,
                   We're going to send it via sendfile. */
                req->out.vector_count -= 1;
@@ -2118,12 +2517,13 @@ NTSTATUS smbd_smb2_request_done_ex(struct smbd_smb2_request *req,
                                   const char *location)
 {
        uint8_t *outhdr;
-       int i = req->current_idx;
+       struct iovec *outbody_v;
+       struct iovec *outdyn_v;
        uint32_t next_command_ofs;
 
        DEBUG(10,("smbd_smb2_request_done_ex: "
                  "idx[%d] status[%s] body[%u] dyn[%s:%u] at %s\n",
-                 i, nt_errstr(status), (unsigned int)body.length,
+                 req->current_idx, nt_errstr(status), (unsigned int)body.length,
                  dyn ? "yes": "no",
                  (unsigned int)(dyn ? dyn->length : 0),
                  location));
@@ -2136,32 +2536,34 @@ NTSTATUS smbd_smb2_request_done_ex(struct smbd_smb2_request *req,
                return smbd_smb2_request_error(req, NT_STATUS_INTERNAL_ERROR);
        }
 
-       outhdr = (uint8_t *)req->out.vector[i].iov_base;
+       outhdr = SMBD_SMB2_OUT_HDR_PTR(req);
+       outbody_v = SMBD_SMB2_OUT_BODY_IOV(req);
+       outdyn_v = SMBD_SMB2_OUT_DYN_IOV(req);
 
        next_command_ofs = IVAL(outhdr, SMB2_HDR_NEXT_COMMAND);
        SIVAL(outhdr, SMB2_HDR_STATUS, NT_STATUS_V(status));
 
-       req->out.vector[i+1].iov_base = (void *)body.data;
-       req->out.vector[i+1].iov_len = body.length;
+       outbody_v->iov_base = (void *)body.data;
+       outbody_v->iov_len = body.length;
 
        if (dyn) {
-               req->out.vector[i+2].iov_base   = (void *)dyn->data;
-               req->out.vector[i+2].iov_len    = dyn->length;
+               outdyn_v->iov_base = (void *)dyn->data;
+               outdyn_v->iov_len = dyn->length;
        } else {
-               req->out.vector[i+2].iov_base = NULL;
-               req->out.vector[i+2].iov_len = 0;
+               outdyn_v->iov_base = NULL;
+               outdyn_v->iov_len = 0;
        }
 
        /* see if we need to recalculate the offset to the next response */
        if (next_command_ofs > 0) {
                next_command_ofs  = SMB2_HDR_BODY;
-               next_command_ofs += req->out.vector[i+1].iov_len;
-               next_command_ofs += req->out.vector[i+2].iov_len;
+               next_command_ofs += SMBD_SMB2_OUT_BODY_LEN(req);
+               next_command_ofs += SMBD_SMB2_OUT_DYN_LEN(req);
        }
 
        if ((next_command_ofs % 8) != 0) {
                size_t pad_size = 8 - (next_command_ofs % 8);
-               if (req->out.vector[i+2].iov_len == 0) {
+               if (SMBD_SMB2_OUT_DYN_LEN(req) == 0) {
                        /*
                         * if the dyn buffer is empty
                         * we can use it to add padding
@@ -2175,8 +2577,8 @@ NTSTATUS smbd_smb2_request_done_ex(struct smbd_smb2_request *req,
                                                NT_STATUS_NO_MEMORY);
                        }
 
-                       req->out.vector[i+2].iov_base = (void *)pad;
-                       req->out.vector[i+2].iov_len = pad_size;
+                       outdyn_v->iov_base = (void *)pad;
+                       outdyn_v->iov_len = pad_size;
                } else {
                        /*
                         * For now we copy the dynamic buffer
@@ -2187,8 +2589,8 @@ NTSTATUS smbd_smb2_request_done_ex(struct smbd_smb2_request *req,
                        size_t new_size;
                        uint8_t *new_dyn;
 
-                       old_size = req->out.vector[i+2].iov_len;
-                       old_dyn = (uint8_t *)req->out.vector[i+2].iov_base;
+                       old_size = SMBD_SMB2_OUT_DYN_LEN(req);
+                       old_dyn = SMBD_SMB2_OUT_DYN_PTR(req);
 
                        new_size = old_size + pad_size;
                        new_dyn = talloc_zero_array(req->out.vector,
@@ -2201,8 +2603,8 @@ NTSTATUS smbd_smb2_request_done_ex(struct smbd_smb2_request *req,
                        memcpy(new_dyn, old_dyn, old_size);
                        memset(new_dyn + old_size, 0, pad_size);
 
-                       req->out.vector[i+2].iov_base = (void *)new_dyn;
-                       req->out.vector[i+2].iov_len = new_size;
+                       outdyn_v->iov_base = (void *)new_dyn;
+                       outdyn_v->iov_len = new_size;
                }
                next_command_ofs += pad_size;
        }
@@ -2218,13 +2620,22 @@ NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req,
                                    const char *location)
 {
        DATA_BLOB body;
-       int i = req->current_idx;
        uint8_t *outhdr = SMBD_SMB2_OUT_HDR_PTR(req);
+       size_t unread_bytes = smbd_smb2_unread_bytes(req);
 
        DEBUG(10,("smbd_smb2_request_error_ex: idx[%d] status[%s] |%s| at %s\n",
-                 i, nt_errstr(status), info ? " +info" : "",
+                 req->current_idx, nt_errstr(status), info ? " +info" : "",
                  location));
 
+       if (unread_bytes) {
+               /* Recvfile error. Drain incoming socket. */
+               size_t ret = drain_socket(req->sconn->sock, unread_bytes);
+               if (ret != unread_bytes) {
+                       smbd_server_connection_terminate(req->sconn,
+                               "Failed to drain SMB2 socket\n");
+               }
+       }
+
        body.data = outhdr + SMB2_HDR_BODY;
        body.length = 8;
        SSVAL(body.data, 0, 9);
@@ -2249,10 +2660,9 @@ NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req,
        }
 
        /*
-        * if a request fails, all other remaining
-        * compounded requests should fail too
+        * Note: Even if there is an error, continue to process the request.
+        * per MS-SMB2.
         */
-       req->next_status = NT_STATUS_INVALID_PARAMETER;
 
        return smbd_smb2_request_done_ex(req, status, body, info, __location__);
 }
@@ -2260,21 +2670,35 @@ NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req,
 
 struct smbd_smb2_send_oplock_break_state {
        struct smbd_server_connection *sconn;
-       uint8_t buf[4 + SMB2_HDR_BODY + 0x18];
-       struct iovec vector;
+       uint8_t buf[NBT_HDR_SIZE + SMB2_TF_HDR_SIZE + SMB2_HDR_BODY + 0x18];
+       struct iovec vector[1+SMBD_SMB2_NUM_IOV_PER_REQ];
 };
 
 static void smbd_smb2_oplock_break_writev_done(struct tevent_req *subreq);
 
 NTSTATUS smbd_smb2_send_oplock_break(struct smbd_server_connection *sconn,
-                                    uint64_t file_id_persistent,
-                                    uint64_t file_id_volatile,
+                                    struct smbXsrv_session *session,
+                                    struct smbXsrv_tcon *tcon,
+                                    struct smbXsrv_open *op,
                                     uint8_t oplock_level)
 {
        struct smbd_smb2_send_oplock_break_state *state;
+       struct smbXsrv_connection *conn = sconn->conn;
        struct tevent_req *subreq;
+       uint8_t *tf;
+       size_t tf_len;
        uint8_t *hdr;
        uint8_t *body;
+       size_t body_len;
+       uint8_t *dyn;
+       size_t dyn_len;
+       bool do_encryption = session->global->encryption_required;
+       uint64_t nonce_high = 0;
+       uint64_t nonce_low = 0;
+
+       if (tcon->global->encryption_required) {
+               do_encryption = true;
+       }
 
        state = talloc(sconn, struct smbd_smb2_send_oplock_break_state);
        if (state == NULL) {
@@ -2282,12 +2706,29 @@ NTSTATUS smbd_smb2_send_oplock_break(struct smbd_server_connection *sconn,
        }
        state->sconn = sconn;
 
-       state->vector.iov_base = (void *)state->buf;
-       state->vector.iov_len = sizeof(state->buf);
-
-       _smb2_setlen(state->buf, sizeof(state->buf) - 4);
-       hdr = state->buf + 4;
+       tf = state->buf + NBT_HDR_SIZE;
+       tf_len = SMB2_TF_HDR_SIZE;
+       hdr = tf + tf_len;
        body = hdr + SMB2_HDR_BODY;
+       body_len = 0x18;
+       dyn = body + body_len;
+       dyn_len = 0;
+
+       if (do_encryption) {
+               nonce_high = session->nonce_high;
+               nonce_low = session->nonce_low;
+
+               session->nonce_low += 1;
+               if (session->nonce_low == 0) {
+                       session->nonce_low += 1;
+                       session->nonce_high += 1;
+               }
+       }
+
+       SIVAL(tf, SMB2_TF_PROTOCOL_ID, SMB2_TF_MAGIC);
+       SBVAL(tf, SMB2_TF_NONCE+0, nonce_low);
+       SBVAL(tf, SMB2_TF_NONCE+8, nonce_high);
+       SBVAL(tf, SMB2_TF_SESSION_ID, session->global->session_wire_id);
 
        SIVAL(hdr, 0,                           SMB2_MAGIC);
        SSVAL(hdr, SMB2_HDR_LENGTH,             SMB2_HDR_BODY);
@@ -2303,19 +2744,55 @@ NTSTATUS smbd_smb2_send_oplock_break(struct smbd_server_connection *sconn,
        SBVAL(hdr, SMB2_HDR_SESSION_ID,         0);
        memset(hdr+SMB2_HDR_SIGNATURE, 0, 16);
 
-       SSVAL(body, 0x00, 0x18);
+       SSVAL(body, 0x00, body_len);
 
        SCVAL(body, 0x02, oplock_level);
        SCVAL(body, 0x03, 0);           /* reserved */
        SIVAL(body, 0x04, 0);           /* reserved */
-       SBVAL(body, 0x08, file_id_persistent);
-       SBVAL(body, 0x10, file_id_volatile);
+       SBVAL(body, 0x08, op->global->open_persistent_id);
+       SBVAL(body, 0x10, op->global->open_volatile_id);
+
+       state->vector[0].iov_base = (void *)state->buf;
+       state->vector[0].iov_len = NBT_HDR_SIZE;
+
+       if (do_encryption) {
+               state->vector[1+SMBD_SMB2_TF_IOV_OFS].iov_base   = tf;
+               state->vector[1+SMBD_SMB2_TF_IOV_OFS].iov_len    = tf_len;
+       } else {
+               state->vector[1+SMBD_SMB2_TF_IOV_OFS].iov_base   = NULL;
+               state->vector[1+SMBD_SMB2_TF_IOV_OFS].iov_len    = 0;
+       }
+
+       state->vector[1+SMBD_SMB2_HDR_IOV_OFS].iov_base  = hdr;
+       state->vector[1+SMBD_SMB2_HDR_IOV_OFS].iov_len   = SMB2_HDR_BODY;
+
+       state->vector[1+SMBD_SMB2_BODY_IOV_OFS].iov_base = body;
+       state->vector[1+SMBD_SMB2_BODY_IOV_OFS].iov_len  = body_len;
+
+       state->vector[1+SMBD_SMB2_DYN_IOV_OFS].iov_base  = dyn;
+       state->vector[1+SMBD_SMB2_DYN_IOV_OFS].iov_len   = dyn_len;
+
+       smb2_setup_nbt_length(state->vector, 1 + SMBD_SMB2_NUM_IOV_PER_REQ);
+
+       if (do_encryption) {
+               NTSTATUS status;
+               DATA_BLOB encryption_key = session->global->encryption_key;
+
+               status = smb2_signing_encrypt_pdu(encryption_key,
+                                       conn->protocol,
+                                       &state->vector[1+SMBD_SMB2_TF_IOV_OFS],
+                                       SMBD_SMB2_NUM_IOV_PER_REQ);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+       }
 
        subreq = tstream_writev_queue_send(state,
                                           sconn->ev_ctx,
                                           sconn->smb2.stream,
                                           sconn->smb2.send_queue,
-                                          &state->vector, 1);
+                                          state->vector,
+                                          ARRAY_SIZE(state->vector));
        if (subreq == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -2354,6 +2831,8 @@ struct smbd_smb2_request_read_state {
                uint8_t nbt[NBT_HDR_SIZE];
                bool done;
        } hdr;
+       bool doing_receivefile;
+       size_t min_recv_size;
        size_t pktlen;
        uint8_t *pktbuf;
 };
@@ -2365,6 +2844,17 @@ static int smbd_smb2_request_next_vector(struct tstream_context *stream,
                                         size_t *_count);
 static void smbd_smb2_request_read_done(struct tevent_req *subreq);
 
+static size_t get_min_receive_file_size(struct smbd_smb2_request *smb2_req)
+{
+       if (smb2_req->do_signing) {
+               return 0;
+       }
+       if (smb2_req->do_encryption) {
+               return 0;
+       }
+       return (size_t)lp_min_receive_file_size();
+}
+
 static struct tevent_req *smbd_smb2_request_read_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        struct smbd_server_connection *sconn)
@@ -2386,6 +2876,7 @@ static struct tevent_req *smbd_smb2_request_read_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
        state->smb2_req->sconn = sconn;
+       state->min_recv_size = get_min_receive_file_size(state->smb2_req);
 
        subreq = tstream_readv_pdu_queue_send(state->smb2_req,
                                        state->ev,