1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-12 09:18:10 +03:00

s3:smb2_server: add SMB3 encryption support

metze
This commit is contained in:
Stefan Metzmacher 2012-08-08 07:07:03 +02:00
parent 9f1dfd8fac
commit 9397d6709f
2 changed files with 357 additions and 53 deletions

View File

@ -462,7 +462,12 @@ struct smbd_smb2_request {
bool compound_related; 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 * request/response of a compound chain
*/ */
DATA_BLOB last_key; DATA_BLOB last_key;
@ -481,15 +486,18 @@ struct smbd_smb2_request {
*/ */
struct tevent_req *subreq; struct tevent_req *subreq;
#define SMBD_SMB2_HDR_IOV_OFS 0 #define SMBD_SMB2_TF_IOV_OFS 0
#define SMBD_SMB2_BODY_IOV_OFS 1 #define SMBD_SMB2_HDR_IOV_OFS 1
#define SMBD_SMB2_DYN_IOV_OFS 2 #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) \ #define SMBD_SMB2_IOV_IDX_OFS(req,dir,idx,ofs) \
(&req->dir.vector[(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) \ #define SMBD_SMB2_IDX_HDR_IOV(req,dir,idx) \
SMBD_SMB2_IOV_IDX_OFS(req,dir,idx,SMBD_SMB2_HDR_IOV_OFS) SMBD_SMB2_IOV_IDX_OFS(req,dir,idx,SMBD_SMB2_HDR_IOV_OFS)
#define SMBD_SMB2_IDX_BODY_IOV(req,dir,idx) \ #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) \ #define SMBD_SMB2_IDX_DYN_IOV(req,dir,idx) \
SMBD_SMB2_IOV_IDX_OFS(req,dir,idx,SMBD_SMB2_DYN_IOV_OFS) 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_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_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) #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_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_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_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_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) #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 { struct {
/* /*
* vector[0] TRANSPORT HEADER * vector[0] TRANSPORT HEADER (empty)
* . * .
* vector[1] SMB2 * vector[1] SMB2_TRANSFORM (optional)
* vector[2] fixed body * vector[2] SMB2
* vector[3] dynamic body * vector[3] fixed body
* vector[4] dynamic body
* . * .
* . * .
* . * .
* vector[4] SMB2 * vector[5] SMB2_TRANSFORM (optional)
* vector[5] fixed body * vector[6] SMB2
* vector[6] dynamic body * vector[7] fixed body
* vector[8] dynamic body
* . * .
* . * .
* . * .
@ -541,15 +555,17 @@ struct smbd_smb2_request {
/* /*
* vector[0] TRANSPORT HEADER * vector[0] TRANSPORT HEADER
* . * .
* vector[1] SMB2 * vector[1] SMB2_TRANSFORM (optional)
* vector[2] fixed body * vector[2] SMB2
* vector[3] dynamic body * vector[3] fixed body
* vector[4] dynamic body
* . * .
* . * .
* . * .
* vector[4] SMB2 * vector[5] SMB2_TRANSFORM (empty)
* vector[5] fixed body * vector[6] SMB2
* vector[6] dynamic body * vector[7] fixed body
* vector[8] dynamic body
* . * .
* . * .
* . * .

View 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) 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); data_blob_clear_free(&req->last_key);
return 0; return 0;
} }
@ -303,6 +304,9 @@ static NTSTATUS smbd_smb2_inbuf_parse_compound(struct smbXsrv_connection *conn,
int num_iov = 1; int num_iov = 1;
size_t taken = 0; size_t taken = 0;
uint8_t *first_hdr = buf; 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 * 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; uint8_t *dyn = NULL;
struct iovec *iov_tmp; 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 * 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]; cur = &iov[num_iov];
num_iov += SMBD_SMB2_NUM_IOV_PER_REQ; 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_base = hdr;
cur[SMBD_SMB2_HDR_IOV_OFS].iov_len = SMB2_HDR_BODY; 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_base = body;
@ -891,6 +978,12 @@ static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req)
outbody = outhdr + SMB2_HDR_BODY; 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_base = (void *)outhdr;
current[SMBD_SMB2_HDR_IOV_OFS].iov_len = SMB2_HDR_BODY; 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); exit_server_cleanly(reason);
} }
static bool dup_smb2_vec3(TALLOC_CTX *ctx, static bool dup_smb2_vec4(TALLOC_CTX *ctx,
struct iovec *outvec, struct iovec *outvec,
const struct iovec *srcvec) const struct iovec *srcvec)
{ {
const uint8_t *srctf;
size_t srctf_len;
const uint8_t *srchdr; const uint8_t *srchdr;
size_t srchdr_len; size_t srchdr_len;
const uint8_t *srcbody; const uint8_t *srcbody;
@ -961,10 +1056,13 @@ static bool dup_smb2_vec3(TALLOC_CTX *ctx,
const uint8_t *srcdyn; const uint8_t *srcdyn;
size_t srcdyn_len; size_t srcdyn_len;
const uint8_t *expected_srcdyn; const uint8_t *expected_srcdyn;
uint8_t *dsttf;
uint8_t *dsthdr; uint8_t *dsthdr;
uint8_t *dstbody; uint8_t *dstbody;
uint8_t *dstdyn; 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 = (const uint8_t *)srcvec[SMBD_SMB2_HDR_IOV_OFS].iov_base;
srchdr_len = srcvec[SMBD_SMB2_HDR_IOV_OFS].iov_len; srchdr_len = srcvec[SMBD_SMB2_HDR_IOV_OFS].iov_len;
srcbody = (const uint8_t *)srcvec[SMBD_SMB2_BODY_IOV_OFS].iov_base; 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; srcdyn_len = srcvec[SMBD_SMB2_DYN_IOV_OFS].iov_len;
expected_srcdyn = srcbody + 8; expected_srcdyn = srcbody + 8;
if ((srctf_len != SMB2_TF_HDR_SIZE) && (srctf_len != 0)) {
return false;
}
if (srchdr_len != SMB2_HDR_BODY) { if (srchdr_len != SMB2_HDR_BODY) {
return false; 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 /* vec[SMBD_SMB2_HDR_IOV_OFS] is always boilerplate and must
* be allocated with size OUTVEC_ALLOC_SIZE. */ * 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. */ /* Setup the vectors identically to the ones in req. */
for (i = 1; i < count; i += SMBD_SMB2_NUM_IOV_PER_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; 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) static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request *req)
{ {
struct smbXsrv_connection *conn = req->sconn->conn; struct smbXsrv_connection *conn = req->sconn->conn;
int first_idx = 1;
struct iovec *firsttf = NULL;
struct iovec *outhdr_v = NULL; struct iovec *outhdr_v = NULL;
uint8_t *outhdr = NULL; uint8_t *outhdr = NULL;
struct smbd_smb2_request *nreq = 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. */ /* Step back to the previous reply. */
nreq->current_idx -= SMBD_SMB2_NUM_IOV_PER_REQ; 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_v = SMBD_SMB2_OUT_HDR_IOV(nreq);
outhdr = SMBD_SMB2_OUT_HDR_PTR(nreq); outhdr = SMBD_SMB2_OUT_HDR_PTR(nreq);
/* And end the chain. */ /* And end the chain. */
@ -1112,20 +1228,6 @@ static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request
/* Calculate outgoing credits */ /* Calculate outgoing credits */
smb2_calculate_credits(req, nreq); smb2_calculate_credits(req, nreq);
/*
* As we have changed the header (SMB2_HDR_NEXT_COMMAND),
* we need to sign here with the last signing key we remembered
*/
if (req->last_key.length > 0) {
status = smb2_signing_sign_pdu(req->last_key,
conn->protocol,
outhdr_v,
SMBD_SMB2_NUM_IOV_PER_REQ);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
}
if (DEBUGLEVEL >= 10) { if (DEBUGLEVEL >= 10) {
dbgtext("smb2_send_async_interim_response: nreq->current_idx = %u\n", dbgtext("smb2_send_async_interim_response: nreq->current_idx = %u\n",
(unsigned int)nreq->current_idx ); (unsigned int)nreq->current_idx );
@ -1133,6 +1235,29 @@ static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request
(unsigned int)nreq->out.vector_count ); (unsigned int)nreq->out.vector_count );
print_req_vectors(nreq); 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->subreq = tstream_writev_queue_send(nreq,
nreq->sconn->ev_ctx, nreq->sconn->ev_ctx,
nreq->sconn->smb2.stream, 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_smb2_request_pending_state {
struct smbd_server_connection *sconn; 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]; 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)) { if (!NT_STATUS_IS_OK(status)) {
return status; return status;
} }
data_blob_clear_free(&req->first_key);
/* /*
* We're splitting off the last SMB2 * 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; struct smbd_smb2_request_pending_state *state = NULL;
uint8_t *outhdr = NULL; uint8_t *outhdr = NULL;
const uint8_t *inhdr = NULL; const uint8_t *inhdr = NULL;
uint8_t *tf = NULL;
size_t tf_len = 0;
uint8_t *hdr = NULL; uint8_t *hdr = NULL;
uint8_t *body = NULL; uint8_t *body = NULL;
uint8_t *dyn = NULL; uint8_t *dyn = NULL;
uint32_t flags = 0; uint32_t flags = 0;
uint64_t session_id = 0;
uint64_t message_id = 0; uint64_t message_id = 0;
uint64_t nonce_high;
uint64_t nonce_low;
uint64_t async_id = 0; uint64_t async_id = 0;
struct tevent_req *subreq = NULL; 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); outhdr = SMBD_SMB2_OUT_HDR_PTR(req);
flags = IVAL(outhdr, SMB2_HDR_FLAGS); flags = IVAL(outhdr, SMB2_HDR_FLAGS);
message_id = BVAL(outhdr, SMB2_HDR_MESSAGE_ID); 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... */ 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; 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; body = hdr + SMB2_HDR_BODY;
dyn = body + 8; 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); SIVAL(hdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC);
SSVAL(hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); 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_base = (void *)state->buf;
state->vector[0].iov_len = NBT_HDR_SIZE; 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_base = hdr;
state->vector[1+SMBD_SMB2_HDR_IOV_OFS].iov_len = SMB2_HDR_BODY; 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); 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; NTSTATUS status;
struct smbXsrv_session *x = req->session; struct smbXsrv_session *x = req->session;
struct smbXsrv_connection *conn = x->connection; 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, status = smb2_signing_sign_pdu(signing_key,
conn->protocol, conn->protocol,
&state->vector[1+SMBD_SMB2_HDR_IOV_OFS], &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)) { if (!NT_STATUS_IS_OK(status)) {
smbd_server_connection_terminate(req->sconn, smbd_server_connection_terminate(req->sconn,
nt_errstr(status)); nt_errstr(status));
@ -1701,6 +1882,7 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
{ {
struct smbXsrv_connection *conn = req->sconn->conn; struct smbXsrv_connection *conn = req->sconn->conn;
const struct smbd_smb2_dispatch_table *call = NULL; const struct smbd_smb2_dispatch_table *call = NULL;
const struct iovec *intf_v = SMBD_SMB2_IN_TF_IOV(req);
const uint8_t *inhdr; const uint8_t *inhdr;
uint16_t opcode; uint16_t opcode;
uint32_t flags; uint32_t flags;
@ -1711,6 +1893,7 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
NTSTATUS return_value; NTSTATUS return_value;
struct smbXsrv_session *x = NULL; struct smbXsrv_session *x = NULL;
bool signing_required = false; bool signing_required = false;
bool encryption_required = false;
inhdr = SMBD_SMB2_IN_HDR_PTR(req); 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); session_status = smbd_smb2_request_check_session(req);
x = req->session; x = req->session;
if (x != NULL) { if (x != NULL) {
signing_required = x->global->signing_required; signing_required = x->global->signing_required;
encryption_required = x->global->encryption_required;
if (opcode == SMB2_OP_SESSSETUP && if (opcode == SMB2_OP_SESSSETUP &&
x->global->channels[0].signing_key.length) { 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); call = smbd_smb2_call(opcode);
if (call == NULL) { if (call == NULL) {
return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 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); return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
} }
req->do_signing = false; if (req->do_encryption) {
if (flags & SMB2_HDR_FLAG_SIGNED) { signing_required = false;
} else if (flags & SMB2_HDR_FLAG_SIGNED) {
DATA_BLOB signing_key; DATA_BLOB signing_key;
if (x == NULL) { if (x == NULL) {
@ -1863,6 +2078,13 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
if (!NT_STATUS_IS_OK(status)) { if (!NT_STATUS_IS_OK(status)) {
return smbd_smb2_request_error(req, 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) { 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 smbXsrv_connection *conn = req->sconn->conn;
struct tevent_req *subreq; 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 *outhdr = SMBD_SMB2_OUT_HDR_IOV(req);
struct iovec *outdyn = SMBD_SMB2_OUT_DYN_IOV(req); struct iovec *outdyn = SMBD_SMB2_OUT_DYN_IOV(req);
req->subreq = NULL; req->subreq = NULL;
TALLOC_FREE(req->async_te); 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) && 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; int last_idx = req->current_idx - SMBD_SMB2_NUM_IOV_PER_REQ;
struct iovec *lasthdr = SMBD_SMB2_IDX_HDR_IOV(req,out,last_idx); struct iovec *lasthdr = SMBD_SMB2_IDX_HDR_IOV(req,out,last_idx);
NTSTATUS status; 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 * compound chain will not change, we can to sign here
* with the last signing key we remembered. * with the last signing key we remembered.
*/ */
status = smb2_signing_sign_pdu(req->last_key, status = smb2_signing_sign_pdu(req->last_key,
conn->protocol, conn->protocol,
lasthdr, lasthdr,
SMBD_SMB2_NUM_IOV_PER_REQ); SMBD_SMB2_NUM_IOV_PER_REQ - 1);
if (!NT_STATUS_IS_OK(status)) { if (!NT_STATUS_IS_OK(status)) {
return status; return status;
} }
@ -2117,7 +2399,7 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
return NT_STATUS_NO_MEMORY; return NT_STATUS_NO_MEMORY;
} }
if (req->do_signing) { if (req->do_signing && firsttf->iov_len == 0) {
struct smbXsrv_session *x = req->session; struct smbXsrv_session *x = req->session;
DATA_BLOB signing_key = x->global->channels[0].signing_key; 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 * 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; NTSTATUS status;
struct smbXsrv_session *x = req->session; struct smbXsrv_session *x = req->session;
DATA_BLOB signing_key = x->global->channels[0].signing_key; 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, status = smb2_signing_sign_pdu(signing_key,
conn->protocol, conn->protocol,
outhdr, outhdr,
SMBD_SMB2_NUM_IOV_PER_REQ); SMBD_SMB2_NUM_IOV_PER_REQ - 1);
if (!NT_STATUS_IS_OK(status)) { if (!NT_STATUS_IS_OK(status)) {
return status; return status;
} }
} }
data_blob_clear_free(&req->first_key);
if (DEBUGLEVEL >= 10) {
dbgtext("smbd_smb2_request_reply: sending...\n");
print_req_vectors(req);
}
/* I am a sick, sick man... :-). Sendfile hack ... JRA. */ /* I am a sick, sick man... :-). Sendfile hack ... JRA. */
if (req->out.vector_count < (2*SMBD_SMB2_NUM_IOV_PER_REQ) && if (req->out.vector_count < (2*SMBD_SMB2_NUM_IOV_PER_REQ) &&