summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2012-08-08 07:07:03 +0200
committerStefan Metzmacher <metze@samba.org>2012-08-17 14:51:57 +0200
commit9397d6709f79f2e8837401d32cd7ac584b6c5b24 (patch)
tree1d839adac1b03f15e3db5b6d7a91216849fb5154 /source3/smbd
parent9f1dfd8facaa59370afd93e89cc729de5cc3d9ba (diff)
downloadsamba-9397d6709f79f2e8837401d32cd7ac584b6c5b24.tar.gz
samba-9397d6709f79f2e8837401d32cd7ac584b6c5b24.tar.bz2
samba-9397d6709f79f2e8837401d32cd7ac584b6c5b24.zip
s3:smb2_server: add SMB3 encryption support
metze
Diffstat (limited to 'source3/smbd')
-rw-r--r--source3/smbd/globals.h52
-rw-r--r--source3/smbd/smb2_server.c350
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) &&