r4769: added a smb_composite_connect() function that provides a simple async
authorAndrew Tridgell <tridge@samba.org>
Sun, 16 Jan 2005 01:28:11 +0000 (01:28 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:08:52 +0000 (13:08 -0500)
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 6bc9e17f5c5236f662c7c8f308d03e6d97379b23)

source4/include/structs.h
source4/libcli/composite/composite.h
source4/libcli/composite/connect.c [new file with mode: 0644]
source4/libcli/config.mk
source4/libcli/raw/clisession.c
source4/libcli/raw/clitree.c

index 5845e981a20fe425f5e06c6977803c18778af1bc..2cffac184878c65930065e747e5ee705062e1373 100644 (file)
@@ -141,3 +141,4 @@ struct ldb_val;
 struct smbcli_composite;
 struct smb_composite_loadfile;
 struct smb_composite_savefile;
+struct smb_composite_connect;
index 6bffc632115e74892140e5b2d607f795b83de2f4..dce4e4e10981b319d4a9b6bbd012b47ef5278aa9 100644 (file)
@@ -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;
+};
+
diff --git a/source4/libcli/composite/connect.c b/source4/libcli/composite/connect.c
new file mode 100644 (file)
index 0000000..310084d
--- /dev/null
@@ -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);
+}
index 40eecca0f9e6f9da9e9741344134eb63c5434829..3d4c3a856670bcc0a489b3986caddd86892e210c 100644 (file)
@@ -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
index 322d27688e357571876d3794eb5f53a3b67b8343..46236217ea543b03f613ca3025626c24f461bf0d 100644 (file)
@@ -256,8 +256,8 @@ 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, 
-                                const char *password, const DATA_BLOB  *nt_response)
+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; 
        uint8_t nt_hash[16];
@@ -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, 
index cc7fefd084e82e0021b964a5295684dcb0287618..7339ca07f10e5614a640714b7d91a19ae4b3f3a2 100644 (file)
@@ -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;
+}