mirror of
https://github.com/samba-team/samba.git
synced 2024-12-27 03:21:53 +03:00
r4769: added a smb_composite_connect() function that provides a simple async
interface to a complete SMB connection setup. Internally it does:
- socket connection
- session request (if needed)
- negprot
- session setup
- tcon
This is the first example of a composite function that builds on other
composite components (the socket connection is a composite function,
which is used as a building block for this function). I think this
will be quite common in composite functions in the future, building up
ever more complex composite functions from smaller building blocks,
while hiding the details from the caller.
There are two things missing from this now. The first is async name
resolution routines (wins, bcast, DNS etc), and the second is that
this code currently only does a NT1 style session setup. I'll work on
adding spnego and old style session setup support next.
(This used to be commit 6bc9e17f5c
)
This commit is contained in:
parent
4da8abcf0d
commit
1e776edfc5
@ -141,3 +141,4 @@ struct ldb_val;
|
||||
struct smbcli_composite;
|
||||
struct smb_composite_loadfile;
|
||||
struct smb_composite_savefile;
|
||||
struct smb_composite_connect;
|
||||
|
@ -87,3 +87,31 @@ struct smb_composite_savefile {
|
||||
uint32_t size;
|
||||
} in;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
a composite request for a full connection to a remote server. Includes
|
||||
|
||||
- socket establishment
|
||||
- session request
|
||||
- negprot
|
||||
- session setup
|
||||
- tree connect
|
||||
*/
|
||||
struct smb_composite_connect {
|
||||
struct {
|
||||
const char *dest_host;
|
||||
int port;
|
||||
const char *called_name;
|
||||
const char *calling_name;
|
||||
const char *service;
|
||||
const char *service_type;
|
||||
const char *user;
|
||||
const char *domain;
|
||||
const char *password;
|
||||
} in;
|
||||
struct {
|
||||
struct smbcli_tree *tree;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
409
source4/libcli/composite/connect.c
Normal file
409
source4/libcli/composite/connect.c
Normal file
@ -0,0 +1,409 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/*
|
||||
a composite API for making a full SMB connection
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
|
||||
/* the stages of this call */
|
||||
enum connect_stage {CONNECT_SOCKET,
|
||||
CONNECT_SESSION_REQUEST,
|
||||
CONNECT_NEGPROT,
|
||||
CONNECT_SESSION_SETUP,
|
||||
CONNECT_TCON};
|
||||
|
||||
struct connect_state {
|
||||
struct smbcli_socket *sock;
|
||||
struct smbcli_transport *transport;
|
||||
struct smbcli_session *session;
|
||||
};
|
||||
|
||||
|
||||
static void request_handler(struct smbcli_request *);
|
||||
static void composite_handler(struct smbcli_composite *);
|
||||
|
||||
/*
|
||||
setup a negprot send
|
||||
*/
|
||||
static NTSTATUS connect_send_negprot(struct smbcli_composite *c,
|
||||
struct smb_composite_connect *io)
|
||||
{
|
||||
struct connect_state *state = c->private;
|
||||
struct smbcli_request *req;
|
||||
|
||||
req = smb_raw_negotiate_send(state->transport, lp_maxprotocol());
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
|
||||
req->async.fn = request_handler;
|
||||
req->async.private = c;
|
||||
c->stage = CONNECT_NEGPROT;
|
||||
c->req = req;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
a tree connect request has competed
|
||||
*/
|
||||
static NTSTATUS connect_tcon(struct smbcli_composite *c,
|
||||
struct smb_composite_connect *io)
|
||||
{
|
||||
struct smbcli_request *req = c->req;
|
||||
union smb_tcon *io_tcon = c->req_parms;
|
||||
NTSTATUS status;
|
||||
|
||||
status = smb_tree_connect_recv(req, c, io_tcon);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
io->out.tree->tid = io_tcon->tconx.out.tid;
|
||||
if (io_tcon->tconx.out.dev_type) {
|
||||
io->out.tree->device = talloc_strdup(io->out.tree,
|
||||
io_tcon->tconx.out.dev_type);
|
||||
}
|
||||
if (io_tcon->tconx.out.fs_type) {
|
||||
io->out.tree->fs_type = talloc_strdup(io->out.tree,
|
||||
io_tcon->tconx.out.fs_type);
|
||||
}
|
||||
|
||||
/* all done! */
|
||||
c->state = SMBCLI_REQUEST_DONE;
|
||||
if (c->async.fn) {
|
||||
c->async.fn(c);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
a session setup request has competed
|
||||
*/
|
||||
static NTSTATUS connect_session_setup(struct smbcli_composite *c,
|
||||
struct smb_composite_connect *io)
|
||||
{
|
||||
struct connect_state *state = c->private;
|
||||
struct smbcli_request *req = c->req;
|
||||
union smb_sesssetup *io_setup = c->req_parms;
|
||||
union smb_tcon *io_tcon;
|
||||
NTSTATUS status;
|
||||
|
||||
status = smb_raw_session_setup_recv(req, c, io_setup);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
state->session->vuid = io_setup->nt1.out.vuid;
|
||||
|
||||
/* setup for a tconx */
|
||||
io->out.tree = smbcli_tree_init(state->session);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io->out.tree);
|
||||
|
||||
io_tcon = talloc(c, union smb_tcon);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io_tcon);
|
||||
|
||||
/* connect to a share using a tree connect */
|
||||
io_tcon->generic.level = RAW_TCON_TCONX;
|
||||
io_tcon->tconx.in.flags = 0;
|
||||
io_tcon->tconx.in.password = data_blob(NULL, 0);
|
||||
|
||||
io_tcon->tconx.in.path = talloc_asprintf(io_tcon,
|
||||
"\\\\%s\\%s",
|
||||
io->in.called_name,
|
||||
io->in.service);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io_tcon->tconx.in.path);
|
||||
if (!io->in.service_type) {
|
||||
io_tcon->tconx.in.device = "?????";
|
||||
} else {
|
||||
io_tcon->tconx.in.device = io->in.service_type;
|
||||
}
|
||||
|
||||
req = smb_tree_connect_send(io->out.tree, io_tcon);
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
|
||||
req->async.fn = request_handler;
|
||||
req->async.private = c;
|
||||
c->req_parms = io_tcon;
|
||||
c->req = req;
|
||||
c->stage = CONNECT_TCON;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
form an encrypted lanman password from a plaintext password
|
||||
and the server supplied challenge
|
||||
*/
|
||||
static DATA_BLOB lanman_blob(const char *pass, DATA_BLOB challenge)
|
||||
{
|
||||
DATA_BLOB blob = data_blob(NULL, 24);
|
||||
SMBencrypt(pass, challenge.data, blob.data);
|
||||
return blob;
|
||||
}
|
||||
|
||||
/*
|
||||
form an encrypted NT password from a plaintext password
|
||||
and the server supplied challenge
|
||||
*/
|
||||
static DATA_BLOB nt_blob(const char *pass, DATA_BLOB challenge)
|
||||
{
|
||||
DATA_BLOB blob = data_blob(NULL, 24);
|
||||
SMBNTencrypt(pass, challenge.data, blob.data);
|
||||
return blob;
|
||||
}
|
||||
|
||||
/*
|
||||
a negprot request has competed
|
||||
*/
|
||||
static NTSTATUS connect_negprot(struct smbcli_composite *c,
|
||||
struct smb_composite_connect *io)
|
||||
{
|
||||
struct connect_state *state = c->private;
|
||||
struct smbcli_request *req = c->req;
|
||||
NTSTATUS status;
|
||||
union smb_sesssetup *io_setup;
|
||||
|
||||
status = smb_raw_negotiate_recv(req);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
/* next step is a session setup */
|
||||
state->session = smbcli_session_init(state->transport);
|
||||
NT_STATUS_HAVE_NO_MEMORY(state->session);
|
||||
|
||||
/* get rid of the extra reference to the transport */
|
||||
talloc_free(state->transport);
|
||||
|
||||
io_setup = talloc(c, union smb_sesssetup);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io_setup);
|
||||
|
||||
/* prepare a session setup to establish a security context */
|
||||
io_setup->nt1.level = RAW_SESSSETUP_NT1;
|
||||
io_setup->nt1.in.bufsize = state->session->transport->options.max_xmit;
|
||||
io_setup->nt1.in.mpx_max = state->session->transport->options.max_mux;
|
||||
io_setup->nt1.in.vc_num = 1;
|
||||
io_setup->nt1.in.sesskey = state->transport->negotiate.sesskey;
|
||||
io_setup->nt1.in.capabilities = state->transport->negotiate.capabilities;
|
||||
io_setup->nt1.in.domain = io->in.domain;
|
||||
io_setup->nt1.in.user = io->in.user;
|
||||
io_setup->nt1.in.os = "Unix";
|
||||
io_setup->nt1.in.lanman = "Samba";
|
||||
|
||||
if (!io->in.password) {
|
||||
io_setup->nt1.in.password1 = data_blob(NULL, 0);
|
||||
io_setup->nt1.in.password2 = data_blob(NULL, 0);
|
||||
} else if (state->session->transport->negotiate.sec_mode &
|
||||
NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) {
|
||||
io_setup->nt1.in.password1 = lanman_blob(io->in.password,
|
||||
state->transport->negotiate.secblob);
|
||||
io_setup->nt1.in.password2 = nt_blob(io->in.password,
|
||||
state->transport->negotiate.secblob);
|
||||
smb_session_use_nt1_session_keys(state->session, io->in.password, &io_setup->nt1.in.password2);
|
||||
|
||||
} else {
|
||||
io_setup->nt1.in.password1 = data_blob(io->in.password,
|
||||
strlen(io->in.password));
|
||||
io_setup->nt1.in.password2 = data_blob(NULL, 0);
|
||||
}
|
||||
|
||||
req = smb_raw_session_setup_send(state->session, io_setup);
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
|
||||
req->async.fn = request_handler;
|
||||
req->async.private = c;
|
||||
c->req_parms = io_setup;
|
||||
c->req = req;
|
||||
c->stage = CONNECT_SESSION_SETUP;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
a session request operation has competed
|
||||
*/
|
||||
static NTSTATUS connect_session_request(struct smbcli_composite *c,
|
||||
struct smb_composite_connect *io)
|
||||
{
|
||||
struct smbcli_request *req = c->req;
|
||||
NTSTATUS status;
|
||||
|
||||
status = smbcli_transport_connect_recv(req);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
/* next step is a negprot */
|
||||
return connect_send_negprot(c, io);
|
||||
}
|
||||
|
||||
/*
|
||||
a socket connection operation has competed
|
||||
*/
|
||||
static NTSTATUS connect_socket(struct smbcli_composite *c,
|
||||
struct smb_composite_connect *io)
|
||||
{
|
||||
struct connect_state *state = c->private;
|
||||
NTSTATUS status;
|
||||
struct smbcli_request *req;
|
||||
struct nmb_name calling, called;
|
||||
|
||||
status = smbcli_sock_connect_recv(c->req);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
/* the socket is up - we can initialise the smbcli transport layer */
|
||||
state->transport = smbcli_transport_init(state->sock);
|
||||
NT_STATUS_HAVE_NO_MEMORY(state->transport);
|
||||
|
||||
/* we have a connected socket - next step is a session
|
||||
request, if needed. Port 445 doesn't need it, so it goes
|
||||
straight to the negprot */
|
||||
if (state->sock->port == 445) {
|
||||
return connect_send_negprot(c, io);
|
||||
}
|
||||
|
||||
make_nmb_name(&calling, io->in.calling_name, 0x0);
|
||||
choose_called_name(&called, io->in.called_name, 0x20);
|
||||
|
||||
req = smbcli_transport_connect_send(state->transport, &calling, &called);
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
|
||||
req->async.fn = request_handler;
|
||||
req->async.private = c;
|
||||
c->stage = CONNECT_SESSION_REQUEST;
|
||||
c->req = req;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
handle and dispatch state transitions
|
||||
*/
|
||||
static void state_handler(struct smbcli_composite *c)
|
||||
{
|
||||
struct smb_composite_connect *io = c->composite_parms;
|
||||
|
||||
switch (c->stage) {
|
||||
case CONNECT_SOCKET:
|
||||
c->status = connect_socket(c, io);
|
||||
break;
|
||||
case CONNECT_SESSION_REQUEST:
|
||||
c->status = connect_session_request(c, io);
|
||||
break;
|
||||
case CONNECT_NEGPROT:
|
||||
c->status = connect_negprot(c, io);
|
||||
break;
|
||||
case CONNECT_SESSION_SETUP:
|
||||
c->status = connect_session_setup(c, io);
|
||||
break;
|
||||
case CONNECT_TCON:
|
||||
c->status = connect_tcon(c, io);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
c->state = SMBCLI_REQUEST_ERROR;
|
||||
if (c->async.fn) {
|
||||
c->async.fn(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
handler for completion of a smbcli_request sub-request
|
||||
*/
|
||||
static void request_handler(struct smbcli_request *req)
|
||||
{
|
||||
struct smbcli_composite *c = req->async.private;
|
||||
return state_handler(c);
|
||||
}
|
||||
|
||||
/*
|
||||
handler for completion of a smbcli_composite sub-request
|
||||
*/
|
||||
static void composite_handler(struct smbcli_composite *req)
|
||||
{
|
||||
struct smbcli_composite *c = req->async.private;
|
||||
return state_handler(c);
|
||||
}
|
||||
|
||||
/*
|
||||
a function to establish a smbcli_tree from scratch
|
||||
*/
|
||||
struct smbcli_composite *smb_composite_connect_send(struct smb_composite_connect *io)
|
||||
{
|
||||
struct smbcli_composite *c, *req;
|
||||
struct connect_state *state;
|
||||
|
||||
c = talloc_zero(NULL, struct smbcli_composite);
|
||||
if (c == NULL) goto failed;
|
||||
|
||||
state = talloc(c, struct connect_state);
|
||||
if (state == NULL) goto failed;
|
||||
|
||||
state->sock = smbcli_sock_init(state);
|
||||
if (state->sock == NULL) goto failed;
|
||||
|
||||
c->state = SMBCLI_REQUEST_SEND;
|
||||
c->composite_parms = io;
|
||||
c->stage = CONNECT_SOCKET;
|
||||
c->event_ctx = state->sock->event.ctx;
|
||||
c->private = state;
|
||||
|
||||
req = smbcli_sock_connect_send(state->sock, io->in.dest_host, io->in.port);
|
||||
if (req == NULL) goto failed;
|
||||
|
||||
req->async.private = c;
|
||||
req->async.fn = composite_handler;
|
||||
c->req = req;
|
||||
|
||||
return c;
|
||||
failed:
|
||||
talloc_free(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
recv half of async composite connect code
|
||||
*/
|
||||
NTSTATUS smb_composite_connect_recv(struct smbcli_composite *c, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
status = smb_composite_wait(c);
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
struct smb_composite_connect *io = c->composite_parms;
|
||||
talloc_steal(mem_ctx, io->out.tree);
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
sync version of smb_composite_connect
|
||||
*/
|
||||
NTSTATUS smb_composite_connect(struct smb_composite_connect *io, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct smbcli_composite *c = smb_composite_connect_send(io);
|
||||
return smb_composite_connect_recv(c, mem_ctx);
|
||||
}
|
@ -22,7 +22,8 @@ ADD_OBJ_FILES = libcli/util/clilsa.o
|
||||
ADD_OBJ_FILES = \
|
||||
libcli/composite/composite.o \
|
||||
libcli/composite/loadfile.o \
|
||||
libcli/composite/savefile.o
|
||||
libcli/composite/savefile.o \
|
||||
libcli/composite/connect.o
|
||||
|
||||
[SUBSYSTEM::LIBCLI]
|
||||
REQUIRED_SUBSYSTEMS = LIBCLI_RAW LIBCLI_UTILS LIBCLI_AUTH LIBCLI_NMB LIBCLI_COMPOSITE
|
||||
|
@ -256,7 +256,7 @@ void smbcli_session_set_user_session_key(struct smbcli_session *session,
|
||||
/*
|
||||
setup signing for a NT1 style session setup
|
||||
*/
|
||||
static void use_nt1_session_keys(struct smbcli_session *session,
|
||||
void smb_session_use_nt1_session_keys(struct smbcli_session *session,
|
||||
const char *password, const DATA_BLOB *nt_response)
|
||||
{
|
||||
struct smbcli_transport *transport = session->transport;
|
||||
@ -352,7 +352,7 @@ static NTSTATUS smb_raw_session_setup_generic_nt1(struct smbcli_session *session
|
||||
session->transport->negotiate.secblob);
|
||||
s2.nt1.in.password2 = nt_blob(parms->generic.in.password,
|
||||
session->transport->negotiate.secblob);
|
||||
use_nt1_session_keys(session, parms->generic.in.password, &s2.nt1.in.password2);
|
||||
smb_session_use_nt1_session_keys(session, parms->generic.in.password, &s2.nt1.in.password2);
|
||||
|
||||
} else {
|
||||
s2.nt1.in.password1 = data_blob(parms->generic.in.password,
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
|
||||
#define SETUP_REQUEST_TREE(cmd, wct, buflen) do { \
|
||||
req = smbcli_request_setup(tree, cmd, wct, buflen); \
|
||||
@ -289,3 +290,36 @@ NTSTATUS smbcli_tree_full_connection(TALLOC_CTX *parent_ctx,
|
||||
*ret_tree = tree;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
a convenient function to establish a smbcli_tree from scratch
|
||||
*/
|
||||
NTSTATUS async_smbcli_tree_full_connection(TALLOC_CTX *parent_ctx,
|
||||
struct smbcli_tree **ret_tree,
|
||||
const char *my_name,
|
||||
const char *dest_host, int port,
|
||||
const char *service, const char *service_type,
|
||||
const char *user, const char *domain,
|
||||
const char *password)
|
||||
{
|
||||
struct smb_composite_connect io;
|
||||
NTSTATUS status;
|
||||
|
||||
io.in.dest_host = dest_host;
|
||||
io.in.port = port;
|
||||
io.in.called_name = dest_host;
|
||||
io.in.calling_name = my_name;
|
||||
io.in.service = service;
|
||||
io.in.service_type = service_type;
|
||||
io.in.user = user;
|
||||
io.in.domain = domain;
|
||||
io.in.password = password;
|
||||
|
||||
status = smb_composite_connect(&io, parent_ctx);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
*ret_tree = io.out.tree;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user