From 1aa80f5788da34cd10141e6bfb5c263346dda75e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Sat, 17 Apr 2010 22:42:23 -0700 Subject: Second part of fix for bug #7331 - Compound async SMB 2 requests don't work right. Cause us to match W2K8R2 by sending an interim compound reply followed by a async reply. Jeremy. --- source3/smbd/smb2_server.c | 216 +++++++++++++++++++++++++++++++++------------ 1 file changed, 158 insertions(+), 58 deletions(-) (limited to 'source3/smbd') diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 64f9eaba14..b697cb924b 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -488,7 +488,7 @@ static bool dup_smb2_vec(struct iovec *dstvec, return true; } -static struct smbd_smb2_request *dup_smb2_req(struct smbd_smb2_request *req) +static struct smbd_smb2_request *dup_smb2_req(const struct smbd_smb2_request *req) { struct smbd_smb2_request *newreq = NULL; struct iovec *outvec = NULL; @@ -536,13 +536,101 @@ static struct smbd_smb2_request *dup_smb2_req(struct smbd_smb2_request *req) 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; + uint8_t *outhdr = NULL; + struct smbd_smb2_request *nreq = NULL; + + /* Create a new smb2 request we'll use + for the interim return. */ + nreq = dup_smb2_req(req); + if (!nreq) { + return NT_STATUS_NO_MEMORY; + } + + /* Lose the last 3 out vectors. They're the + ones we'll be using for the async reply. */ + nreq->out.vector_count -= 3; + + smb2_setup_nbt_length(nreq->out.vector, + nreq->out.vector_count); + + /* Step back to the previous reply. */ + i = nreq->current_idx - 3; + outhdr = nreq->out.vector[i].iov_base; + /* And end the chain. */ + SIVAL(outhdr, SMB2_HDR_NEXT_COMMAND, 0); + + /* Re-sign if needed. */ + if (nreq->do_signing) { + NTSTATUS status; + status = smb2_signing_sign_pdu(nreq->session->session_key, + &nreq->out.vector[i], 3); + 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->smb2.event_ctx, + nreq->sconn->smb2.stream, + nreq->sconn->smb2.send_queue, + nreq->out.vector, + nreq->out.vector_count); + + if (nreq->subreq == NULL) { + return NT_STATUS_NO_MEMORY; + } + + tevent_req_set_callback(nreq->subreq, + smbd_smb2_request_writev_done, + nreq); + + return NT_STATUS_OK; +} + +struct smbd_smb2_request_pending_state { + struct smbd_server_connection *sconn; + uint8_t buf[4 + SMB2_HDR_BODY + 0x08 + 1]; + struct iovec vector[3]; +}; + +static void smbd_smb2_request_pending_writev_done(struct tevent_req *subreq) +{ + struct smbd_smb2_request_pending_state *state = + tevent_req_callback_data(subreq, + struct smbd_smb2_request_pending_state); + struct smbd_server_connection *sconn = state->sconn; + int ret; + int sys_errno; + + ret = tstream_writev_queue_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + if (ret == -1) { + NTSTATUS status = map_nt_error_from_unix(sys_errno); + smbd_server_connection_terminate(sconn, nt_errstr(status)); + return; + } + + TALLOC_FREE(state); +} + NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, struct tevent_req *subreq) { + NTSTATUS status; + struct smbd_smb2_request_pending_state *state = NULL; int i = req->current_idx; - struct smbd_smb2_request *nreq = NULL; - uint8_t *outhdr = NULL; - uint8_t *outbody = NULL; + uint8_t *reqhdr = NULL; + uint8_t *hdr = NULL; + uint8_t *body = NULL; uint32_t flags = 0; uint64_t message_id = 0; uint64_t async_id = 0; @@ -577,75 +665,87 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, print_req_vectors(req); } - /* Create a new smb2 request we'll use to return. */ - nreq = dup_smb2_req(req); - if (!nreq) { - return NT_STATUS_NO_MEMORY; + if (req->out.vector_count > 4) { + /* This is a compound reply. We + * must do an interim response + * followed by the async response + * to match W2K8R2. + */ + status = smb2_send_async_interim_response(req); + if (!NT_STATUS_IS_OK(status)) { + return status; + } } - outhdr = (uint8_t *)nreq->out.vector[i].iov_base; - - flags = IVAL(outhdr, SMB2_HDR_FLAGS); - message_id = BVAL(outhdr, SMB2_HDR_MESSAGE_ID); - + reqhdr = (uint8_t *)req->out.vector[i].iov_base; + flags = IVAL(reqhdr, SMB2_HDR_FLAGS); + message_id = BVAL(reqhdr, SMB2_HDR_MESSAGE_ID); async_id = message_id; /* keep it simple for now... */ - SIVAL(outhdr, SMB2_HDR_FLAGS, flags | SMB2_HDR_FLAG_ASYNC); - SBVAL(outhdr, SMB2_HDR_PID, async_id); - SIVAL(outhdr, SMB2_HDR_STATUS, NT_STATUS_V(STATUS_PENDING)); - - nreq->out.vector[i+1].iov_base = talloc_zero_array(nreq->out.vector, - uint8_t, - 9); - if (!nreq->out.vector[i+1].iov_base) { + + state = talloc_zero(req->sconn, struct smbd_smb2_request_pending_state); + if (state == NULL) { return NT_STATUS_NO_MEMORY; } - nreq->out.vector[i+1].iov_len = 9; - outbody = (uint8_t *)nreq->out.vector[i+1].iov_base; + state->sconn = req->sconn; - /* setup error body header */ - SSVAL(outbody, 0x00, 0x08 + 1); - SSVAL(outbody, 0x02, 0); - SIVAL(outbody, 0x04, 0); - /* Match W2K8R2... */ - SCVAL(outbody, 8, 0x21); + state->vector[0].iov_base = (void *)state->buf; + state->vector[0].iov_len = 4; - nreq->out.vector[i+2].iov_base = NULL; - nreq->out.vector[i+2].iov_len = 0; + state->vector[1].iov_base = state->buf + 4; + state->vector[1].iov_len = SMB2_HDR_BODY; - smb2_setup_nbt_length(nreq->out.vector, - nreq->out.vector_count); + state->vector[2].iov_base = state->buf + 4 + SMB2_HDR_BODY; + state->vector[2].iov_len = 9; - if (nreq->do_signing) { - NTSTATUS status; - status = smb2_signing_sign_pdu(nreq->session->session_key, - &nreq->out.vector[i], 3); + smb2_setup_nbt_length(state->vector, 3); + + hdr = state->vector[1].iov_base; + body = state->vector[2].iov_base; + + SIVAL(hdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC); + SSVAL(hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); + SSVAL(hdr, SMB2_HDR_EPOCH, 0); + SIVAL(hdr, SMB2_HDR_STATUS, NT_STATUS_V(STATUS_PENDING)); + SSVAL(hdr, SMB2_HDR_OPCODE, SVAL(reqhdr, SMB2_HDR_OPCODE)); + SSVAL(hdr, SMB2_HDR_CREDIT, 5); + SIVAL(hdr, SMB2_HDR_FLAGS, flags | SMB2_HDR_FLAG_ASYNC); + SIVAL(hdr, SMB2_HDR_NEXT_COMMAND, 0); + SBVAL(hdr, SMB2_HDR_MESSAGE_ID, message_id); + SBVAL(hdr, SMB2_HDR_PID, async_id); + SBVAL(hdr, SMB2_HDR_SESSION_ID, + BVAL(reqhdr, SMB2_HDR_SESSION_ID)); + memset(hdr+SMB2_HDR_SIGNATURE, 0, 16); + + SSVAL(body, 0x00, 0x08 + 1); + + SCVAL(body, 0x02, 0); + SCVAL(body, 0x03, 0); + SIVAL(body, 0x04, 0); + /* Match W2K8R2... */ + SCVAL(body, 0x08, 0x21); + + if (req->do_signing) { + status = smb2_signing_sign_pdu(req->session->session_key, + state->vector, 3); if (!NT_STATUS_IS_OK(status)) { return status; } } - if (DEBUGLEVEL >= 10) { - dbgtext("smbd_smb2_request_pending_queue: nreq->current_idx = %u\n", - (unsigned int)nreq->current_idx ); - dbgtext("smbd_smb2_request_pending_queue: returning %u vectors\n", - (unsigned int)nreq->out.vector_count ); - print_req_vectors(nreq); - } - - nreq->subreq = tstream_writev_queue_send(nreq, - nreq->sconn->smb2.event_ctx, - nreq->sconn->smb2.stream, - nreq->sconn->smb2.send_queue, - nreq->out.vector, - nreq->out.vector_count); + subreq = tstream_writev_queue_send(state, + req->sconn->smb2.event_ctx, + req->sconn->smb2.stream, + req->sconn->smb2.send_queue, + state->vector, + 3); - if (nreq->subreq == NULL) { + if (subreq == NULL) { return NT_STATUS_NO_MEMORY; } - tevent_req_set_callback(nreq->subreq, - smbd_smb2_request_writev_done, - nreq); + tevent_req_set_callback(subreq, + smbd_smb2_request_pending_writev_done, + state); /* Note we're going async with this request. */ req->async = true; @@ -716,9 +816,9 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, req->out.vector_count); /* Ensure our final reply matches the interim one. */ - outhdr = (uint8_t *)req->out.vector[1].iov_base; - SIVAL(outhdr, SMB2_HDR_FLAGS, flags | SMB2_HDR_FLAG_ASYNC); - SBVAL(outhdr, SMB2_HDR_PID, async_id); + reqhdr = (uint8_t *)req->out.vector[1].iov_base; + SIVAL(reqhdr, SMB2_HDR_FLAGS, flags | SMB2_HDR_FLAG_ASYNC); + SBVAL(reqhdr, SMB2_HDR_PID, async_id); { const uint8_t *inhdr = -- cgit