diff options
author | Stefan Metzmacher <metze@samba.org> | 2012-08-08 07:07:03 +0200 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2012-08-17 14:51:57 +0200 |
commit | 9397d6709f79f2e8837401d32cd7ac584b6c5b24 (patch) | |
tree | 1d839adac1b03f15e3db5b6d7a91216849fb5154 /source3 | |
parent | 9f1dfd8facaa59370afd93e89cc729de5cc3d9ba (diff) | |
download | samba-9397d6709f79f2e8837401d32cd7ac584b6c5b24.tar.gz samba-9397d6709f79f2e8837401d32cd7ac584b6c5b24.tar.bz2 samba-9397d6709f79f2e8837401d32cd7ac584b6c5b24.zip |
s3:smb2_server: add SMB3 encryption support
metze
Diffstat (limited to 'source3')
-rw-r--r-- | source3/smbd/globals.h | 52 | ||||
-rw-r--r-- | source3/smbd/smb2_server.c | 350 |
2 files changed, 353 insertions, 49 deletions
diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index ac8a1b2b18..b024fb35f8 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -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 * . * . * . diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index ff4ee60e95..106e6acd0b 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -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) && |