s3:smb2_server: add SMB3 encryption support
authorStefan Metzmacher <metze@samba.org>
Wed, 8 Aug 2012 05:07:03 +0000 (07:07 +0200)
committerStefan Metzmacher <metze@samba.org>
Fri, 17 Aug 2012 12:51:57 +0000 (14:51 +0200)
metze

source3/smbd/globals.h
source3/smbd/smb2_server.c

index ac8a1b2b18ec4e242da092ec07fbc4d176c1f906..b024fb35f842c96a7529ca9fd7584016fc21a7cc 100644 (file)
@@ -462,7 +462,12 @@ struct smbd_smb2_request {
        bool compound_related;
 
        /*
-        * the signing/encryption key for the last
+        * the encryption key for the whole
+        * compound chain
+        */
+       DATA_BLOB first_key;
+       /*
+        * the signing key for the last
         * request/response of a compound chain
         */
        DATA_BLOB last_key;
@@ -481,15 +486,18 @@ struct smbd_smb2_request {
         */
        struct tevent_req *subreq;
 
-#define SMBD_SMB2_HDR_IOV_OFS 0
-#define SMBD_SMB2_BODY_IOV_OFS 1
-#define SMBD_SMB2_DYN_IOV_OFS 2
+#define SMBD_SMB2_TF_IOV_OFS 0
+#define SMBD_SMB2_HDR_IOV_OFS 1
+#define SMBD_SMB2_BODY_IOV_OFS 2
+#define SMBD_SMB2_DYN_IOV_OFS 3
 
-#define SMBD_SMB2_NUM_IOV_PER_REQ 3
+#define SMBD_SMB2_NUM_IOV_PER_REQ 4
 
 #define SMBD_SMB2_IOV_IDX_OFS(req,dir,idx,ofs) \
        (&req->dir.vector[(idx)+(ofs)])
 
+#define SMBD_SMB2_IDX_TF_IOV(req,dir,idx) \
+       SMBD_SMB2_IOV_IDX_OFS(req,dir,idx,SMBD_SMB2_TF_IOV_OFS)
 #define SMBD_SMB2_IDX_HDR_IOV(req,dir,idx) \
        SMBD_SMB2_IOV_IDX_OFS(req,dir,idx,SMBD_SMB2_HDR_IOV_OFS)
 #define SMBD_SMB2_IDX_BODY_IOV(req,dir,idx) \
@@ -497,6 +505,8 @@ struct smbd_smb2_request {
 #define SMBD_SMB2_IDX_DYN_IOV(req,dir,idx) \
        SMBD_SMB2_IOV_IDX_OFS(req,dir,idx,SMBD_SMB2_DYN_IOV_OFS)
 
+#define SMBD_SMB2_IN_TF_IOV(req)    SMBD_SMB2_IDX_TF_IOV(req,in,req->current_idx)
+#define SMBD_SMB2_IN_TF_PTR(req)    (uint8_t *)(SMBD_SMB2_IN_TF_IOV(req)->iov_base)
 #define SMBD_SMB2_IN_HDR_IOV(req)    SMBD_SMB2_IDX_HDR_IOV(req,in,req->current_idx)
 #define SMBD_SMB2_IN_HDR_PTR(req)    (uint8_t *)(SMBD_SMB2_IN_HDR_IOV(req)->iov_base)
 #define SMBD_SMB2_IN_BODY_IOV(req)   SMBD_SMB2_IDX_BODY_IOV(req,in,req->current_idx)
@@ -506,6 +516,8 @@ struct smbd_smb2_request {
 #define SMBD_SMB2_IN_DYN_PTR(req)    (uint8_t *)(SMBD_SMB2_IN_DYN_IOV(req)->iov_base)
 #define SMBD_SMB2_IN_DYN_LEN(req)    (SMBD_SMB2_IN_DYN_IOV(req)->iov_len)
 
+#define SMBD_SMB2_OUT_TF_IOV(req)   SMBD_SMB2_IDX_TF_IOV(req,out,req->current_idx)
+#define SMBD_SMB2_OUT_TF_PTR(req)   (uint8_t *)(SMBD_SMB2_OUT_TF_IOV(req)->iov_base)
 #define SMBD_SMB2_OUT_HDR_IOV(req)   SMBD_SMB2_IDX_HDR_IOV(req,out,req->current_idx)
 #define SMBD_SMB2_OUT_HDR_PTR(req)   (uint8_t *)(SMBD_SMB2_OUT_HDR_IOV(req)->iov_base)
 #define SMBD_SMB2_OUT_BODY_IOV(req)  SMBD_SMB2_IDX_BODY_IOV(req,out,req->current_idx)
@@ -517,17 +529,19 @@ struct smbd_smb2_request {
 
        struct {
                /*
-                * vector[0] TRANSPORT HEADER
+                * vector[0] TRANSPORT HEADER (empty)
                 * .
-                * vector[1] SMB2
-                * vector[2] fixed body
-                * vector[3] dynamic body
+                * vector[1] SMB2_TRANSFORM (optional)
+                * vector[2] SMB2
+                * vector[3] fixed body
+                * vector[4] dynamic body
                 * .
                 * .
                 * .
-                * vector[4] SMB2
-                * vector[5] fixed body
-                * vector[6] dynamic body
+                * vector[5] SMB2_TRANSFORM (optional)
+                * vector[6] SMB2
+                * vector[7] fixed body
+                * vector[8] dynamic body
                 * .
                 * .
                 * .
@@ -541,15 +555,17 @@ struct smbd_smb2_request {
                /*
                 * vector[0] TRANSPORT HEADER
                 * .
-                * vector[1] SMB2
-                * vector[2] fixed body
-                * vector[3] dynamic body
+                * vector[1] SMB2_TRANSFORM (optional)
+                * vector[2] SMB2
+                * vector[3] fixed body
+                * vector[4] dynamic body
                 * .
                 * .
                 * .
-                * vector[4] SMB2
-                * vector[5] fixed body
-                * vector[6] dynamic body
+                * vector[5] SMB2_TRANSFORM (empty)
+                * vector[6] SMB2
+                * vector[7] fixed body
+                * vector[8] dynamic body
                 * .
                 * .
                 * .
index ff4ee60e95c518011f7a2e5c57f2ae67b5b30b52..106e6acd0b21ae3846f03ddb18123c1f20e18eb2 100644 (file)
@@ -256,6 +256,7 @@ static void smb2_setup_nbt_length(struct iovec *vector, int count)
 
 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;
 }
@@ -303,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
@@ -324,6 +328,87 @@ static NTSTATUS smbd_smb2_inbuf_parse_compound(struct smbXsrv_connection *conn,
                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
                 */
@@ -382,6 +467,8 @@ static NTSTATUS smbd_smb2_inbuf_parse_compound(struct smbXsrv_connection *conn,
                cur = &iov[num_iov];
                num_iov += SMBD_SMB2_NUM_IOV_PER_REQ;
 
+               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;
@@ -891,6 +978,12 @@ static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req)
 
                outbody = outhdr + 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;
 
@@ -949,10 +1042,12 @@ 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)
 {
+       const uint8_t *srctf;
+       size_t srctf_len;
        const uint8_t *srchdr;
        size_t srchdr_len;
        const uint8_t *srcbody;
@@ -961,10 +1056,13 @@ static bool dup_smb2_vec3(TALLOC_CTX *ctx,
        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;
@@ -974,10 +1072,25 @@ static bool dup_smb2_vec3(TALLOC_CTX *ctx,
        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. */
 
@@ -1061,7 +1174,7 @@ static struct smbd_smb2_request *dup_smb2_req(const struct smbd_smb2_request *re
 
        /* Setup the vectors identically to the ones in req. */
        for (i = 1; i < count; i += SMBD_SMB2_NUM_IOV_PER_REQ) {
-               if (!dup_smb2_vec3(outvec, &outvec[i], &req->out.vector[i])) {
+               if (!dup_smb2_vec4(outvec, &outvec[i], &req->out.vector[i])) {
                        break;
                }
        }
@@ -1083,6 +1196,8 @@ static void smbd_smb2_request_writev_done(struct tevent_req *subreq);
 static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request *req)
 {
        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;
@@ -1104,6 +1219,7 @@ static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request
 
        /* Step back to the previous reply. */
        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. */
@@ -1112,27 +1228,36 @@ static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request
        /* Calculate outgoing credits */
        smb2_calculate_credits(req, nreq);
 
+       if (DEBUGLEVEL >= 10) {
+               dbgtext("smb2_send_async_interim_response: nreq->current_idx = %u\n",
+                       (unsigned int)nreq->current_idx );
+               dbgtext("smb2_send_async_interim_response: returning %u vectors\n",
+                       (unsigned int)nreq->out.vector_count );
+               print_req_vectors(nreq);
+       }
+
        /*
         * As we have changed the header (SMB2_HDR_NEXT_COMMAND),
-        * we need to sign here with the last signing key we remembered
+        * we need to sign/encrypt here with the last/first key we remembered
         */
-       if (req->last_key.length > 0) {
+       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);
+                                              SMBD_SMB2_NUM_IOV_PER_REQ - 1);
                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 );
-               dbgtext("smb2_send_async_interim_response: returning %u vectors\n",
-                       (unsigned int)nreq->out.vector_count );
-               print_req_vectors(nreq);
-       }
        nreq->subreq = tstream_writev_queue_send(nreq,
                                        nreq->sconn->ev_ctx,
                                        nreq->sconn->smb2.stream,
@@ -1153,7 +1278,7 @@ 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[NBT_HDR_SIZE + SMB2_HDR_BODY + 0x08 + 1];
+        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];
 };
 
@@ -1239,6 +1364,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
@@ -1297,11 +1423,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;
+       uint64_t nonce_low;
        uint64_t async_id = 0;
        struct tevent_req *subreq = NULL;
 
@@ -1312,6 +1443,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... */
 
@@ -1337,13 +1469,27 @@ static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
        }
        state->sconn = req->sconn;
 
+       tf = state->buf + NBT_HDR_SIZE;
+       tf_len = SMB2_TF_HDR_SIZE;
 
-       hdr = state->buf + NBT_HDR_SIZE;
+       hdr = tf + SMB2_TF_HDR_SIZE;
        body = hdr + SMB2_HDR_BODY;
        dyn = body + 8;
 
+       /*
+        * We may have 2 responses (PENDING, FINAL),
+        * so alter the nonce.
+        *
+        * The PENDING response has the SMB2_HDR_FLAG_ASYNC bit
+        * set.
+        */
+       nonce_high = session_id | SMB2_HDR_FLAG_ASYNC;
+       nonce_low = message_id;
 
-
+       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);
@@ -1371,6 +1517,14 @@ static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
        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;
 
@@ -1391,7 +1545,34 @@ static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
 
        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);
+                       dump_data(0, state->vector[i].iov_base, 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;
@@ -1400,7 +1581,7 @@ static void smbd_smb2_request_pending_timer(struct tevent_context *ev,
                status = smb2_signing_sign_pdu(signing_key,
                                        conn->protocol,
                                        &state->vector[1+SMBD_SMB2_HDR_IOV_OFS],
-                                       SMBD_SMB2_NUM_IOV_PER_REQ);
+                                       SMBD_SMB2_NUM_IOV_PER_REQ - 1);
                if (!NT_STATUS_IS_OK(status)) {
                        smbd_server_connection_terminate(req->sconn,
                                                nt_errstr(status));
@@ -1701,6 +1882,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;
@@ -1711,6 +1893,7 @@ 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);
 
@@ -1753,9 +1936,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) {
@@ -1763,6 +1946,37 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
                }
        }
 
+       req->do_signing = false;
+       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);
@@ -1778,8 +1992,9 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
                return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
        }
 
-       req->do_signing = false;
-       if (flags & SMB2_HDR_FLAG_SIGNED) {
+       if (req->do_encryption) {
+               signing_required = false;
+       } else if (flags & SMB2_HDR_FLAG_SIGNED) {
                DATA_BLOB signing_key;
 
                if (x == NULL) {
@@ -1863,6 +2078,13 @@ 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) {
@@ -2073,14 +2295,75 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
 {
        struct smbXsrv_connection *conn = req->sconn->conn;
        struct tevent_req *subreq;
+       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);
 
+       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;
+               const uint8_t *inhdr = SMBD_SMB2_IN_HDR_PTR(req);
+               uint64_t session_id = req->session->global->session_wire_id;
+               uint64_t message_id = BVAL(inhdr, SMB2_HDR_MESSAGE_ID);
+               uint64_t async_id = BVAL(inhdr, SMB2_HDR_ASYNC_ID);
+               /*
+                * We may have 2 responses (PENDING, FINAL),
+                * so alter the nonce.
+                *
+                * The FINAL response has the SMB2_HDR_FLAG_ASYNC bit
+                * cleared.
+                */
+               uint64_t nonce_high = session_id & ~SMB2_HDR_FLAG_ASYNC;
+               uint64_t nonce_low = message_id;
+
+               if (nonce_low == 0) {
+                       nonce_low = async_id;
+               }
+
+               /*
+                * 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)) {
+           (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;
@@ -2090,11 +2373,10 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
                 * 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);
+                                              SMBD_SMB2_NUM_IOV_PER_REQ - 1);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
                }
@@ -2117,7 +2399,7 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
                        return NT_STATUS_NO_MEMORY;
                }
 
-               if (req->do_signing) {
+               if (req->do_signing && firsttf->iov_len == 0) {
                        struct smbXsrv_session *x = req->session;
                        DATA_BLOB signing_key = x->global->channels[0].signing_key;
 
@@ -2153,7 +2435,17 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
        /*
         * now check if we need to sign the current response
         */
-       if (req->do_signing) {
+       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;
                DATA_BLOB signing_key = x->global->channels[0].signing_key;
@@ -2161,16 +2453,12 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
                status = smb2_signing_sign_pdu(signing_key,
                                               conn->protocol,
                                               outhdr,
-                                              SMBD_SMB2_NUM_IOV_PER_REQ);
+                                              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 < (2*SMBD_SMB2_NUM_IOV_PER_REQ) &&