From 300343d16c2d7f1f10bbd3c5e484131bf8fa5dfc Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 20 Sep 2011 20:59:45 +0200 Subject: s4:libcli/smb2: implement on top of smbXcli_conn/req metze --- source4/libcli/smb2/cancel.c | 47 +-- source4/libcli/smb2/connect.c | 90 +---- source4/libcli/smb2/logoff.c | 2 - source4/libcli/smb2/negprot.c | 112 ------ source4/libcli/smb2/read.c | 2 + source4/libcli/smb2/request.c | 50 +-- source4/libcli/smb2/session.c | 209 +++++------ source4/libcli/smb2/smb2.h | 53 +-- source4/libcli/smb2/tcon.c | 1 - source4/libcli/smb2/transport.c | 715 +++++++++++++++--------------------- source4/libcli/smb2/wscript_build | 4 +- source4/libcli/smb_composite/smb2.c | 8 +- source4/librpc/rpc/dcerpc_smb2.c | 5 +- source4/torture/gentest.c | 17 +- source4/torture/smb2/compound.c | 19 +- 15 files changed, 452 insertions(+), 882 deletions(-) delete mode 100644 source4/libcli/smb2/negprot.c diff --git a/source4/libcli/smb2/cancel.c b/source4/libcli/smb2/cancel.c index 9fcb8cffb5..cc40b34a12 100644 --- a/source4/libcli/smb2/cancel.c +++ b/source4/libcli/smb2/cancel.c @@ -20,6 +20,7 @@ */ #include "includes.h" +#include #include "libcli/raw/libcliraw.h" #include "libcli/smb2/smb2.h" #include "libcli/smb2/smb2_calls.h" @@ -29,50 +30,16 @@ */ NTSTATUS smb2_cancel(struct smb2_request *r) { - NTSTATUS status; - struct smb2_request *c; - uint32_t old_timeout; - uint64_t old_seqnum; + bool ok; - /* - * if we don't get a pending id yet, we just - * mark the request for pending, so that we directly - * send the cancel after getting the pending id - */ - if (!r->cancel.can_cancel) { - r->cancel.do_cancel++; + if (r->subreq == NULL) { return NT_STATUS_OK; } - /* we don't want a seqmun for a SMB2 Cancel */ - old_seqnum = r->transport->seqnum; - c = smb2_request_init(r->transport, SMB2_OP_CANCEL, 0x04, false, 0); - r->transport->seqnum = old_seqnum; - NT_STATUS_HAVE_NO_MEMORY(c); - c->seqnum = 0; - - SIVAL(c->out.hdr, SMB2_HDR_FLAGS, 0x00000002); - SSVAL(c->out.hdr, SMB2_HDR_CREDIT, 0x0030); - SBVAL(c->out.hdr, SMB2_HDR_ASYNC_ID, r->cancel.async_id); - SBVAL(c->out.hdr, SMB2_HDR_MESSAGE_ID, c->seqnum); - if (r->session) { - SBVAL(c->out.hdr, SMB2_HDR_SESSION_ID, r->session->uid); - c->session = r->session; - } - - SSVAL(c->out.body, 0x02, 0); - - old_timeout = c->transport->options.request_timeout; - c->transport->options.request_timeout = 0; - smb2_transport_send(c); - c->transport->options.request_timeout = old_timeout; - - if (c->state == SMB2_REQUEST_ERROR) { - status = c->status; - } else { - status = NT_STATUS_OK; + ok = tevent_req_cancel(r->subreq); + if (!ok) { + return NT_STATUS_INTERNAL_ERROR; } - talloc_free(c); - return status; + return NT_STATUS_OK; } diff --git a/source4/libcli/smb2/connect.c b/source4/libcli/smb2/connect.c index 2e837287f5..0d97691a44 100644 --- a/source4/libcli/smb2/connect.c +++ b/source4/libcli/smb2/connect.c @@ -29,6 +29,7 @@ #include "libcli/composite/composite.h" #include "libcli/resolve/resolve.h" #include "param/param.h" +#include "../libcli/smb/smbXcli_base.h" struct smb2_connect_state { struct tevent_context *ev; @@ -40,7 +41,7 @@ struct smb2_connect_state { const char *socket_options; struct gensec_settings *gensec_settings; struct smbcli_options options; - struct smb2_negprot negprot; + struct smb2_transport *transport; struct smb2_tree_connect tcon; struct smb2_session *session; struct smb2_tree *tree; @@ -132,7 +133,7 @@ static void smb2_connect_resolve_done(struct composite_context *creq) creq->async.private_data = req; } -static void smb2_connect_negprot_done(struct smb2_request *smb2req); +static void smb2_connect_negprot_done(struct tevent_req *subreq); static void smb2_connect_socket_done(struct composite_context *creq) { @@ -143,103 +144,52 @@ static void smb2_connect_socket_done(struct composite_context *creq) tevent_req_data(req, struct smb2_connect_state); struct smbcli_socket *sock; - struct smb2_transport *transport; - struct smb2_request *smb2req; + struct tevent_req *subreq; NTSTATUS status; - uint16_t dialects[3] = { - SMB2_DIALECT_REVISION_000, - SMB2_DIALECT_REVISION_202, - SMB2_DIALECT_REVISION_210 - }; + uint32_t timeout_msec; status = smbcli_sock_connect_recv(creq, state, &sock); if (tevent_req_nterror(req, status)) { return; } - transport = smb2_transport_init(sock, state, &state->options); - if (tevent_req_nomem(transport, req)) { + state->transport = smb2_transport_init(sock, state, &state->options); + if (tevent_req_nomem(state->transport, req)) { return; } - ZERO_STRUCT(state->negprot); - state->negprot.in.dialect_count = ARRAY_SIZE(dialects); - switch (transport->options.signing) { - case SMB_SIGNING_OFF: - state->negprot.in.security_mode = 0; - break; - case SMB_SIGNING_DEFAULT: - case SMB_SIGNING_IF_REQUIRED: - state->negprot.in.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; - break; - case SMB_SIGNING_REQUIRED: - state->negprot.in.security_mode = - SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED; - break; - } - state->negprot.in.capabilities = 0; - unix_to_nt_time(&state->negprot.in.start_time, time(NULL)); - state->negprot.in.dialects = dialects; + timeout_msec = state->transport->options.request_timeout * 1000; - smb2req = smb2_negprot_send(transport, &state->negprot); - if (tevent_req_nomem(smb2req, req)) { + subreq = smbXcli_negprot_send(state, state->ev, + state->transport->conn, timeout_msec, + PROTOCOL_SMB2_02, PROTOCOL_SMB2_22); + if (tevent_req_nomem(subreq, req)) { return; } - smb2req->async.fn = smb2_connect_negprot_done; - smb2req->async.private_data = req; + tevent_req_set_callback(subreq, smb2_connect_negprot_done, req); } static void smb2_connect_session_done(struct tevent_req *subreq); -static void smb2_connect_negprot_done(struct smb2_request *smb2req) +static void smb2_connect_negprot_done(struct tevent_req *subreq) { struct tevent_req *req = - talloc_get_type_abort(smb2req->async.private_data, + tevent_req_callback_data(subreq, struct tevent_req); struct smb2_connect_state *state = tevent_req_data(req, struct smb2_connect_state); - struct smb2_transport *transport = smb2req->transport; - struct tevent_req *subreq; + struct smb2_transport *transport = state->transport; NTSTATUS status; - status = smb2_negprot_recv(smb2req, state, &state->negprot); + status = smbXcli_negprot_recv(subreq); + TALLOC_FREE(subreq); if (tevent_req_nterror(req, status)) { return; } - transport->negotiate.secblob = state->negprot.out.secblob; - talloc_steal(transport, transport->negotiate.secblob.data); - transport->negotiate.system_time = state->negprot.out.system_time; - transport->negotiate.server_start_time = state->negprot.out.server_start_time; - transport->negotiate.security_mode = state->negprot.out.security_mode; - transport->negotiate.dialect_revision = state->negprot.out.dialect_revision; - - switch (transport->options.signing) { - case SMB_SIGNING_OFF: - if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) { - tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); - return; - } - transport->signing_required = false; - break; - case SMB_SIGNING_DEFAULT: - case SMB_SIGNING_IF_REQUIRED: - if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) { - transport->signing_required = true; - } else { - transport->signing_required = false; - } - break; - case SMB_SIGNING_REQUIRED: - if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) { - transport->signing_required = true; - } else { - tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); - return; - } - break; - } + /* This is a hack... */ + smb2cli_conn_set_max_credits(transport->conn, 30); state->session = smb2_session_init(transport, state->gensec_settings, state, true); if (tevent_req_nomem(state->session, req)) { diff --git a/source4/libcli/smb2/logoff.c b/source4/libcli/smb2/logoff.c index e3f83f27d8..12cd55311f 100644 --- a/source4/libcli/smb2/logoff.c +++ b/source4/libcli/smb2/logoff.c @@ -35,8 +35,6 @@ struct smb2_request *smb2_logoff_send(struct smb2_session *session) req->session = session; - SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, session->uid); - SSVAL(req->out.body, 0x02, 0); smb2_transport_send(req); diff --git a/source4/libcli/smb2/negprot.c b/source4/libcli/smb2/negprot.c deleted file mode 100644 index 1ff4193499..0000000000 --- a/source4/libcli/smb2/negprot.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - SMB2 client negprot handling - - Copyright (C) Andrew Tridgell 2005 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include "includes.h" -#include "libcli/raw/libcliraw.h" -#include "libcli/raw/raw_proto.h" -#include "libcli/smb2/smb2.h" -#include "libcli/smb2/smb2_calls.h" -#include "librpc/ndr/libndr.h" - -/* - send a negprot request -*/ -struct smb2_request *smb2_negprot_send(struct smb2_transport *transport, - struct smb2_negprot *io) -{ - struct smb2_request *req; - uint16_t size = 0x24 + io->in.dialect_count*2; - int i; - NTSTATUS status; - - req = smb2_request_init(transport, SMB2_OP_NEGPROT, size, false, 0); - if (req == NULL) return NULL; - - - SSVAL(req->out.body, 0x00, 0x24); - SSVAL(req->out.body, 0x02, io->in.dialect_count); - SSVAL(req->out.body, 0x04, io->in.security_mode); - SSVAL(req->out.body, 0x06, io->in.reserved); - SIVAL(req->out.body, 0x08, io->in.capabilities); - status = smbcli_push_guid(req->out.body, 0x0C, &io->in.client_guid); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(req); - return NULL; - } - smbcli_push_nttime(req->out.body, 0x1C, io->in.start_time); - for (i=0;iin.dialect_count;i++) { - SSVAL(req->out.body, 0x24 + i*2, io->in.dialects[i]); - } - - smb2_transport_send(req); - - return req; -} - -/* - recv a negprot reply -*/ -NTSTATUS smb2_negprot_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, - struct smb2_negprot *io) -{ - NTSTATUS status; - - if (!smb2_request_receive(req) || - smb2_request_is_error(req)) { - return smb2_request_destroy(req); - } - - SMB2_CHECK_PACKET_RECV(req, 0x40, true); - - io->out.security_mode = SVAL(req->in.body, 0x02); - io->out.dialect_revision = SVAL(req->in.body, 0x04); - io->out.reserved = SVAL(req->in.body, 0x06); - status = smbcli_pull_guid(req->in.body, 0x08, &io->in.client_guid); - if (!NT_STATUS_IS_OK(status)) { - smb2_request_destroy(req); - return status; - } - io->out.capabilities = IVAL(req->in.body, 0x18); - io->out.max_transact_size = IVAL(req->in.body, 0x1C); - io->out.max_read_size = IVAL(req->in.body, 0x20); - io->out.max_write_size = IVAL(req->in.body, 0x24); - io->out.system_time = smbcli_pull_nttime(req->in.body, 0x28); - io->out.server_start_time = smbcli_pull_nttime(req->in.body, 0x30); - io->out.reserved2 = IVAL(req->in.body, 0x3C); - - status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x38, &io->out.secblob); - if (!NT_STATUS_IS_OK(status)) { - smb2_request_destroy(req); - return status; - } - - return smb2_request_destroy(req); -} - -/* - sync negprot request -*/ -NTSTATUS smb2_negprot(struct smb2_transport *transport, - TALLOC_CTX *mem_ctx, struct smb2_negprot *io) -{ - struct smb2_request *req = smb2_negprot_send(transport, io); - return smb2_negprot_recv(req, mem_ctx, io); -} diff --git a/source4/libcli/smb2/read.c b/source4/libcli/smb2/read.c index 9d40e32a4d..ca487a74a1 100644 --- a/source4/libcli/smb2/read.c +++ b/source4/libcli/smb2/read.c @@ -44,6 +44,8 @@ struct smb2_request *smb2_read_send(struct smb2_tree *tree, struct smb2_read *io SSVAL(req->out.body, 0x2C, io->in.channel_offset); SSVAL(req->out.body, 0x2E, io->in.channel_length); + req->credit_charge = (MAX(io->in.length, 1) - 1)/ 65536 + 1; + smb2_transport_send(req); return req; diff --git a/source4/libcli/smb2/request.c b/source4/libcli/smb2/request.c index cf416a61cf..fae995a396 100644 --- a/source4/libcli/smb2/request.c +++ b/source4/libcli/smb2/request.c @@ -42,18 +42,6 @@ void smb2_setup_bufinfo(struct smb2_request *req) } } - -/* destroy a request structure */ -static int smb2_request_destructor(struct smb2_request *req) -{ - if (req->transport) { - /* remove it from the list of pending requests (a null op if - its not in the list) */ - DLIST_REMOVE(req->transport->pending_recv, req); - } - return 0; -} - /* initialise a smb2 request */ @@ -62,9 +50,7 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_ uint32_t body_dynamic_size) { struct smb2_request *req; - uint64_t seqnum; uint32_t hdr_offset; - uint32_t flags = 0; bool compound = false; if (body_dynamic_present) { @@ -78,36 +64,17 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_ req = talloc(transport, struct smb2_request); if (req == NULL) return NULL; - seqnum = transport->seqnum; - if (transport->credits.charge > 0) { - transport->seqnum += transport->credits.charge; - } else { - transport->seqnum += 1; - } - req->state = SMB2_REQUEST_INIT; req->transport = transport; req->session = NULL; req->tree = NULL; - req->seqnum = seqnum; + req->recv_iov = NULL; req->status = NT_STATUS_OK; req->async.fn = NULL; - req->next = req->prev = NULL; ZERO_STRUCT(req->cancel); ZERO_STRUCT(req->in); - - if (transport->compound.missing > 0) { - compound = true; - 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); - } + ZERO_STRUCT(req->out); if (req->out.size > 0) { hdr_offset = req->out.size; @@ -133,13 +100,13 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_ SIVAL(req->out.hdr, 0, SMB2_MAGIC); SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); - SSVAL(req->out.hdr, SMB2_HDR_EPOCH, transport->credits.charge); + SSVAL(req->out.hdr, SMB2_HDR_CREDIT_CHARGE, 0); SIVAL(req->out.hdr, SMB2_HDR_STATUS, 0); SSVAL(req->out.hdr, SMB2_HDR_OPCODE, opcode); - SSVAL(req->out.hdr, SMB2_HDR_CREDIT, transport->credits.ask_num); - SIVAL(req->out.hdr, SMB2_HDR_FLAGS, flags); + SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0); + SIVAL(req->out.hdr, SMB2_HDR_FLAGS, 0); SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, 0); - SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, req->seqnum); + SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, 0); SIVAL(req->out.hdr, SMB2_HDR_PID, 0); SIVAL(req->out.hdr, SMB2_HDR_TID, 0); SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, 0); @@ -157,8 +124,6 @@ struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_ SCVAL(req->out.dynamic, 0, 0); } - talloc_set_destructor(req, smb2_request_destructor); - return req; } @@ -174,7 +139,6 @@ struct smb2_request *smb2_request_init_tree(struct smb2_tree *tree, uint16_t opc body_dynamic_size); if (req == NULL) return NULL; - SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, tree->session->uid); SIVAL(req->out.hdr, SMB2_HDR_PID, tree->session->pid); SIVAL(req->out.hdr, SMB2_HDR_TID, tree->tid); req->session = tree->session; @@ -214,7 +178,7 @@ bool smb2_request_receive(struct smb2_request *req) /* keep receiving packets until this one is replied to */ while (req->state <= SMB2_REQUEST_RECV) { - if (tevent_loop_once(req->transport->socket->event.ctx) != 0) { + if (tevent_loop_once(req->transport->ev) != 0) { return false; } } diff --git a/source4/libcli/smb2/session.c b/source4/libcli/smb2/session.c index 74b8668462..e35a4734de 100644 --- a/source4/libcli/smb2/session.c +++ b/source4/libcli/smb2/session.c @@ -27,6 +27,8 @@ #include "libcli/smb2/smb2.h" #include "libcli/smb2/smb2_calls.h" #include "auth/gensec/gensec.h" +#include "../libcli/smb/smbXcli_base.h" +#include "../source3/libsmb/smb2cli.h" /** initialise a smb2_session structure @@ -50,8 +52,14 @@ struct smb2_session *smb2_session_init(struct smb2_transport *transport, session->pid = getpid(); + session->smbXcli = smbXcli_session_create(session, transport->conn); + if (session->smbXcli == NULL) { + talloc_free(session); + return NULL; + } + /* prepare a gensec context for later use */ - status = gensec_client_start(session, &session->gensec, + status = gensec_client_start(session, &session->gensec, settings); if (!NT_STATUS_IS_OK(status)) { talloc_free(session); @@ -63,85 +71,16 @@ struct smb2_session *smb2_session_init(struct smb2_transport *transport, return session; } -/** - send a session setup request -*/ -struct smb2_request *smb2_session_setup_send(struct smb2_session *session, - struct smb2_session_setup *io) -{ - struct smb2_request *req; - NTSTATUS status; - - req = smb2_request_init(session->transport, SMB2_OP_SESSSETUP, - 0x18, true, io->in.secblob.length); - if (req == NULL) return NULL; - - SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, session->uid); - SCVAL(req->out.body, 0x02, io->in.vc_number); - SCVAL(req->out.body, 0x03, io->in.security_mode); - SIVAL(req->out.body, 0x04, io->in.capabilities); - SIVAL(req->out.body, 0x08, io->in.channel); - SBVAL(req->out.body, 0x10, io->in.previous_sessionid); - - req->session = session; - - status = smb2_push_o16s16_blob(&req->out, 0x0C, io->in.secblob); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(req); - return NULL; - } - - smb2_transport_send(req); - - return req; -} - - -/** - recv a session setup reply -*/ -NTSTATUS smb2_session_setup_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, - struct smb2_session_setup *io) -{ - NTSTATUS status; - - if (!smb2_request_receive(req) || - (smb2_request_is_error(req) && - !NT_STATUS_EQUAL(req->status, NT_STATUS_MORE_PROCESSING_REQUIRED))) { - return smb2_request_destroy(req); - } - - SMB2_CHECK_PACKET_RECV(req, 0x08, true); - - io->out.session_flags = SVAL(req->in.body, 0x02); - io->out.uid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID); - - status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x04, &io->out.secblob); - if (!NT_STATUS_IS_OK(status)) { - smb2_request_destroy(req); - return status; - } - - return smb2_request_destroy(req); -} - -/* - sync session setup request -*/ -NTSTATUS smb2_session_setup(struct smb2_session *session, - TALLOC_CTX *mem_ctx, struct smb2_session_setup *io) -{ - struct smb2_request *req = smb2_session_setup_send(session, io); - return smb2_session_setup_recv(req, mem_ctx, io); -} - struct smb2_session_setup_spnego_state { - struct smb2_session_setup io; - struct smb2_request *req; + struct tevent_context *ev; + struct smb2_session *session; + struct cli_credentials *credentials; NTSTATUS gensec_status; + DATA_BLOB in_secblob; + DATA_BLOB out_secblob; }; -static void smb2_session_setup_spnego_handler(struct smb2_request *req); +static void smb2_session_setup_spnego_done(struct tevent_req *subreq); /* a composite function that does a full SPNEGO session setup @@ -154,24 +93,27 @@ struct tevent_req *smb2_session_setup_spnego_send(TALLOC_CTX *mem_ctx, struct tevent_req *req; struct smb2_session_setup_spnego_state *state; const char *chosen_oid; - struct smb2_request *subreq; + struct tevent_req *subreq; NTSTATUS status; + const DATA_BLOB *server_gss_blob; + DATA_BLOB negprot_secblob = data_blob_null; + uint32_t timeout_msec; + + timeout_msec = session->transport->options.request_timeout * 1000; req = tevent_req_create(mem_ctx, &state, struct smb2_session_setup_spnego_state); if (req == NULL) { return NULL; } + state->ev = ev; + state->session = session; + state->credentials = credentials; - ZERO_STRUCT(state->io); - state->io.in.vc_number = 0; - if (session->transport->signing_required) { - state->io.in.security_mode = - SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED; + server_gss_blob = smbXcli_conn_server_gss_blob(session->transport->conn); + if (server_gss_blob) { + negprot_secblob = *server_gss_blob; } - state->io.in.capabilities = 0; - state->io.in.channel = 0; - state->io.in.previous_sessionid = 0; status = gensec_set_credentials(session->gensec, credentials); if (tevent_req_nterror(req, status)) { @@ -179,7 +121,7 @@ struct tevent_req *smb2_session_setup_spnego_send(TALLOC_CTX *mem_ctx, } status = gensec_set_target_hostname(session->gensec, - session->transport->socket->hostname); + smbXcli_conn_remote_name(session->transport->conn)); if (tevent_req_nterror(req, status)) { return tevent_req_post(req, ev); } @@ -189,7 +131,7 @@ struct tevent_req *smb2_session_setup_spnego_send(TALLOC_CTX *mem_ctx, return tevent_req_post(req, ev); } - if (session->transport->negotiate.secblob.length > 0) { + if (negprot_secblob.length > 0) { chosen_oid = GENSEC_OID_SPNEGO; } else { chosen_oid = GENSEC_OID_NTLMSSP; @@ -201,21 +143,28 @@ struct tevent_req *smb2_session_setup_spnego_send(TALLOC_CTX *mem_ctx, } status = gensec_update(session->gensec, state, - session->transport->socket->event.ctx, - session->transport->negotiate.secblob, - &state->io.in.secblob); + state->ev, + negprot_secblob, + &state->in_secblob); if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { tevent_req_nterror(req, status); return tevent_req_post(req, ev); } state->gensec_status = status; - subreq = smb2_session_setup_send(session, &state->io); + subreq = smb2cli_session_setup_send(state, state->ev, + session->transport->conn, + timeout_msec, + session->smbXcli, + 0, /* in_flags */ + 0, /* in_capabilities */ + 0, /* in_channel */ + NULL, /* in_previous_session */ + &state->in_secblob); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } - subreq->async.fn = smb2_session_setup_spnego_handler; - subreq->async.private_data = req; + tevent_req_set_callback(subreq, smb2_session_setup_spnego_done, req); return req; } @@ -223,29 +172,38 @@ struct tevent_req *smb2_session_setup_spnego_send(TALLOC_CTX *mem_ctx, /* handle continuations of the spnego session setup */ -static void smb2_session_setup_spnego_handler(struct smb2_request *subreq) +static void smb2_session_setup_spnego_done(struct tevent_req *subreq) { struct tevent_req *req = - talloc_get_type_abort(subreq->async.private_data, + tevent_req_callback_data(subreq, struct tevent_req); struct smb2_session_setup_spnego_state *state = tevent_req_data(req, struct smb2_session_setup_spnego_state); - struct smb2_session *session = subreq->session; + struct smb2_session *session = state->session; NTSTATUS peer_status; NTSTATUS status; + struct iovec *recv_iov; + uint32_t timeout_msec; + + timeout_msec = session->transport->options.request_timeout * 1000; - status = smb2_session_setup_recv(subreq, state, &state->io); + status = smb2cli_session_setup_recv(subreq, state, + &recv_iov, + &state->out_secblob); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + tevent_req_nterror(req, status); + return; + } peer_status = status; - if (NT_STATUS_EQUAL(peer_status, NT_STATUS_MORE_PROCESSING_REQUIRED) || - (NT_STATUS_IS_OK(peer_status) && - NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED))) { + + if (NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { status = gensec_update(session->gensec, state, - session->transport->socket->event.ctx, - state->io.out.secblob, - &state->io.in.secblob); + state->ev, + state->out_secblob, + &state->in_secblob); state->gensec_status = status; - session->uid = state->io.out.uid; } if (!NT_STATUS_IS_OK(status) && @@ -254,30 +212,37 @@ static void smb2_session_setup_spnego_handler(struct smb2_request *subreq) return; } - if (NT_STATUS_EQUAL(peer_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - subreq = smb2_session_setup_send(session, &state->io); - if (tevent_req_nomem(subreq, req)) { + if (NT_STATUS_IS_OK(peer_status) && NT_STATUS_IS_OK(state->gensec_status)) { + status = gensec_session_key(session->gensec, session, + &session->session_key); + if (tevent_req_nterror(req, status)) { return; } - subreq->async.fn = smb2_session_setup_spnego_handler; - subreq->async.private_data = req; - return; - } - - gensec_session_key(session->gensec, session, &session->session_key); - - if (session->transport->signing_required) { - if (session->session_key.length == 0) { - DEBUG(0,("Wrong session key length %u for SMB2 signing\n", - (unsigned)session->session_key.length)); - tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + status = smb2cli_session_update_session_key(session->smbXcli, + session->session_key, + recv_iov); + if (tevent_req_nterror(req, status)) { return; } - session->signing_active = true; + + tevent_req_done(req); + return; } - tevent_req_done(req); + subreq = smb2cli_session_setup_send(state, state->ev, + session->transport->conn, + timeout_msec, + session->smbXcli, + 0, /* in_flags */ + 0, /* in_capabilities */ + 0, /* in_channel */ + NULL, /* in_previous_session */ + &state->in_secblob); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, smb2_session_setup_spnego_done, req); } /* @@ -298,7 +263,7 @@ NTSTATUS smb2_session_setup_spnego(struct smb2_session *session, NTSTATUS status; bool ok; TALLOC_CTX *frame = talloc_stackframe(); - struct tevent_context *ev = session->transport->socket->event.ctx; + struct tevent_context *ev = session->transport->ev; if (frame == NULL) { return NT_STATUS_NO_MEMORY; diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h index aaafedd28f..e8ccdd9dc0 100644 --- a/source4/libcli/smb2/smb2.h +++ b/source4/libcli/smb2/smb2.h @@ -28,17 +28,6 @@ struct smb2_handle; struct smb2_lease_break; -/* - information returned from the negotiate process -*/ -struct smb2_negotiate { - DATA_BLOB secblob; - NTTIME system_time; - NTTIME server_start_time; - uint16_t security_mode; - uint16_t dialect_revision; -}; - struct smb2_request_buffer { /* the raw SMB2 buffer, including the 4 byte length header */ uint8_t *buffer; @@ -70,33 +59,15 @@ struct smb2_request_buffer { /* this is the context for the smb2 transport layer */ struct smb2_transport { - /* socket level info */ - struct smbcli_socket *socket; - - struct smb2_negotiate negotiate; - - /* next seqnum to allocate */ - uint64_t seqnum; + struct tevent_context *ev; /* TODO: remove this !!! */ + struct smbXcli_conn *conn; /* the details for coumpounded requests */ struct { - uint32_t missing; bool related; - struct smb2_request_buffer buffer; + struct tevent_req **reqs; } compound; - struct { - uint16_t charge; - uint16_t ask_num; - } credits; - - /* a list of requests that are pending for receive on this - connection */ - struct smb2_request *pending_recv; - - /* context of the stream -> packet parser */ - struct packet_context *packet; - /* an idle function - if this is defined then it will be called once every period microseconds while we are waiting for a packet */ @@ -123,10 +94,9 @@ struct smb2_transport { /* private data passed to the oplock handler */ void *private_data; } lease; + struct tevent_req *break_subreq; struct smbcli_options options; - - bool signing_required; }; @@ -144,10 +114,9 @@ struct smb2_tree { struct smb2_session { struct smb2_transport *transport; struct gensec_security *gensec; - uint64_t uid; uint32_t pid; DATA_BLOB session_key; - bool signing_active; + struct smbXcli_session *smbXcli; }; @@ -162,22 +131,17 @@ enum smb2_request_state {SMB2_REQUEST_INIT, /* we are creating the request */ /* the context for a single SMB2 request */ struct smb2_request { - /* allow a request to be part of a list of requests */ - struct smb2_request *next, *prev; - /* each request is in one of 3 possible states */ enum smb2_request_state state; + struct tevent_req *subreq; + struct smb2_transport *transport; struct smb2_session *session; struct smb2_tree *tree; - uint64_t seqnum; - struct { - bool do_cancel; bool can_cancel; - uint64_t async_id; } cancel; /* the NT status for this request. Set by packet receive code @@ -186,6 +150,9 @@ struct smb2_request { struct smb2_request_buffer in; struct smb2_request_buffer out; + struct iovec *recv_iov; + + uint16_t credit_charge; /* information on what to do with a reply when it is received asyncronously. If this is not setup when a reply is received then diff --git a/source4/libcli/smb2/tcon.c b/source4/libcli/smb2/tcon.c index ec7152b264..c5e5b99f73 100644 --- a/source4/libcli/smb2/tcon.c +++ b/source4/libcli/smb2/tcon.c @@ -56,7 +56,6 @@ struct smb2_request *smb2_tree_connect_send(struct smb2_tree *tree, 0x08, true, 0); if (req == NULL) return NULL; - SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, tree->session->uid); req->session = tree->session; SSVAL(req->out.body, 0x02, io->in.reserved); diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c index da0fba8e8e..01f363b0a0 100644 --- a/source4/libcli/smb2/transport.c +++ b/source4/libcli/smb2/transport.c @@ -20,6 +20,7 @@ */ #include "includes.h" +#include "system/network.h" #include "libcli/raw/libcliraw.h" #include "libcli/raw/raw_proto.h" #include "libcli/smb2/smb2.h" @@ -28,25 +29,7 @@ #include "lib/events/events.h" #include "lib/stream/packet.h" #include "../lib/util/dlinklist.h" - - -/* - an event has happened on the socket -*/ -static void smb2_transport_event_handler(struct tevent_context *ev, - struct tevent_fd *fde, - uint16_t flags, void *private_data) -{ - struct smb2_transport *transport = talloc_get_type(private_data, - struct smb2_transport); - if (flags & TEVENT_FD_READ) { - packet_recv(transport->packet); - return; - } - if (flags & TEVENT_FD_WRITE) { - packet_queue_run(transport->packet); - } -} +#include "../libcli/smb/smbXcli_base.h" /* destroy a transport @@ -57,19 +40,6 @@ static int transport_destructor(struct smb2_transport *transport) return 0; } - -/* - handle receive errors -*/ -static void smb2_transport_error(void *private_data, NTSTATUS status) -{ - struct smb2_transport *transport = talloc_get_type(private_data, - struct smb2_transport); - smb2_transport_dead(transport, status); -} - -static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob); - /* create a transport structure based on an established socket */ @@ -82,37 +52,24 @@ struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock, transport = talloc_zero(parent_ctx, struct smb2_transport); if (!transport) return NULL; - transport->socket = talloc_steal(transport, sock); + transport->ev = sock->event.ctx; transport->options = *options; - transport->credits.charge = 0; - transport->credits.ask_num = 1; - /* setup the stream -> packet parser */ - transport->packet = packet_init(transport); - if (transport->packet == NULL) { + TALLOC_FREE(sock->event.fde); + TALLOC_FREE(sock->event.te); + + transport->conn = smbXcli_conn_create(transport, + sock->sock->fd, + sock->hostname, + options->signing, + 0, /* smb1_capabilities */ + NULL); /* client_guid */ + if (transport->conn == NULL) { talloc_free(transport); return NULL; } - packet_set_private(transport->packet, transport); - packet_set_socket(transport->packet, transport->socket->sock); - packet_set_callback(transport->packet, smb2_transport_finish_recv); - packet_set_full_request(transport->packet, packet_full_request_nbt); - packet_set_error_handler(transport->packet, smb2_transport_error); - packet_set_event_context(transport->packet, transport->socket->event.ctx); - packet_set_nofree(transport->packet); - - /* take over event handling from the socket layer - it only - handles events up until we are connected */ - talloc_free(transport->socket->event.fde); - transport->socket->event.fde = tevent_add_fd(transport->socket->event.ctx, - transport->socket, - socket_get_fd(transport->socket->sock), - TEVENT_FD_READ, - smb2_transport_event_handler, - transport); - - packet_set_fde(transport->packet, transport->socket->event.fde); - packet_set_serialise(transport->packet); + sock->sock->fd = -1; + TALLOC_FREE(sock); talloc_set_destructor(transport, transport_destructor); @@ -124,431 +81,343 @@ struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock, */ void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status) { - smbcli_sock_dead(transport->socket); - if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) { status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; } - - /* kill all pending receives */ - while (transport->pending_recv) { - struct smb2_request *req = transport->pending_recv; - req->state = SMB2_REQUEST_ERROR; - req->status = status; - DLIST_REMOVE(transport->pending_recv, req); - if (req->async.fn) { - req->async.fn(req); - } + if (NT_STATUS_IS_OK(status)) { + status = NT_STATUS_LOCAL_DISCONNECT; } -} -static NTSTATUS smb2_handle_oplock_break(struct smb2_transport *transport, - const DATA_BLOB *blob) -{ - uint8_t *hdr; - uint8_t *body; - uint16_t len, bloblen; - bool lease; + smbXcli_conn_disconnect(transport->conn, status); +} - hdr = blob->data+NBT_HDR_SIZE; - body = hdr+SMB2_HDR_BODY; - bloblen = blob->length - SMB2_HDR_BODY; +static void smb2_request_done(struct tevent_req *subreq); +static void smb2_transport_break_handler(struct tevent_req *subreq); - if (bloblen < 2) { - DEBUG(1,("Discarding smb2 oplock reply of size %u\n", - (unsigned)blob->length)); - return NT_STATUS_INVALID_NETWORK_RESPONSE; +/* + put a request into the send queue +*/ +void smb2_transport_send(struct smb2_request *req) +{ + NTSTATUS status; + struct smb2_transport *transport = req->transport; + struct tevent_req **reqs = transport->compound.reqs; + size_t num_reqs = talloc_array_length(reqs); + size_t i; + uint16_t cmd = SVAL(req->out.hdr, SMB2_HDR_OPCODE); + uint32_t additional_flags = IVAL(req->out.hdr, SMB2_HDR_FLAGS); + uint32_t clear_flags = 0; + uint32_t pid = IVAL(req->out.hdr, SMB2_HDR_PID); + uint32_t tid = IVAL(req->out.hdr, SMB2_HDR_TID); + struct smbXcli_session *session = NULL; + bool need_pending_break = false; + size_t hdr_ofs; + size_t pdu_len; + DATA_BLOB body = data_blob_null; + DATA_BLOB dyn = data_blob_null; + uint32_t timeout_msec = transport->options.request_timeout * 1000; + + if (transport->oplock.handler) { + need_pending_break = true; + } + + if (transport->lease.handler) { + need_pending_break = true; + } + + if (transport->break_subreq) { + need_pending_break = false; + } + + if (need_pending_break) { + struct tevent_req *subreq; + + subreq = smb2cli_req_create(transport, + transport->ev, + transport->conn, + SMB2_OP_BREAK, + 0, /* additional_flags */ + 0, /*clear_flags */ + 0, /* timeout_msec */ + 0, /* pid */ + 0, /* tid */ + NULL, /* session */ + NULL, /* body */ + 0, /* body_fixed */ + NULL, /* dyn */ + 0); /* dyn_len */ + if (subreq != NULL) { + smbXcli_req_set_pending(subreq); + tevent_req_set_callback(subreq, + smb2_transport_break_handler, + transport); + transport->break_subreq = subreq; + } } - len = CVAL(body, 0x00); - if (len > bloblen) { - DEBUG(1,("Discarding smb2 oplock reply," - "packet claims %u byte body, only %u bytes seen\n", - len, bloblen)); - return NT_STATUS_INVALID_NETWORK_RESPONSE; + if (req->session) { + session = req->session->smbXcli; } - if (len == 24) { - lease = false; - } else if (len == 44) { - lease = true; - } else { - DEBUG(1,("Discarding smb2 oplock reply of invalid size %u\n", - (unsigned)blob->length)); - return NT_STATUS_INVALID_NETWORK_RESPONSE; + if (transport->compound.related) { + additional_flags |= SMB2_HDR_FLAG_CHAINED; } - if (!lease && transport->oplock.handler) { - struct smb2_handle h; - uint8_t level; - - level = CVAL(body, 0x02); - smb2_pull_handle(body+0x08, &h); - - transport->oplock.handler(transport, &h, level, - transport->oplock.private_data); - } else if (lease && transport->lease.handler) { - struct smb2_lease_break lb; + hdr_ofs = PTR_DIFF(req->out.hdr, req->out.buffer); + pdu_len = req->out.size - hdr_ofs; + body.data = req->out.body; + body.length = req->out.body_fixed; + dyn.data = req->out.body + req->out.body_fixed; + dyn.length = pdu_len - (SMB2_HDR_BODY + req->out.body_fixed); - ZERO_STRUCT(lb); - lb.break_flags = SVAL(body, 0x4); - memcpy(&lb.current_lease.lease_key, body+0x8, - sizeof(struct smb2_lease_key)); - lb.current_lease.lease_state = SVAL(body, 0x18); - lb.new_lease_state = SVAL(body, 0x1C); - lb.break_reason = SVAL(body, 0x20); - lb.access_mask_hint = SVAL(body, 0x24); - lb.share_mask_hint = SVAL(body, 0x28); - - transport->lease.handler(transport, &lb, - transport->lease.private_data); - } else { - DEBUG(5,("Got SMB2 %s break with no handler\n", - lease ? "lease" : "oplock")); + req->subreq = smb2cli_req_create(req, + transport->ev, + transport->conn, + cmd, + additional_flags, + clear_flags, + timeout_msec, + pid, + tid, + session, + body.data, body.length, + dyn.data, dyn.length); + if (req->subreq == NULL) { + req->state = SMB2_REQUEST_ERROR; + req->status = NT_STATUS_NO_MEMORY; + return; } - 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); + if (!tevent_req_is_in_progress(req->subreq)) { + req->state = SMB2_REQUEST_ERROR; + req->status = NT_STATUS_INTERNAL_ERROR;/* TODO */ + return; } -} -/* - we have a full request in our receive buffer - match it to a pending request - and process - */ -static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob) -{ - struct smb2_transport *transport = talloc_get_type(private_data, - struct smb2_transport); - uint8_t *buffer, *hdr; - int len; - struct smb2_request *req = NULL; - uint64_t seqnum; - uint32_t flags; - uint16_t buffer_code; - uint32_t dynamic_size; - uint32_t i; - uint16_t opcode; - NTSTATUS status; - uint32_t next_ofs; - - buffer = blob.data; - len = blob.length; - - hdr = buffer+NBT_HDR_SIZE; + tevent_req_set_callback(req->subreq, smb2_request_done, req); - if (len < SMB2_MIN_SIZE) { - DEBUG(1,("Discarding smb2 reply of size %d\n", len)); - goto error; + smb2cli_req_set_notify_async(req->subreq); + if (req->credit_charge) { + smb2cli_req_set_credit_charge(req->subreq, req->credit_charge); } - flags = IVAL(hdr, SMB2_HDR_FLAGS); - seqnum = BVAL(hdr, SMB2_HDR_MESSAGE_ID); - opcode = SVAL(hdr, SMB2_HDR_OPCODE); + ZERO_STRUCT(req->out); + req->state = SMB2_REQUEST_RECV; - /* see MS-SMB2 3.2.5.19 */ - if (seqnum == UINT64_MAX) { - if (opcode != SMB2_OP_BREAK) { - DEBUG(1,("Discarding packet with invalid seqnum, " - "opcode %u\n", opcode)); - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } + if (num_reqs > 0) { + for (i=0; i < num_reqs; i++) { + if (reqs[i] != NULL) { + continue; + } - return smb2_handle_oplock_break(transport, &blob); - } + reqs[i] = req->subreq; + i++; + break; + } - if (opcode == SMB2_OP_CANCEL) { - /* - * ignore responses to cancel requests, - * this can happen if signing was wrong or - * we specified the wrong session id - */ - talloc_free(buffer); - return NT_STATUS_OK; + if (i < num_reqs) { + return; + } + } else { + reqs = &req->subreq; + num_reqs = 1; } + status = smb2cli_req_compound_submit(reqs, num_reqs); - /* match the incoming request against the list of pending requests */ - for (req=transport->pending_recv; req; req=req->next) { - if (req->seqnum == seqnum) break; - } + TALLOC_FREE(transport->compound.reqs); - if (!req) { - DEBUG(1,("Discarding unmatched reply with seqnum 0x%llx op %d\n", - (long long)seqnum, SVAL(hdr, SMB2_HDR_OPCODE))); - goto error; + if (!NT_STATUS_IS_OK(status)) { + smbXcli_conn_disconnect(transport->conn, status); } +} - /* fill in the 'in' portion of the matching request */ - req->in.buffer = buffer; - talloc_steal(req, buffer); - req->in.size = len; - req->in.allocated = req->in.size; +static void smb2_request_done(struct tevent_req *subreq) +{ + struct smb2_request *req = + tevent_req_callback_data(subreq, + struct smb2_request); + ssize_t len; + size_t i; - req->in.hdr = hdr; - req->in.body = hdr+SMB2_HDR_BODY; - req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE); - req->status = NT_STATUS(IVAL(hdr, SMB2_HDR_STATUS)); + req->recv_iov = NULL; - if ((flags & SMB2_HDR_FLAG_ASYNC) && - NT_STATUS_EQUAL(req->status, STATUS_PENDING)) { + req->status = smb2cli_req_recv(req->subreq, req, &req->recv_iov, NULL, 0); + if (NT_STATUS_EQUAL(req->status, STATUS_PENDING)) { req->cancel.can_cancel = true; - req->cancel.async_id = BVAL(hdr, SMB2_HDR_ASYNC_ID); - for (i=0; i< req->cancel.do_cancel; i++) { - smb2_cancel(req); - } - talloc_free(buffer); - 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 && - !NT_STATUS_EQUAL(req->status, NT_STATUS_USER_SESSION_DELETED)) { - status = smb2_check_signature(&req->in, - req->session->session_key); - if (!NT_STATUS_IS_OK(status)) { - /* the spec says to ignore packets with a bad signature */ - talloc_free(buffer); - return status; - } + return; } - - buffer_code = SVAL(req->in.body, 0); - req->in.body_fixed = (buffer_code & ~1); - req->in.dynamic = NULL; - dynamic_size = req->in.body_size - req->in.body_fixed; - if (dynamic_size != 0 && (buffer_code & 1)) { - req->in.dynamic = req->in.body + req->in.body_fixed; - if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) { - DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n", - dynamic_size)); - goto error; + TALLOC_FREE(req->subreq); + if (!NT_STATUS_IS_OK(req->status)) { + if (req->recv_iov == NULL) { + req->state = SMB2_REQUEST_ERROR; + if (req->async.fn) { + req->async.fn(req); + } + return; } } - smb2_setup_bufinfo(req); - - DEBUG(2, ("SMB2 RECV seqnum=0x%llx\n", (long long)req->seqnum)); - dump_data(5, req->in.body, req->in.body_size); + len = req->recv_iov[0].iov_len; + for (i=1; i < 3; i++) { + uint8_t *p = req->recv_iov[i-1].iov_base; + uint8_t *c1 = req->recv_iov[i].iov_base; + uint8_t *c2 = p + req->recv_iov[i-1].iov_len; - if (next_ofs > 0) { - struct tevent_immediate *im; - struct smb2_transport_compount_response_state *state; + len += req->recv_iov[i].iov_len; - state = talloc(transport, - struct smb2_transport_compount_response_state); - if (!state) { - goto error; + if (req->recv_iov[i].iov_len == 0) { + continue; } - 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; + if (c1 != c2) { + req->status = NT_STATUS_INTERNAL_ERROR; + req->state = SMB2_REQUEST_ERROR; + if (req->async.fn) { + req->async.fn(req); + } + return; } - _smb_setlen_tcp(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 */ - DLIST_REMOVE(transport->pending_recv, req); - req->state = SMB2_REQUEST_DONE; - if (req->async.fn) { - req->async.fn(req); - } - return NT_STATUS_OK; + req->in.buffer = req->recv_iov[0].iov_base; + req->in.size = len; + req->in.allocated = req->in.size; -error: - dump_data(5, buffer, len); - if (req) { - DLIST_REMOVE(transport->pending_recv, req); - req->state = SMB2_REQUEST_ERROR; - if (req->async.fn) { - req->async.fn(req); - } - } else { - talloc_free(buffer); - } - return NT_STATUS_UNSUCCESSFUL; -} + req->in.hdr = req->recv_iov[0].iov_base; + req->in.body = req->recv_iov[1].iov_base; + req->in.dynamic = req->recv_iov[2].iov_base; + req->in.body_fixed = req->recv_iov[1].iov_len; + req->in.body_size = req->in.body_fixed; + req->in.body_size += req->recv_iov[2].iov_len; -/* - handle timeouts of individual smb requests -*/ -static void smb2_timeout_handler(struct tevent_context *ev, struct tevent_timer *te, - struct timeval t, void *private_data) -{ - struct smb2_request *req = talloc_get_type(private_data, struct smb2_request); + smb2_setup_bufinfo(req); - if (req->state == SMB2_REQUEST_RECV) { - DLIST_REMOVE(req->transport->pending_recv, req); - } - req->status = NT_STATUS_IO_TIMEOUT; - req->state = SMB2_REQUEST_ERROR; + req->state = SMB2_REQUEST_DONE; if (req->async.fn) { req->async.fn(req); } } - -/* - destroy a request -*/ -static int smb2_request_destructor(struct smb2_request *req) -{ - if (req->state == SMB2_REQUEST_RECV) { - DLIST_REMOVE(req->transport->pending_recv, req); - } - return 0; -} - -static NTSTATUS smb2_transport_raw_send(struct smb2_transport *transport, - struct smb2_request_buffer *buffer) +static void smb2_transport_break_handler(struct tevent_req *subreq) { - DATA_BLOB blob; + struct smb2_transport *transport = + tevent_req_callback_data(subreq, + struct smb2_transport); NTSTATUS status; + uint8_t *hdr; + uint8_t *body; + uint16_t len = 0; + bool lease; + struct iovec *recv_iov = NULL; - /* check if the transport is dead */ - if (transport->socket->sock == NULL) { - return NT_STATUS_NET_WRITE_FAULT; - } + transport->break_subreq = NULL; - _smb_setlen_tcp(buffer->buffer, buffer->size - NBT_HDR_SIZE); - blob = data_blob_const(buffer->buffer, buffer->size); - status = packet_send(transport->packet, blob); + status = smb2cli_req_recv(subreq, transport, &recv_iov, NULL, 0); + TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { - return status; + TALLOC_FREE(recv_iov); + smb2_transport_dead(transport, status); + return; } - return NT_STATUS_OK; -} - -/* - put a request into the send queue -*/ -void smb2_transport_send(struct smb2_request *req) -{ - NTSTATUS status; - - DEBUG(2, ("SMB2 send seqnum=0x%llx\n", (long long)req->seqnum)); - dump_data(5, req->out.body, req->out.body_size); + /* + * Setup the subreq to handle the + * next incoming SMB2 Break. + */ + subreq = smb2cli_req_create(transport, + transport->ev, + transport->conn, + SMB2_OP_BREAK, + 0, /* additional_flags */ + 0, /*clear_flags */ + 0, /* timeout_msec */ + 0, /* pid */ + 0, /* tid */ + NULL, /* session */ + NULL, /* body */ + 0, /* body_fixed */ + NULL, /* dyn */ + 0); /* dyn_len */ + if (subreq != NULL) { + smbXcli_req_set_pending(subreq); + tevent_req_set_callback(subreq, + smb2_transport_break_handler, + transport); + transport->break_subreq = subreq; + } + + hdr = recv_iov[0].iov_base; + body = recv_iov[1].iov_base; + + len = recv_iov[1].iov_len; + if (recv_iov[1].iov_len >= 2) { + len = CVAL(body, 0x00); + if (len != recv_iov[1].iov_len) { + len = recv_iov[1].iov_len; + } + } - if (req->transport->compound.missing > 0) { - off_t next_ofs; - size_t pad = 0; - uint8_t *end; + if (len == 24) { + lease = false; + } else if (len == 44) { + lease = true; + } else { + DEBUG(1,("Discarding smb2 oplock reply of invalid size %u\n", + (unsigned)len)); + TALLOC_FREE(recv_iov); + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + smb2_transport_dead(transport, status); + return; + } - end = req->out.buffer + req->out.size; + if (!lease && transport->oplock.handler) { + struct smb2_handle h; + uint8_t level; - /* - * we need to set dynamic otherwise - * smb2_grow_buffer segfaults - */ - if (req->out.dynamic == NULL) { - req->out.dynamic = end; - } + level = CVAL(body, 0x02); + smb2_pull_handle(body+0x08, &h); - next_ofs = end - req->out.hdr; - if ((next_ofs % 8) > 0) { - pad = 8 - (next_ofs % 8); - } - next_ofs += pad; + TALLOC_FREE(recv_iov); - 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; + transport->oplock.handler(transport, &h, level, + transport->oplock.private_data); + } else if (lease && transport->lease.handler) { + struct smb2_lease_break lb; - SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, next_ofs); - } + ZERO_STRUCT(lb); + lb.break_flags = SVAL(body, 0x4); + memcpy(&lb.current_lease.lease_key, body+0x8, + sizeof(struct smb2_lease_key)); + lb.current_lease.lease_state = SVAL(body, 0x18); + lb.new_lease_state = SVAL(body, 0x1C); + lb.break_reason = SVAL(body, 0x20); + lb.access_mask_hint = SVAL(body, 0x24); + lb.share_mask_hint = SVAL(body, 0x28); - /* possibly sign the message */ - if (req->session && req->session->signing_active) { - status = smb2_sign_message(&req->out, req->session->session_key); - if (!NT_STATUS_IS_OK(status)) { - req->state = SMB2_REQUEST_ERROR; - req->status = status; - return; - } - } + TALLOC_FREE(recv_iov); - if (req->transport->compound.missing > 0) { - req->transport->compound.buffer = req->out; + transport->lease.handler(transport, &lb, + transport->lease.private_data); } 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); - - /* add a timeout */ - if (req->transport->options.request_timeout) { - tevent_add_timer(req->transport->socket->event.ctx, req, - timeval_current_ofs(req->transport->options.request_timeout, 0), - smb2_timeout_handler, req); + DEBUG(5,("Got SMB2 %s break with no handler\n", + lease ? "lease" : "oplock")); } - - talloc_set_destructor(req, smb2_request_destructor); + TALLOC_FREE(recv_iov); } NTSTATUS smb2_transport_compound_start(struct smb2_transport *transport, uint32_t num) { + TALLOC_FREE(transport->compound.reqs); ZERO_STRUCT(transport->compound); - transport->compound.missing = num; + + transport->compound.reqs = talloc_zero_array(transport, + struct tevent_req *, + num); + if (transport->compound.reqs == NULL) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; } @@ -561,13 +430,7 @@ void smb2_transport_compound_set_related(struct smb2_transport *transport, void smb2_transport_credits_ask_num(struct smb2_transport *transport, uint16_t ask_num) { - transport->credits.ask_num = ask_num; -} - -void smb2_transport_credits_set_charge(struct smb2_transport *transport, - uint16_t charge) -{ - transport->credits.charge = charge; + smb2cli_conn_set_max_credits(transport->conn, ask_num); } static void idle_handler(struct tevent_context *ev, @@ -576,10 +439,10 @@ static void idle_handler(struct tevent_context *ev, struct smb2_transport *transport = talloc_get_type(private_data, struct smb2_transport); struct timeval next = timeval_add(&t, 0, transport->idle.period); - transport->socket->event.te = tevent_add_timer(transport->socket->event.ctx, - transport, - next, - idle_handler, transport); + tevent_add_timer(transport->ev, + transport, + next, + idle_handler, transport); transport->idle.func(transport, transport->idle.private_data); } @@ -596,12 +459,8 @@ void smb2_transport_idle_handler(struct smb2_transport *transport, transport->idle.private_data = private_data; transport->idle.period = period; - if (transport->socket->event.te != NULL) { - talloc_free(transport->socket->event.te); - } - - transport->socket->event.te = tevent_add_timer(transport->socket->event.ctx, - transport, - timeval_current_ofs(0, period), - idle_handler, transport); + tevent_add_timer(transport->ev, + transport, + timeval_current_ofs(0, period), + idle_handler, transport); } diff --git a/source4/libcli/smb2/wscript_build b/source4/libcli/smb2/wscript_build index 8aac8272e3..d2478a4497 100644 --- a/source4/libcli/smb2/wscript_build +++ b/source4/libcli/smb2/wscript_build @@ -1,9 +1,9 @@ #!/usr/bin/env python bld.SAMBA_SUBSYSTEM('LIBCLI_SMB2', - source='transport.c request.c negprot.c session.c tcon.c create.c close.c connect.c getinfo.c write.c read.c setinfo.c find.c ioctl.c logoff.c tdis.c flush.c lock.c notify.c cancel.c keepalive.c break.c util.c signing.c lease_break.c', + source='transport.c request.c session.c tcon.c create.c close.c connect.c getinfo.c write.c read.c setinfo.c find.c ioctl.c logoff.c tdis.c flush.c lock.c notify.c cancel.c keepalive.c break.c util.c signing.c lease_break.c', autoproto='smb2_proto.h', - deps='tevent-util', + deps='tevent-util libsmb', public_deps='smbclient-raw LIBPACKET gensec tevent' ) diff --git a/source4/libcli/smb_composite/smb2.c b/source4/libcli/smb_composite/smb2.c index 5c93869b5c..0fa51b2857 100644 --- a/source4/libcli/smb_composite/smb2.c +++ b/source4/libcli/smb_composite/smb2.c @@ -82,7 +82,7 @@ struct composite_context *smb2_composite_unlink_send(struct smb2_tree *tree, struct smb2_create create_parm; struct smb2_request *req; - ctx = composite_create(tree, tree->session->transport->socket->event.ctx); + ctx = composite_create(tree, tree->session->transport->ev); if (ctx == NULL) return NULL; /* check for wildcards - we could support these with a @@ -161,7 +161,7 @@ struct composite_context *smb2_composite_mkdir_send(struct smb2_tree *tree, struct smb2_create create_parm; struct smb2_request *req; - ctx = composite_create(tree, tree->session->transport->socket->event.ctx); + ctx = composite_create(tree, tree->session->transport->ev); if (ctx == NULL) return NULL; ZERO_STRUCT(create_parm); @@ -232,7 +232,7 @@ struct composite_context *smb2_composite_rmdir_send(struct smb2_tree *tree, struct smb2_create create_parm; struct smb2_request *req; - ctx = composite_create(tree, tree->session->transport->socket->event.ctx); + ctx = composite_create(tree, tree->session->transport->ev); if (ctx == NULL) return NULL; ZERO_STRUCT(create_parm); @@ -416,7 +416,7 @@ NTSTATUS smb2_composite_setpathinfo(struct smb2_tree *tree, union smb_setfileinf NTSTATUS status; bool ok; TALLOC_CTX *frame = talloc_stackframe(); - struct tevent_context *ev = tree->session->transport->socket->event.ctx; + struct tevent_context *ev = tree->session->transport->ev; if (frame == NULL) { return NT_STATUS_NO_MEMORY; diff --git a/source4/librpc/rpc/dcerpc_smb2.c b/source4/librpc/rpc/dcerpc_smb2.c index 0de8935797..ecc250412a 100644 --- a/source4/librpc/rpc/dcerpc_smb2.c +++ b/source4/librpc/rpc/dcerpc_smb2.c @@ -28,6 +28,7 @@ #include "librpc/rpc/dcerpc.h" #include "librpc/rpc/dcerpc_proto.h" #include "librpc/rpc/rpc_common.h" +#include "../libcli/smb/smbXcli_base.h" /* transport private information used by SMB2 pipe transport */ struct smb2_private { @@ -377,7 +378,7 @@ static const char *smb2_target_hostname(struct dcecli_connection *c) { struct smb2_private *smb = talloc_get_type(c->transport.private_data, struct smb2_private); - return smb->tree->session->transport->socket->hostname; + return smbXcli_conn_remote_name(smb->tree->session->transport->conn); } /* @@ -488,7 +489,7 @@ static void pipe_open_recv(struct smb2_request *req) smb->handle = io.out.file.handle; smb->tree = talloc_reference(smb, tree); smb->server_name= strupper_talloc(smb, - tree->session->transport->socket->hostname); + smbXcli_conn_remote_name(tree->session->transport->conn)); if (composite_nomem(smb->server_name, ctx)) return; smb->dead = false; diff --git a/source4/torture/gentest.c b/source4/torture/gentest.c index 9b6e7fc0fd..c7e32de359 100644 --- a/source4/torture/gentest.c +++ b/source4/torture/gentest.c @@ -38,6 +38,7 @@ #include "dynconfig/dynconfig.h" #include "libcli/security/security.h" #include "libcli/raw/raw_proto.h" +#include "../libcli/smb/smbXcli_base.h" #define NSERVERS 2 #define NINSTANCES 2 @@ -279,13 +280,21 @@ static bool connect_servers(struct tevent_context *ev, static unsigned int time_skew(void) { unsigned int ret; + NTTIME nt0, nt1; + if (options.smb2) { - ret = labs(servers[0].smb2_tree[0]->session->transport->negotiate.system_time - - servers[1].smb2_tree[0]->session->transport->negotiate.system_time); + struct smbXcli_conn *c0, *c1; + + c0 = servers[0].smb2_tree[0]->session->transport->conn; + c1 = servers[1].smb2_tree[0]->session->transport->conn; + + nt0 = smbXcli_conn_server_system_time(c0); + nt1 = smbXcli_conn_server_system_time(c1); } else { - ret = labs(servers[0].smb_tree[0]->session->transport->negotiate.server_time - - servers[1].smb_tree[0]->session->transport->negotiate.server_time); + nt0 = servers[0].smb_tree[0]->session->transport->negotiate.server_time; + nt1 = servers[1].smb_tree[0]->session->transport->negotiate.server_time; } + ret = labs(nt0 - nt1); return ret + 300; } diff --git a/source4/torture/smb2/compound.c b/source4/torture/smb2/compound.c index 1caa688a35..cb5b744c5d 100644 --- a/source4/torture/smb2/compound.c +++ b/source4/torture/smb2/compound.c @@ -24,6 +24,7 @@ #include "libcli/smb2/smb2_calls.h" #include "torture/torture.h" #include "torture/smb2/proto.h" +#include "../libcli/smb/smbXcli_base.h" #define CHECK_STATUS(status, correct) do { \ if (!NT_STATUS_EQUAL(status, correct)) { \ @@ -47,7 +48,7 @@ static bool test_compound_related1(struct torture_context *tctx, bool ret = true; struct smb2_request *req[2]; uint32_t saved_tid = tree->tid; - uint64_t saved_uid = tree->session->uid; + uint64_t saved_uid = smb2cli_session_current_id(tree->session->smbXcli); smb2_transport_credits_ask_num(tree->session->transport, 2); @@ -86,7 +87,7 @@ static bool test_compound_related1(struct torture_context *tctx, cl.in.file.handle = hd; tree->tid = 0xFFFFFFFF; - tree->session->uid = UINT64_MAX; + smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0); req[1] = smb2_close_send(tree, &cl); @@ -96,7 +97,7 @@ static bool test_compound_related1(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); tree->tid = saved_tid; - tree->session->uid = saved_uid; + smb2cli_session_set_id_and_flags(tree->session->smbXcli, saved_uid, 0); smb2_util_unlink(tree, fname); done: @@ -114,7 +115,7 @@ static bool test_compound_related2(struct torture_context *tctx, bool ret = true; struct smb2_request *req[5]; uint32_t saved_tid = tree->tid; - uint64_t saved_uid = tree->session->uid; + uint64_t saved_uid = smb2cli_session_current_id(tree->session->smbXcli); smb2_transport_credits_ask_num(tree->session->transport, 5); @@ -152,7 +153,7 @@ static bool test_compound_related2(struct torture_context *tctx, ZERO_STRUCT(cl); cl.in.file.handle = hd; tree->tid = 0xFFFFFFFF; - tree->session->uid = UINT64_MAX; + smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0); req[1] = smb2_close_send(tree, &cl); req[2] = smb2_close_send(tree, &cl); @@ -171,7 +172,7 @@ static bool test_compound_related2(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); tree->tid = saved_tid; - tree->session->uid = saved_uid; + smb2cli_session_set_id_and_flags(tree->session->smbXcli, saved_uid, 0); smb2_util_unlink(tree, fname); done: @@ -314,7 +315,7 @@ static bool test_compound_invalid2(struct torture_context *tctx, bool ret = true; struct smb2_request *req[5]; uint32_t saved_tid = tree->tid; - uint64_t saved_uid = tree->session->uid; + uint64_t saved_uid = smb2cli_session_current_id(tree->session->smbXcli); smb2_transport_credits_ask_num(tree->session->transport, 5); @@ -352,7 +353,7 @@ static bool test_compound_invalid2(struct torture_context *tctx, ZERO_STRUCT(cl); cl.in.file.handle = hd; tree->tid = 0xFFFFFFFF; - tree->session->uid = UINT64_MAX; + smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0); req[1] = smb2_close_send(tree, &cl); /* strange that this is not generating invalid parameter */ @@ -374,7 +375,7 @@ static bool test_compound_invalid2(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); tree->tid = saved_tid; - tree->session->uid = saved_uid; + smb2cli_session_set_id_and_flags(tree->session->smbXcli, saved_uid, 0); smb2_util_unlink(tree, fname); done: -- cgit