From d777879aaa3d86cd31fe5d1e0bbd15b9ee02e5cb Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 8 Jun 2009 16:24:27 +0200 Subject: s4:libcli/smb2: add support sending compounded requests metze --- source4/libcli/smb2/request.c | 34 +++++++-- source4/libcli/smb2/smb2.h | 65 +++++++++-------- source4/libcli/smb2/transport.c | 156 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 205 insertions(+), 50 deletions(-) (limited to 'source4/libcli') diff --git a/source4/libcli/smb2/request.c b/source4/libcli/smb2/request.c index 649a1db8d5..c351fd2fcb 100644 --- a/source4/libcli/smb2/request.c +++ b/source4/libcli/smb2/request.c @@ -63,6 +63,8 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_ { struct smb2_request *req; uint64_t seqnum; + uint32_t hdr_offset; + uint32_t flags = 0; if (body_dynamic_present) { if (body_dynamic_size == 0) { @@ -92,16 +94,34 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_ ZERO_STRUCT(req->cancel); ZERO_STRUCT(req->in); - req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size; + if (transport->compound.missing > 0) { + transport->compound.missing -= 1; + req->out = transport->compound.buffer; + ZERO_STRUCT(transport->compound.buffer); + if (transport->compound.related) { + flags |= SMB2_HDR_FLAG_CHAINED; + } + } else { + ZERO_STRUCT(req->out); + } + if (req->out.size > 0) { + hdr_offset = req->out.size; + } else { + hdr_offset = NBT_HDR_SIZE; + } + + req->out.size = hdr_offset + SMB2_HDR_BODY + body_fixed_size; req->out.allocated = req->out.size + body_dynamic_size; - req->out.buffer = talloc_array(req, uint8_t, req->out.allocated); + + req->out.buffer = talloc_realloc(req, req->out.buffer, + uint8_t, req->out.allocated); if (req->out.buffer == NULL) { talloc_free(req); return NULL; } - req->out.hdr = req->out.buffer + NBT_HDR_SIZE; + req->out.hdr = req->out.buffer + hdr_offset; req->out.body = req->out.hdr + SMB2_HDR_BODY; req->out.body_fixed= body_fixed_size; req->out.body_size = body_fixed_size; @@ -113,7 +133,7 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_ SIVAL(req->out.hdr, SMB2_HDR_STATUS, 0); SSVAL(req->out.hdr, SMB2_HDR_OPCODE, opcode); SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0); - SIVAL(req->out.hdr, SMB2_HDR_FLAGS, 0); + SIVAL(req->out.hdr, SMB2_HDR_FLAGS, flags); SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, 0); SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, req->seqnum); SIVAL(req->out.hdr, SMB2_HDR_PID, 0); @@ -245,8 +265,9 @@ static size_t smb2_padding_fix(struct smb2_request_buffer *buf) /* grow a SMB2 buffer by the specified amount */ -static NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase) +NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase) { + size_t hdr_ofs; size_t dynamic_ofs; uint8_t *buffer_ptr; uint32_t newsize = buf->size + increase; @@ -256,13 +277,14 @@ static NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increas if (newsize <= buf->allocated) return NT_STATUS_OK; + hdr_ofs = buf->hdr - buf->buffer; dynamic_ofs = buf->dynamic - buf->buffer; buffer_ptr = talloc_realloc(buf, buf->buffer, uint8_t, newsize); NT_STATUS_HAVE_NO_MEMORY(buffer_ptr); buf->buffer = buffer_ptr; - buf->hdr = buf->buffer + NBT_HDR_SIZE; + buf->hdr = buf->buffer + hdr_ofs; buf->body = buf->hdr + SMB2_HDR_BODY; buf->dynamic = buf->buffer + dynamic_ofs; buf->allocated = newsize; diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h index f1c83215c7..39d6aaacba 100644 --- a/source4/libcli/smb2/smb2.h +++ b/source4/libcli/smb2/smb2.h @@ -40,6 +40,35 @@ struct smb2_negotiate { uint16_t dialect_revision; }; +struct smb2_request_buffer { + /* the raw SMB2 buffer, including the 4 byte length header */ + uint8_t *buffer; + + /* the size of the raw buffer, including 4 byte header */ + size_t size; + + /* how much has been allocated - on reply the buffer is over-allocated to + prevent too many realloc() calls + */ + size_t allocated; + + /* the start of the SMB2 header - this is always buffer+4 */ + uint8_t *hdr; + + /* the packet body */ + uint8_t *body; + size_t body_fixed; + size_t body_size; + + /* this point to the next dynamic byte that can be used + * this will be moved when some dynamic data is pushed + */ + uint8_t *dynamic; + + /* this is used to range check and align strings and buffers */ + struct request_bufinfo bufinfo; +}; + /* this is the context for the smb2 transport layer */ struct smb2_transport { /* socket level info */ @@ -50,6 +79,13 @@ struct smb2_transport { /* next seqnum to allocate */ uint64_t seqnum; + /* the details for coumpounded requests */ + struct { + uint32_t missing; + bool related; + struct smb2_request_buffer buffer; + } compound; + /* a list of requests that are pending for receive on this connection */ struct smb2_request *pending_recv; @@ -110,35 +146,6 @@ struct smb2_session { }; -struct smb2_request_buffer { - /* the raw SMB2 buffer, including the 4 byte length header */ - uint8_t *buffer; - - /* the size of the raw buffer, including 4 byte header */ - size_t size; - - /* how much has been allocated - on reply the buffer is over-allocated to - prevent too many realloc() calls - */ - size_t allocated; - - /* the start of the SMB2 header - this is always buffer+4 */ - uint8_t *hdr; - - /* the packet body */ - uint8_t *body; - size_t body_fixed; - size_t body_size; - - /* this point to the next dynamic byte that can be used - * this will be moved when some dynamic data is pushed - */ - uint8_t *dynamic; - - /* this is used to range check and align strings and buffers */ - struct request_bufinfo bufinfo; -}; - /* a client request moves between the following 4 states. diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c index 6a87d124d9..49b72a4545 100644 --- a/source4/libcli/smb2/transport.c +++ b/source4/libcli/smb2/transport.c @@ -208,6 +208,28 @@ static NTSTATUS smb2_handle_oplock_break(struct smb2_transport *transport, return NT_STATUS_OK; } +struct smb2_transport_compount_response_state { + struct smb2_transport *transport; + DATA_BLOB blob; +}; + +static void smb2_transport_compound_response_handler(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data) +{ + struct smb2_transport_compount_response_state *state = + talloc_get_type_abort(private_data, + struct smb2_transport_compount_response_state); + struct smb2_transport *transport = state->transport; + NTSTATUS status; + + status = smb2_transport_finish_recv(transport, state->blob); + TALLOC_FREE(state); + if (!NT_STATUS_IS_OK(status)) { + smb2_transport_error(transport, status); + } +} + /* we have a full request in our receive buffer - match it to a pending request and process @@ -226,6 +248,7 @@ static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob) uint32_t i; uint16_t opcode; NTSTATUS status; + uint32_t next_ofs; buffer = blob.data; len = blob.length; @@ -285,6 +308,18 @@ static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob) return NT_STATUS_OK; } + next_ofs = IVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND); + if (next_ofs > 0) { + if (smb2_oob(&req->in, req->in.hdr + next_ofs, SMB2_HDR_BODY + 2)) { + DEBUG(1,("SMB2 request invalid next offset 0x%x\n", + next_ofs)); + goto error; + } + + req->in.size = NBT_HDR_SIZE + next_ofs; + req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE); + } + if (req->session && req->session->signing_active) { status = smb2_check_signature(&req->in, req->session->session_key); @@ -313,6 +348,36 @@ static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob) DEBUG(2, ("SMB2 RECV seqnum=0x%llx\n", (long long)req->seqnum)); dump_data(5, req->in.body, req->in.body_size); + if (next_ofs > 0) { + struct tevent_immediate *im; + struct smb2_transport_compount_response_state *state; + + state = talloc(transport, + struct smb2_transport_compount_response_state); + if (!state) { + goto error; + } + state->transport = transport; + + state->blob = data_blob_talloc(state, NULL, + blob.length - next_ofs); + if (!state->blob.data) { + goto error; + } + im = tevent_create_immediate(state); + if (!im) { + TALLOC_FREE(state); + goto error; + } + _smb2_setlen(state->blob.data, state->blob.length - NBT_HDR_SIZE); + memcpy(state->blob.data + NBT_HDR_SIZE, + req->in.hdr + next_ofs, + req->in.allocated - req->in.size); + tevent_schedule_immediate(im, transport->socket->event.ctx, + smb2_transport_compound_response_handler, + state); + } + /* if this request has an async handler then call that to notify that the reply has been received. This might destroy the request so it must happen last */ @@ -367,25 +432,67 @@ static int smb2_request_destructor(struct smb2_request *req) return 0; } +static NTSTATUS smb2_transport_raw_send(struct smb2_transport *transport, + struct smb2_request_buffer *buffer) +{ + DATA_BLOB blob; + NTSTATUS status; + + /* check if the transport is dead */ + if (transport->socket->sock == NULL) { + return NT_STATUS_NET_WRITE_FAULT; + } + + _smb2_setlen(buffer->buffer, buffer->size - NBT_HDR_SIZE); + blob = data_blob_const(buffer->buffer, buffer->size); + status = packet_send(transport->packet, blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; +} /* put a request into the send queue */ void smb2_transport_send(struct smb2_request *req) { - DATA_BLOB blob; NTSTATUS status; - _smb2_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE); - DEBUG(2, ("SMB2 send seqnum=0x%llx\n", (long long)req->seqnum)); dump_data(5, req->out.body, req->out.body_size); - /* check if the transport is dead */ - if (req->transport->socket->sock == NULL) { - req->state = SMB2_REQUEST_ERROR; - req->status = NT_STATUS_NET_WRITE_FAULT; - return; + if (req->transport->compound.missing > 0) { + off_t next_ofs; + size_t pad = 0; + uint8_t *end; + + end = req->out.buffer + req->out.size; + + /* + * we need to set dynamic otherwise + * smb2_grow_buffer segfaults + */ + if (req->out.dynamic == NULL) { + req->out.dynamic = end; + } + + next_ofs = end - req->out.hdr; + if ((next_ofs % 8) > 0) { + pad = 8 - (next_ofs % 8); + } + next_ofs += pad; + + status = smb2_grow_buffer(&req->out, pad); + if (!NT_STATUS_IS_OK(status)) { + req->state = SMB2_REQUEST_ERROR; + req->status = status; + return; + } + req->out.size += pad; + + SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, next_ofs); } /* possibly sign the message */ @@ -397,14 +504,19 @@ void smb2_transport_send(struct smb2_request *req) return; } } - - blob = data_blob_const(req->out.buffer, req->out.size); - status = packet_send(req->transport->packet, blob); - if (!NT_STATUS_IS_OK(status)) { - req->state = SMB2_REQUEST_ERROR; - req->status = status; - return; + + if (req->transport->compound.missing > 0) { + req->transport->compound.buffer = req->out; + } else { + status = smb2_transport_raw_send(req->transport, + &req->out); + if (!NT_STATUS_IS_OK(status)) { + req->state = SMB2_REQUEST_ERROR; + req->status = status; + return; + } } + ZERO_STRUCT(req->out); req->state = SMB2_REQUEST_RECV; DLIST_ADD(req->transport->pending_recv, req); @@ -419,6 +531,20 @@ void smb2_transport_send(struct smb2_request *req) talloc_set_destructor(req, smb2_request_destructor); } +NTSTATUS smb2_transport_compound_start(struct smb2_transport *transport, + uint32_t num) +{ + ZERO_STRUCT(transport->compound); + transport->compound.missing = num; + return NT_STATUS_OK; +} + +void smb2_transport_compound_set_related(struct smb2_transport *transport, + bool related) +{ + transport->compound.related = related; +} + static void idle_handler(struct tevent_context *ev, struct tevent_timer *te, struct timeval t, void *private_data) { -- cgit