diff options
Diffstat (limited to 'source4/libcli/smb2/connect.c')
-rw-r--r-- | source4/libcli/smb2/connect.c | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/source4/libcli/smb2/connect.c b/source4/libcli/smb2/connect.c new file mode 100644 index 0000000000..43151943d3 --- /dev/null +++ b/source4/libcli/smb2/connect.c @@ -0,0 +1,291 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 composite connection setup + + 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 <http://www.gnu.org/licenses/>. +*/ + +#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 "libcli/composite/composite.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" + +struct smb2_connect_state { + struct cli_credentials *credentials; + struct resolve_context *resolve_ctx; + const char *host; + const char *share; + struct smbcli_options options; + struct smb2_negprot negprot; + struct smb2_tree_connect tcon; + struct smb2_session *session; + struct smb2_tree *tree; +}; + +/* + continue after tcon reply +*/ +static void continue_tcon(struct smb2_request *req) +{ + struct composite_context *c = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_connect_state *state = talloc_get_type(c->private_data, + struct smb2_connect_state); + + c->status = smb2_tree_connect_recv(req, &state->tcon); + if (!composite_is_ok(c)) return; + + state->tree->tid = state->tcon.out.tid; + + composite_done(c); +} + +/* + continue after a session setup +*/ +static void continue_session(struct composite_context *creq) +{ + struct composite_context *c = talloc_get_type(creq->async.private_data, + struct composite_context); + struct smb2_connect_state *state = talloc_get_type(c->private_data, + struct smb2_connect_state); + struct smb2_request *req; + + c->status = smb2_session_setup_spnego_recv(creq); + if (!composite_is_ok(c)) return; + + state->tree = smb2_tree_init(state->session, state, true); + if (composite_nomem(state->tree, c)) return; + + state->tcon.in.reserved = 0; + state->tcon.in.path = talloc_asprintf(state, "\\\\%s\\%s", + state->host, state->share); + if (composite_nomem(state->tcon.in.path, c)) return; + + req = smb2_tree_connect_send(state->tree, &state->tcon); + if (composite_nomem(req, c)) return; + + req->async.fn = continue_tcon; + req->async.private_data = c; +} + +/* + continue after negprot reply +*/ +static void continue_negprot(struct smb2_request *req) +{ + struct composite_context *c = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_connect_state *state = talloc_get_type(c->private_data, + struct smb2_connect_state); + struct smb2_transport *transport = req->transport; + struct composite_context *creq; + + c->status = smb2_negprot_recv(req, c, &state->negprot); + if (!composite_is_ok(c)) return; + + 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; + + switch (transport->options.signing) { + case SMB_SIGNING_OFF: + if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) { + composite_error(c, NT_STATUS_ACCESS_DENIED); + return; + } + transport->signing_required = false; + break; + case SMB_SIGNING_SUPPORTED: + if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) { + transport->signing_required = true; + } else { + transport->signing_required = false; + } + break; + case SMB_SIGNING_AUTO: + if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) { + 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 { + composite_error(c, NT_STATUS_ACCESS_DENIED); + return; + } + break; + } + + + state->session = smb2_session_init(transport, global_loadparm, state, true); + if (composite_nomem(state->session, c)) return; + + creq = smb2_session_setup_spnego_send(state->session, state->credentials); + + composite_continue(c, creq, continue_session, c); +} + +/* + continue after a socket connect completes +*/ +static void continue_socket(struct composite_context *creq) +{ + struct composite_context *c = talloc_get_type(creq->async.private_data, + struct composite_context); + struct smb2_connect_state *state = talloc_get_type(c->private_data, + struct smb2_connect_state); + struct smbcli_socket *sock; + struct smb2_transport *transport; + struct smb2_request *req; + uint16_t dialects[2]; + + c->status = smbcli_sock_connect_recv(creq, state, &sock); + if (!composite_is_ok(c)) return; + + transport = smb2_transport_init(sock, state, &state->options); + if (composite_nomem(transport, c)) return; + + ZERO_STRUCT(state->negprot); + state->negprot.in.dialect_count = 2; + switch (transport->options.signing) { + case SMB_SIGNING_OFF: + state->negprot.in.security_mode = 0; + break; + case SMB_SIGNING_SUPPORTED: + case SMB_SIGNING_AUTO: + 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)); + dialects[0] = 0; + dialects[1] = SMB2_DIALECT_REVISION; + state->negprot.in.dialects = dialects; + + req = smb2_negprot_send(transport, &state->negprot); + if (composite_nomem(req, c)) return; + + req->async.fn = continue_negprot; + req->async.private_data = c; +} + + +/* + continue after a resolve finishes +*/ +static void continue_resolve(struct composite_context *creq) +{ + struct composite_context *c = talloc_get_type(creq->async.private_data, + struct composite_context); + struct smb2_connect_state *state = talloc_get_type(c->private_data, + struct smb2_connect_state); + const char *addr; + const char *ports[2] = { "445", NULL }; + + c->status = resolve_name_recv(creq, state, &addr); + if (!composite_is_ok(c)) return; + + creq = smbcli_sock_connect_send(state, addr, ports, state->host, state->resolve_ctx, c->event_ctx); + + composite_continue(c, creq, continue_socket, c); +} + +/* + a composite function that does a full negprot/sesssetup/tcon, returning + a connected smb2_tree + */ +struct composite_context *smb2_connect_send(TALLOC_CTX *mem_ctx, + const char *host, + const char *share, + struct resolve_context *resolve_ctx, + struct cli_credentials *credentials, + struct event_context *ev, + struct smbcli_options *options) +{ + struct composite_context *c; + struct smb2_connect_state *state; + struct nbt_name name; + struct composite_context *creq; + + c = composite_create(mem_ctx, ev); + if (c == NULL) return NULL; + + state = talloc(c, struct smb2_connect_state); + if (composite_nomem(state, c)) return c; + c->private_data = state; + + state->credentials = credentials; + state->options = *options; + state->host = talloc_strdup(c, host); + if (composite_nomem(state->host, c)) return c; + state->share = talloc_strdup(c, share); + if (composite_nomem(state->share, c)) return c; + state->resolve_ctx = talloc_reference(state, resolve_ctx); + + ZERO_STRUCT(name); + name.name = host; + + creq = resolve_name_send(resolve_ctx, &name, c->event_ctx); + composite_continue(c, creq, continue_resolve, c); + return c; +} + +/* + receive a connect reply +*/ +NTSTATUS smb2_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, + struct smb2_tree **tree) +{ + NTSTATUS status; + struct smb2_connect_state *state = talloc_get_type(c->private_data, + struct smb2_connect_state); + status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + *tree = talloc_steal(mem_ctx, state->tree); + } + talloc_free(c); + return status; +} + +/* + sync version of smb2_connect +*/ +NTSTATUS smb2_connect(TALLOC_CTX *mem_ctx, + const char *host, const char *share, + struct resolve_context *resolve_ctx, + struct cli_credentials *credentials, + struct smb2_tree **tree, + struct event_context *ev, + struct smbcli_options *options) +{ + struct composite_context *c = smb2_connect_send(mem_ctx, host, share, + resolve_ctx, + credentials, ev, options); + return smb2_connect_recv(c, mem_ctx, tree); +} |