summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/smbd/globals.h6
-rw-r--r--source3/smbd/smb2_server.c85
2 files changed, 78 insertions, 13 deletions
diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index f9810eb998..7b2d31d6aa 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -460,6 +460,12 @@ struct smbd_smb2_request {
bool cancelled;
bool compound_related;
+ /*
+ * the signing/encryption key for the last
+ * request/response of a compound chain
+ */
+ DATA_BLOB last_key;
+
struct timeval request_time;
/* fake smb1 request. */
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index f439cd7e5f..afd001c291 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -235,6 +235,12 @@ static void smb2_setup_nbt_length(struct iovec *vector, int count)
_smb2_setlen(vector[0].iov_base, len);
}
+static int smbd_smb2_request_destructor(struct smbd_smb2_request *req)
+{
+ data_blob_clear_free(&req->last_key);
+ return 0;
+}
+
static struct smbd_smb2_request *smbd_smb2_request_allocate(TALLOC_CTX *mem_ctx)
{
TALLOC_CTX *mem_pool;
@@ -261,6 +267,8 @@ static struct smbd_smb2_request *smbd_smb2_request_allocate(TALLOC_CTX *mem_ctx)
req->last_session_id = UINT64_MAX;
req->last_tid = UINT32_MAX;
+ talloc_set_destructor(req, smbd_smb2_request_destructor);
+
return req;
}
@@ -1044,9 +1052,11 @@ static void smbd_smb2_request_writev_done(struct tevent_req *subreq);
static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request *req)
{
- int i = 0;
+ struct smbXsrv_connection *conn = req->sconn->conn;
+ struct iovec *outhdr_v = NULL;
uint8_t *outhdr = NULL;
struct smbd_smb2_request *nreq = NULL;
+ NTSTATUS status;
/* Create a new smb2 request we'll use
for the interim return. */
@@ -1063,29 +1073,29 @@ static NTSTATUS smb2_send_async_interim_response(const struct smbd_smb2_request
nreq->out.vector_count);
/* Step back to the previous reply. */
- i = nreq->current_idx - SMBD_SMB2_NUM_IOV_PER_REQ;
- outhdr = (uint8_t *)nreq->out.vector[i].iov_base;
+ nreq->current_idx -= SMBD_SMB2_NUM_IOV_PER_REQ;
+ outhdr_v = SMBD_SMB2_OUT_HDR_IOV(nreq);
+ outhdr = SMBD_SMB2_OUT_HDR_PTR(nreq);
/* And end the chain. */
SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND, 0);
/* Calculate outgoing credits */
smb2_calculate_credits(req, nreq);
- /* Re-sign if needed. */
- if (nreq->do_signing) {
- NTSTATUS status;
- struct smbXsrv_session *x = nreq->session;
- struct smbXsrv_connection *conn = x->connection;
- DATA_BLOB signing_key = x->global->channels[0].signing_key;
-
- status = smb2_signing_sign_pdu(signing_key,
+ /*
+ * 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,
- &nreq->out.vector[i],
+ outhdr_v,
SMBD_SMB2_NUM_IOV_PER_REQ);
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 );
@@ -1235,6 +1245,7 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req,
flags = (IVAL(outhdr, SMB2_HDR_FLAGS) & ~SMB2_HDR_FLAG_CHAINED);
SIVAL(outhdr, SMB2_HDR_FLAGS, flags);
}
+ data_blob_clear_free(&req->last_key);
defer_endtime = timeval_current_ofs_usec(defer_time);
req->async_te = tevent_add_timer(req->sconn->ev_ctx,
@@ -1962,13 +1973,22 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req)
static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
{
+ struct smbXsrv_connection *conn = req->sconn->conn;
struct tevent_req *subreq;
struct iovec *outhdr = SMBD_SMB2_OUT_HDR_IOV(req);
struct iovec *outdyn = SMBD_SMB2_OUT_DYN_IOV(req);
+ struct iovec *lasthdr = NULL;
req->subreq = NULL;
TALLOC_FREE(req->async_te);
+ if ((req->current_idx > SMBD_SMB2_NUM_IOV_PER_REQ) &&
+ (req->last_key.length > 0)) {
+ int last_idx = req->current_idx - SMBD_SMB2_NUM_IOV_PER_REQ;
+
+ lasthdr = SMBD_SMB2_IDX_HDR_IOV(req,out,last_idx);
+ }
+
req->current_idx += SMBD_SMB2_NUM_IOV_PER_REQ;
if (req->current_idx < req->out.vector_count) {
@@ -1984,6 +2004,25 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
if (!im) {
return NT_STATUS_NO_MEMORY;
}
+
+ data_blob_clear_free(&req->last_key);
+
+ if (req->do_signing) {
+ struct smbXsrv_session *x = req->session;
+ DATA_BLOB signing_key = x->global->channels[0].signing_key;
+
+ /*
+ * we need to remember the signing key
+ * and defer the signing until
+ * we are sure that we do not change
+ * the header again.
+ */
+ req->last_key = data_blob_dup_talloc(req, signing_key);
+ if (req->last_key.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
tevent_schedule_immediate(im,
req->sconn->ev_ctx,
smbd_smb2_request_dispatch_immediate,
@@ -2001,10 +2040,30 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
is a final reply for an async operation). */
smb2_calculate_credits(req, req);
+ /*
+ * As we are sure the header of the last request in the
+ * compound chain will not change, we can to sign here
+ * with the last signing key we remembered.
+ */
+ if (lasthdr != NULL) {
+ NTSTATUS status;
+
+ status = smb2_signing_sign_pdu(req->last_key,
+ conn->protocol,
+ lasthdr,
+ SMBD_SMB2_NUM_IOV_PER_REQ);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ data_blob_clear_free(&req->last_key);
+
+ /*
+ * now check if we need to sign the current response
+ */
if (req->do_signing) {
NTSTATUS status;
struct smbXsrv_session *x = req->session;
- struct smbXsrv_connection *conn = x->connection;
DATA_BLOB signing_key = x->global->channels[0].signing_key;
status = smb2_signing_sign_pdu(signing_key,