From 7cbc768376ed0a839afca64aeea99cd53d0fbc6f Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 16 Jan 2005 11:15:08 +0000 Subject: r4777: added a smb_composite_sesssetup() async composite function. This encapsulates all the different session setup methods, including the multi-pass spnego code. I have hooked this into all the places that previously used the RAW_SESSSETUP_GENERIC method, and have removed the old RAW_SESSSETUP_GENERIC code from clisession.c and clitree.c. A nice side effect is that these two modules are now very simple again, back to being "raw" session setup handling, which was what was originally intended. I have also used this to replace the session setup code in the smb_composite_connect() code, and used that to build a very simple replacement for smbcli_tree_full_connection(). As a result, smbclient, smbtorture and all our other SMB connection code now goes via these composite async functions. That should give them a good workout! (This used to be commit 080d0518bc7d6fd4bc3ef783e7d4d2e3275d0799) --- source4/libcli/cliconnect.c | 28 +-- source4/libcli/composite/composite.h | 17 ++ source4/libcli/composite/connect.c | 88 ++------ source4/libcli/composite/sesssetup.c | 403 +++++++++++++++++++++++++++++++++++ source4/libcli/config.mk | 3 +- source4/libcli/raw/clisession.c | 358 +------------------------------ source4/libcli/raw/clisocket.c | 2 + source4/libcli/raw/clitransport.c | 3 +- source4/libcli/raw/clitree.c | 147 +------------ 9 files changed, 473 insertions(+), 576 deletions(-) create mode 100644 source4/libcli/composite/sesssetup.c (limited to 'source4/libcli') diff --git a/source4/libcli/cliconnect.c b/source4/libcli/cliconnect.c index 4d46d1d23c..0f916d1eb1 100644 --- a/source4/libcli/cliconnect.c +++ b/source4/libcli/cliconnect.c @@ -21,6 +21,7 @@ #include "includes.h" #include "system/filesys.h" #include "libcli/raw/libcliraw.h" +#include "libcli/composite/composite.h" /* wrapper around smbcli_sock_connect() @@ -66,7 +67,7 @@ NTSTATUS smbcli_session_setup(struct smbcli_state *cli, const char *password, const char *domain) { - union smb_sesssetup setup; + struct smb_composite_sesssetup setup; NTSTATUS status; TALLOC_CTX *mem_ctx; @@ -77,27 +78,26 @@ NTSTATUS smbcli_session_setup(struct smbcli_state *cli, mem_ctx = talloc_init("smbcli_session_setup"); if (!mem_ctx) return NT_STATUS_NO_MEMORY; - setup.generic.level = RAW_SESSSETUP_GENERIC; - setup.generic.in.sesskey = cli->transport->negotiate.sesskey; - setup.generic.in.capabilities = cli->transport->negotiate.capabilities; + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities = cli->transport->negotiate.capabilities; if (!user || !user[0]) { - setup.generic.in.password = NULL; - setup.generic.in.user = ""; - setup.generic.in.domain = ""; - setup.generic.in.capabilities &= ~CAP_EXTENDED_SECURITY; + setup.in.password = NULL; + setup.in.user = ""; + setup.in.domain = ""; + setup.in.capabilities &= ~CAP_EXTENDED_SECURITY; } else { if (cli->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) { - setup.generic.in.password = password; + setup.in.password = password; } else { - setup.generic.in.password = NULL; + setup.in.password = NULL; } - setup.generic.in.user = user; - setup.generic.in.domain = domain; + setup.in.user = user; + setup.in.domain = domain; } - status = smb_raw_session_setup(cli->session, mem_ctx, &setup); + status = smb_composite_sesssetup(cli->session, &setup); - cli->session->vuid = setup.generic.out.vuid; + cli->session->vuid = setup.out.vuid; talloc_free(mem_ctx); diff --git a/source4/libcli/composite/composite.h b/source4/libcli/composite/composite.h index dce4e4e109..321d3be943 100644 --- a/source4/libcli/composite/composite.h +++ b/source4/libcli/composite/composite.h @@ -115,3 +115,20 @@ struct smb_composite_connect { } out; }; + +/* + generic session setup interface that takes care of which + session setup varient to use +*/ +struct smb_composite_sesssetup { + struct { + uint32_t sesskey; + uint32_t capabilities; + const char *password; + const char *user; + const char *domain; + } in; + struct { + uint16_t vuid; + } out; +}; diff --git a/source4/libcli/composite/connect.c b/source4/libcli/composite/connect.c index 310084d0b1..c51c8d48fd 100644 --- a/source4/libcli/composite/connect.c +++ b/source4/libcli/composite/connect.c @@ -103,15 +103,16 @@ 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; + struct smbcli_composite *req = c->req; + struct smbcli_request *req2; + struct smb_composite_sesssetup *io_setup = c->req_parms; union smb_tcon *io_tcon; NTSTATUS status; - status = smb_raw_session_setup_recv(req, c, io_setup); + status = smb_composite_sesssetup_recv(req); NT_STATUS_NOT_OK_RETURN(status); - state->session->vuid = io_setup->nt1.out.vuid; + state->session->vuid = io_setup->out.vuid; /* setup for a tconx */ io->out.tree = smbcli_tree_init(state->session); @@ -136,40 +137,18 @@ static NTSTATUS connect_session_setup(struct smbcli_composite *c, 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); + req2 = smb_tree_connect_send(io->out.tree, io_tcon); + NT_STATUS_HAVE_NO_MEMORY(req2); - req->async.fn = request_handler; - req->async.private = c; + req2->async.fn = request_handler; + req2->async.private = c; c->req_parms = io_tcon; - c->req = req; + c->req = req2; 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 */ @@ -178,8 +157,9 @@ static NTSTATUS connect_negprot(struct smbcli_composite *c, { struct connect_state *state = c->private; struct smbcli_request *req = c->req; + struct smbcli_composite *req2; NTSTATUS status; - union smb_sesssetup *io_setup; + struct smb_composite_sesssetup *io_setup; status = smb_raw_negotiate_recv(req); NT_STATUS_NOT_OK_RETURN(status); @@ -191,45 +171,23 @@ static NTSTATUS connect_negprot(struct smbcli_composite *c, /* get rid of the extra reference to the transport */ talloc_free(state->transport); - io_setup = talloc(c, union smb_sesssetup); + io_setup = talloc(c, struct smb_composite_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); + io_setup->in.sesskey = state->transport->negotiate.sesskey; + io_setup->in.capabilities = state->transport->negotiate.capabilities; + io_setup->in.domain = io->in.domain; + io_setup->in.user = io->in.user; + io_setup->in.password = io->in.password; - } else { - io_setup->nt1.in.password1 = data_blob(io->in.password, - strlen(io->in.password)); - io_setup->nt1.in.password2 = data_blob(NULL, 0); - } + req2 = smb_composite_sesssetup_send(state->session, io_setup); + NT_STATUS_HAVE_NO_MEMORY(req2); - 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; + req2->async.fn = composite_handler; + req2->async.private = c; c->req_parms = io_setup; - c->req = req; + c->req = req2; c->stage = CONNECT_SESSION_SETUP; return NT_STATUS_OK; diff --git a/source4/libcli/composite/sesssetup.c b/source4/libcli/composite/sesssetup.c new file mode 100644 index 0000000000..062235670e --- /dev/null +++ b/source4/libcli/composite/sesssetup.c @@ -0,0 +1,403 @@ +/* + 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 handling a generic async session setup +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/composite/composite.h" +#include "auth/auth.h" + + +struct sesssetup_state { + union smb_sesssetup setup; + NTSTATUS session_key_err; +}; + + +/* + form an encrypted lanman password from a plaintext password + and the server supplied challenge +*/ +static DATA_BLOB lanman_blob(TALLOC_CTX *mem_ctx, const char *pass, DATA_BLOB challenge) +{ + DATA_BLOB blob = data_blob_talloc(mem_ctx, 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(TALLOC_CTX *mem_ctx, const char *pass, DATA_BLOB challenge) +{ + DATA_BLOB blob = data_blob_talloc(mem_ctx, NULL, 24); + SMBNTencrypt(pass, challenge.data, blob.data); + return blob; +} + +/* + store the user session key for a transport +*/ +static void set_user_session_key(struct smbcli_session *session, + const DATA_BLOB *session_key) +{ + session->user_session_key = data_blob_talloc(session, + session_key->data, + session_key->length); +} + +/* + 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) +{ + struct smbcli_transport *transport = session->transport; + uint8_t nt_hash[16]; + DATA_BLOB session_key = data_blob_talloc(session, NULL, 16); + + E_md4hash(password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, session_key.data); + + smbcli_transport_simple_set_signing(transport, session_key, *nt_response); + + set_user_session_key(session, &session_key); + data_blob_free(&session_key); +} + + +/* + handler for completion of a smbcli_request sub-request +*/ +static void request_handler(struct smbcli_request *req) +{ + struct smbcli_composite *c = req->async.private; + struct sesssetup_state *state = c->private; + struct smb_composite_sesssetup *io = c->composite_parms; + struct smbcli_session *session = req->session; + DATA_BLOB session_key = data_blob(NULL, 0); + DATA_BLOB null_data_blob = data_blob(NULL, 0); + + c->status = smb_raw_session_setup_recv(req, state, &state->setup); + + switch (state->setup.old.level) { + case RAW_SESSSETUP_OLD: + io->out.vuid = state->setup.old.out.vuid; + break; + + case RAW_SESSSETUP_NT1: + io->out.vuid = state->setup.nt1.out.vuid; + break; + + case RAW_SESSSETUP_SPNEGO: + session->vuid = io->out.vuid = state->setup.spnego.out.vuid; + if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) && + !NT_STATUS_IS_OK(c->status)) { + break; + } + c->status = gensec_update(session->gensec, state, + state->setup.spnego.out.secblob, + &state->setup.spnego.in.secblob); + if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) && + !NT_STATUS_IS_OK(c->status)) { + break; + } + if (state->setup.spnego.in.secblob.length == 0) { + break; + } + + /* we need to do another round of session setup. We keep going until both sides + are happy */ + state->session_key_err = gensec_session_key(session->gensec, &session_key); + if (NT_STATUS_IS_OK(state->session_key_err)) { + smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob); + } + + req = smb_raw_session_setup_send(session, &state->setup); + req->async.fn = request_handler; + req->async.private = c; + c->req = req; + return; + } + + /* enforce the local signing required flag */ + if (NT_STATUS_IS_OK(c->status) && io->in.user && io->in.user[0]) { + if (!session->transport->negotiate.sign_info.doing_signing + && session->transport->negotiate.sign_info.mandatory_signing) { + DEBUG(0, ("SMB signing required, but server does not support it\n")); + c->status = NT_STATUS_ACCESS_DENIED; + } + } + + if (NT_STATUS_IS_OK(c->status)) { + c->state = SMBCLI_REQUEST_DONE; + } else { + c->state = SMBCLI_REQUEST_ERROR; + } + if (c->async.fn) { + c->async.fn(c); + } +} + + +/* + send a nt1 style session setup +*/ +static struct smbcli_request *session_setup_nt1(struct smbcli_composite *c, + struct smbcli_session *session, + struct smb_composite_sesssetup *io) +{ + struct sesssetup_state *state = c->private; + + state->setup.nt1.level = RAW_SESSSETUP_NT1; + state->setup.nt1.in.bufsize = session->transport->options.max_xmit; + state->setup.nt1.in.mpx_max = session->transport->options.max_mux; + state->setup.nt1.in.vc_num = 1; + state->setup.nt1.in.sesskey = io->in.sesskey; + state->setup.nt1.in.capabilities = io->in.capabilities; + state->setup.nt1.in.domain = io->in.domain; + state->setup.nt1.in.user = io->in.user; + state->setup.nt1.in.os = "Unix"; + state->setup.nt1.in.lanman = "Samba"; + + if (!io->in.password) { + state->setup.nt1.in.password1 = data_blob(NULL, 0); + state->setup.nt1.in.password2 = data_blob(NULL, 0); + } else if (session->transport->negotiate.sec_mode & + NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) { + state->setup.nt1.in.password1 = lanman_blob(state, io->in.password, + session->transport->negotiate.secblob); + state->setup.nt1.in.password2 = nt_blob(state, io->in.password, + session->transport->negotiate.secblob); + use_nt1_session_keys(session, io->in.password, &state->setup.nt1.in.password2); + } else { + state->setup.nt1.in.password1 = data_blob_talloc(state, io->in.password, strlen(io->in.password)); + state->setup.nt1.in.password2 = data_blob(NULL, 0); + } + + return smb_raw_session_setup_send(session, &state->setup); +} + + +/* + old style session setup (pre NT1 protocol level) +*/ +static struct smbcli_request *session_setup_old(struct smbcli_composite *c, + struct smbcli_session *session, + struct smb_composite_sesssetup *io) +{ + struct sesssetup_state *state = c->private; + + state->setup.old.level = RAW_SESSSETUP_OLD; + state->setup.old.in.bufsize = session->transport->options.max_xmit; + state->setup.old.in.mpx_max = session->transport->options.max_mux; + state->setup.old.in.vc_num = 1; + state->setup.old.in.sesskey = io->in.sesskey; + state->setup.old.in.domain = io->in.domain; + state->setup.old.in.user = io->in.user; + state->setup.old.in.os = "Unix"; + state->setup.old.in.lanman = "Samba"; + + if (!io->in.password) { + state->setup.old.in.password = data_blob(NULL, 0); + } else if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) { + state->setup.old.in.password = lanman_blob(state, io->in.password, + session->transport->negotiate.secblob); + } else { + state->setup.old.in.password = data_blob_talloc(state, + io->in.password, + strlen(io->in.password)); + } + + return smb_raw_session_setup_send(session, &state->setup); +} + + +/* + old style session setup (pre NT1 protocol level) +*/ +static struct smbcli_request *session_setup_spnego(struct smbcli_composite *c, + struct smbcli_session *session, + struct smb_composite_sesssetup *io) +{ + struct sesssetup_state *state = c->private; + NTSTATUS status; + DATA_BLOB session_key = data_blob(NULL, 0); + DATA_BLOB null_data_blob = data_blob(NULL, 0); + const char *chosen_oid = NULL; + + state->setup.spnego.level = RAW_SESSSETUP_SPNEGO; + state->setup.spnego.in.bufsize = session->transport->options.max_xmit; + state->setup.spnego.in.mpx_max = session->transport->options.max_mux; + state->setup.spnego.in.vc_num = 1; + state->setup.spnego.in.sesskey = io->in.sesskey; + state->setup.spnego.in.capabilities = io->in.capabilities; + state->setup.spnego.in.domain = io->in.domain; + state->setup.spnego.in.os = "Unix"; + state->setup.spnego.in.lanman = "Samba"; + state->setup.spnego.out.vuid = session->vuid; + + smbcli_temp_set_signing(session->transport); + + status = gensec_client_start(session, &session->gensec); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status))); + return NULL; + } + + gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY); + + status = gensec_set_domain(session->gensec, io->in.domain); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client domain to %s: %s\n", + io->in.domain, nt_errstr(status))); + return NULL; + } + + status = gensec_set_username(session->gensec, io->in.user); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client username to %s: %s\n", + io->in.user, nt_errstr(status))); + return NULL; + } + + status = gensec_set_password(session->gensec, io->in.password); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client password: %s\n", + nt_errstr(status))); + return NULL; + } + + status = gensec_set_target_hostname(session->gensec, session->transport->socket->hostname); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n", + nt_errstr(status))); + return NULL; + } + + if (session->transport->negotiate.secblob.length) { + chosen_oid = GENSEC_OID_SPNEGO; + } else { + /* without a sec blob, means raw NTLMSSP */ + chosen_oid = GENSEC_OID_NTLMSSP; + } + + status = gensec_start_mech_by_oid(session->gensec, chosen_oid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism %s: %s\n", + gensec_get_name_by_oid(chosen_oid), nt_errstr(status))); + return NULL; + } + + status = gensec_update(session->gensec, state, + session->transport->negotiate.secblob, + &state->setup.spnego.in.secblob); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + DEBUG(1, ("Failed initial gensec_update with mechanism %s: %s\n", + gensec_get_name_by_oid(chosen_oid), nt_errstr(status))); + return NULL; + } + + state->session_key_err = gensec_session_key(session->gensec, &session_key); + if (NT_STATUS_IS_OK(state->session_key_err)) { + smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob); + } + + return smb_raw_session_setup_send(session, &state->setup); +} + + +/* + composite session setup function that hides the details of all the + different session setup varients, including the multi-pass nature of + the spnego varient +*/ +struct smbcli_composite *smb_composite_sesssetup_send(struct smbcli_session *session, + struct smb_composite_sesssetup *io) +{ + struct smbcli_composite *c; + struct sesssetup_state *state; + struct smbcli_request *req = NULL; + + c = talloc_zero(session, struct smbcli_composite); + if (c == NULL) goto failed; + + state = talloc(c, struct sesssetup_state); + if (state == NULL) goto failed; + + c->state = SMBCLI_REQUEST_SEND; + c->req_parms = io; + c->private = state; + c->event_ctx = session->transport->socket->event.ctx; + c->composite_parms = io; + + /* no session setup at all in earliest protocol varients */ + if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) { + ZERO_STRUCT(io->out); + c->state = SMBCLI_REQUEST_DONE; + return c; + } + + /* see what session setup interface we will use */ + if (session->transport->negotiate.protocol < PROTOCOL_NT1) { + req = session_setup_old(c, session, io); + } else if (!session->transport->options.use_spnego || + !(io->in.capabilities & CAP_EXTENDED_SECURITY)) { + req = session_setup_nt1(c, session, io); + } else { + req = session_setup_spnego(c, session, io); + } + + if (req == NULL) goto failed; + + req->async.fn = request_handler; + req->async.private = c; + c->req = req; + + return c; + +failed: + talloc_free(c); + return NULL; +} + + +/* + receive a composite session setup reply +*/ +NTSTATUS smb_composite_sesssetup_recv(struct smbcli_composite *c) +{ + NTSTATUS status; + status = smb_composite_wait(c); + talloc_free(c); + return status; +} + +/* + sync version of smb_composite_sesssetup +*/ +NTSTATUS smb_composite_sesssetup(struct smbcli_session *session, struct smb_composite_sesssetup *io) +{ + struct smbcli_composite *c = smb_composite_sesssetup_send(session, io); + return smb_composite_sesssetup_recv(c); +} diff --git a/source4/libcli/config.mk b/source4/libcli/config.mk index 3d4c3a8566..0c4ba5cef0 100644 --- a/source4/libcli/config.mk +++ b/source4/libcli/config.mk @@ -23,7 +23,8 @@ ADD_OBJ_FILES = \ libcli/composite/composite.o \ libcli/composite/loadfile.o \ libcli/composite/savefile.o \ - libcli/composite/connect.o + libcli/composite/connect.o \ + libcli/composite/sesssetup.o [SUBSYSTEM::LIBCLI] REQUIRED_SUBSYSTEMS = LIBCLI_RAW LIBCLI_UTILS LIBCLI_AUTH LIBCLI_NMB LIBCLI_COMPOSITE diff --git a/source4/libcli/raw/clisession.c b/source4/libcli/raw/clisession.c index 46236217ea..ed50601c25 100644 --- a/source4/libcli/raw/clisession.c +++ b/source4/libcli/raw/clisession.c @@ -1,7 +1,8 @@ /* Unix SMB/CIFS implementation. SMB client session context management functions - Copyright (C) Andrew Tridgell 1994-1998 + + Copyright (C) Andrew Tridgell 1994-2005 Copyright (C) James Myers 2003 This program is free software; you can redistribute it and/or modify @@ -76,11 +77,7 @@ struct smbcli_request *smb_raw_session_setup_send(struct smbcli_session *session { struct smbcli_request *req = NULL; - switch (parms->generic.level) { - case RAW_SESSSETUP_GENERIC: - /* handled elsewhere */ - return NULL; - + switch (parms->old.level) { case RAW_SESSSETUP_OLD: SETUP_REQUEST_SESSION(SMBsesssetupX, 10, 0); SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); @@ -164,11 +161,7 @@ NTSTATUS smb_raw_session_setup_recv(struct smbcli_request *req, return smbcli_request_destroy(req); } - switch (parms->generic.level) { - case RAW_SESSSETUP_GENERIC: - /* handled elsewhere */ - return NT_STATUS_INVALID_LEVEL; - + switch (parms->old.level) { case RAW_SESSSETUP_OLD: SMBCLI_CHECK_WCT(req, 3); ZERO_STRUCT(parms->old.out); @@ -220,353 +213,14 @@ failed: return smbcli_request_destroy(req); } -/* - 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; -} - -/* - store the user session key for a transport -*/ -void smbcli_session_set_user_session_key(struct smbcli_session *session, - const DATA_BLOB *session_key) -{ - session->user_session_key = data_blob_talloc(session, - session_key->data, - session_key->length); -} /* - setup signing for a NT1 style session setup -*/ -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]; - DATA_BLOB session_key = data_blob(NULL, 16); - - E_md4hash(password, nt_hash); - SMBsesskeygen_ntv1(nt_hash, session_key.data); - - smbcli_transport_simple_set_signing(transport, session_key, *nt_response); - - smbcli_session_set_user_session_key(session, &session_key); - data_blob_free(&session_key); -} - -/**************************************************************************** - Perform a session setup (sync interface) using generic interface and the old - style sesssetup call -****************************************************************************/ -static NTSTATUS smb_raw_session_setup_generic_old(struct smbcli_session *session, - TALLOC_CTX *mem_ctx, - union smb_sesssetup *parms) -{ - NTSTATUS status; - union smb_sesssetup s2; - - /* use the old interface */ - s2.generic.level = RAW_SESSSETUP_OLD; - s2.old.in.bufsize = session->transport->options.max_xmit; - s2.old.in.mpx_max = session->transport->options.max_mux; - s2.old.in.vc_num = 1; - s2.old.in.sesskey = parms->generic.in.sesskey; - s2.old.in.domain = parms->generic.in.domain; - s2.old.in.user = parms->generic.in.user; - s2.old.in.os = "Unix"; - s2.old.in.lanman = "Samba"; - - if (!parms->generic.in.password) { - s2.old.in.password = data_blob(NULL, 0); - } else if (session->transport->negotiate.sec_mode & - NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) { - s2.old.in.password = lanman_blob(parms->generic.in.password, - session->transport->negotiate.secblob); - } else { - s2.old.in.password = data_blob(parms->generic.in.password, - strlen(parms->generic.in.password)); - } - - status = smb_raw_session_setup(session, mem_ctx, &s2); - - data_blob_free(&s2.old.in.password); - - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - parms->generic.out.vuid = s2.old.out.vuid; - parms->generic.out.os = s2.old.out.os; - parms->generic.out.lanman = s2.old.out.lanman; - parms->generic.out.domain = s2.old.out.domain; - - return NT_STATUS_OK; -} - -/**************************************************************************** - Perform a session setup (sync interface) using generic interface and the NT1 - style sesssetup call -****************************************************************************/ -static NTSTATUS smb_raw_session_setup_generic_nt1(struct smbcli_session *session, - TALLOC_CTX *mem_ctx, - union smb_sesssetup *parms) -{ - NTSTATUS status; - union smb_sesssetup s2; - - s2.generic.level = RAW_SESSSETUP_NT1; - s2.nt1.in.bufsize = session->transport->options.max_xmit; - s2.nt1.in.mpx_max = session->transport->options.max_mux; - s2.nt1.in.vc_num = 1; - s2.nt1.in.sesskey = parms->generic.in.sesskey; - s2.nt1.in.capabilities = parms->generic.in.capabilities; - s2.nt1.in.domain = parms->generic.in.domain; - s2.nt1.in.user = parms->generic.in.user; - s2.nt1.in.os = "Unix"; - s2.nt1.in.lanman = "Samba"; - - if (!parms->generic.in.password) { - s2.nt1.in.password1 = data_blob(NULL, 0); - s2.nt1.in.password2 = data_blob(NULL, 0); - } else if (session->transport->negotiate.sec_mode & - NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) { - s2.nt1.in.password1 = lanman_blob(parms->generic.in.password, - session->transport->negotiate.secblob); - s2.nt1.in.password2 = nt_blob(parms->generic.in.password, - session->transport->negotiate.secblob); - 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, - strlen(parms->generic.in.password)); - s2.nt1.in.password2 = data_blob(NULL, 0); - } - - status = smb_raw_session_setup(session, mem_ctx, &s2); - - data_blob_free(&s2.nt1.in.password1); - data_blob_free(&s2.nt1.in.password2); - - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - parms->generic.out.vuid = s2.nt1.out.vuid; - parms->generic.out.os = s2.nt1.out.os; - parms->generic.out.lanman = s2.nt1.out.lanman; - parms->generic.out.domain = s2.nt1.out.domain; - - return NT_STATUS_OK; -} - -/**************************************************************************** - Perform a session setup (sync interface) using generic interface and the SPNEGO - style sesssetup call -****************************************************************************/ -static NTSTATUS smb_raw_session_setup_generic_spnego(struct smbcli_session *session, - TALLOC_CTX *mem_ctx, - union smb_sesssetup *parms) -{ - NTSTATUS status; - NTSTATUS session_key_err = NT_STATUS_NO_USER_SESSION_KEY; - union smb_sesssetup s2; - DATA_BLOB session_key = data_blob(NULL, 0); - DATA_BLOB null_data_blob = data_blob(NULL, 0); - const char *chosen_oid = NULL; - - s2.generic.level = RAW_SESSSETUP_SPNEGO; - s2.spnego.in.bufsize = session->transport->options.max_xmit; - s2.spnego.in.mpx_max = session->transport->options.max_mux; - s2.spnego.in.vc_num = 1; - s2.spnego.in.sesskey = parms->generic.in.sesskey; - s2.spnego.in.capabilities = parms->generic.in.capabilities; - s2.spnego.in.domain = parms->generic.in.domain; - s2.spnego.in.os = "Unix"; - s2.spnego.in.lanman = "Samba"; - s2.spnego.out.vuid = session->vuid; - - smbcli_temp_set_signing(session->transport); - - status = gensec_client_start(session, &session->gensec); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status))); - return status; - } - - gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY); - - status = gensec_set_domain(session->gensec, parms->generic.in.domain); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start set GENSEC client domain to %s: %s\n", - parms->generic.in.domain, nt_errstr(status))); - goto done; - } - - status = gensec_set_username(session->gensec, parms->generic.in.user); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start set GENSEC client username to %s: %s\n", - parms->generic.in.user, nt_errstr(status))); - goto done; - } - - status = gensec_set_password(session->gensec, parms->generic.in.password); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start set GENSEC client password: %s\n", - nt_errstr(status))); - goto done; - } - - status = gensec_set_target_hostname(session->gensec, session->transport->socket->hostname); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n", - nt_errstr(status))); - goto done; - } - - if (session->transport->negotiate.secblob.length) { - chosen_oid = GENSEC_OID_SPNEGO; - } else { - /* without a sec blob, means raw NTLMSSP */ - chosen_oid = GENSEC_OID_NTLMSSP; - } - - status = gensec_start_mech_by_oid(session->gensec, chosen_oid); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism %s: %s\n", - gensec_get_name_by_oid(chosen_oid), nt_errstr(status))); - goto done; - } - - status = gensec_update(session->gensec, mem_ctx, - session->transport->negotiate.secblob, - &s2.spnego.in.secblob); - - while(1) { - if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) { - break; - } - - if (!NT_STATUS_IS_OK(session_key_err)) { - session_key_err = gensec_session_key(session->gensec, &session_key); - } - if (NT_STATUS_IS_OK(session_key_err)) { - smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob); - } - - if (NT_STATUS_IS_OK(status) && s2.spnego.in.secblob.length == 0) { - break; - } - - session->vuid = s2.spnego.out.vuid; - status = smb_raw_session_setup(session, mem_ctx, &s2); - session->vuid = UID_FIELD_INVALID; - if (!NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - break; - } - - status = gensec_update(session->gensec, mem_ctx, - s2.spnego.out.secblob, - &s2.spnego.in.secblob); - - } - -done: - if (NT_STATUS_IS_OK(status)) { - if (!NT_STATUS_IS_OK(session_key_err)) { - DEBUG(1, ("Failed to get user session key: %s\n", nt_errstr(session_key_err))); - return session_key_err; - } - - smbcli_session_set_user_session_key(session, &session_key); - - parms->generic.out.vuid = s2.spnego.out.vuid; - parms->generic.out.os = s2.spnego.out.os; - parms->generic.out.lanman = s2.spnego.out.lanman; - parms->generic.out.domain = s2.spnego.out.domain; - } else { - talloc_free(session->gensec); - session->gensec = NULL; - DEBUG(1, ("Failed to login with %s: %s\n", gensec_get_name_by_oid(chosen_oid), nt_errstr(status))); - return status; - } - - return status; -} - -/**************************************************************************** - Perform a session setup (sync interface) using generic interface -****************************************************************************/ -static NTSTATUS smb_raw_session_setup_generic(struct smbcli_session *session, - TALLOC_CTX *mem_ctx, - union smb_sesssetup *parms) -{ - if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) { - /* no session setup at all in earliest protocols */ - ZERO_STRUCT(parms->generic.out); - return NT_STATUS_OK; - } - - /* see if we need to use the original session setup interface */ - if (session->transport->negotiate.protocol < PROTOCOL_NT1) { - return smb_raw_session_setup_generic_old(session, mem_ctx, parms); - } - - /* see if we should use the NT1 interface */ - if (!session->transport->options.use_spnego || - !(parms->generic.in.capabilities & CAP_EXTENDED_SECURITY)) { - return smb_raw_session_setup_generic_nt1(session, mem_ctx, parms); - } - - /* default to using SPNEGO/NTLMSSP */ - return smb_raw_session_setup_generic_spnego(session, mem_ctx, parms); -} - - -/**************************************************************************** Perform a session setup (sync interface) -this interface allows for RAW_SESSSETUP_GENERIC to auto-select session -setup variant based on negotiated protocol options -****************************************************************************/ +*/ NTSTATUS smb_raw_session_setup(struct smbcli_session *session, TALLOC_CTX *mem_ctx, union smb_sesssetup *parms) { - struct smbcli_request *req; - - if (parms->generic.level == RAW_SESSSETUP_GENERIC) { - NTSTATUS ret = smb_raw_session_setup_generic(session, mem_ctx, parms); - - if (NT_STATUS_IS_OK(ret) - && parms->generic.in.user - && *parms->generic.in.user) { - if (!session->transport->negotiate.sign_info.doing_signing - && session->transport->negotiate.sign_info.mandatory_signing) { - DEBUG(0, ("SMB signing required, but server does not support it\n")); - return NT_STATUS_ACCESS_DENIED; - } - } - return ret; - } - - req = smb_raw_session_setup_send(session, parms); + struct smbcli_request *req = smb_raw_session_setup_send(session, parms); return smb_raw_session_setup_recv(req, mem_ctx, parms); } diff --git a/source4/libcli/raw/clisocket.c b/source4/libcli/raw/clisocket.c index 851cf67caa..ad1c6a13b8 100644 --- a/source4/libcli/raw/clisocket.c +++ b/source4/libcli/raw/clisocket.c @@ -1,5 +1,6 @@ /* Unix SMB/CIFS implementation. + SMB client socket context management functions Copyright (C) Andrew Tridgell 1994-2005 @@ -72,6 +73,7 @@ static void smbcli_sock_connect_handler(struct event_context *ev, struct fd_even c->status = socket_connect_complete(conn->sock->sock, 0); if (NT_STATUS_IS_OK(c->status)) { socket_set_option(conn->sock->sock, lp_socket_options(), NULL); + conn->sock->hostname = talloc_strdup(conn->sock, conn->dest_host); c->state = SMBCLI_REQUEST_DONE; if (c->async.fn) { c->async.fn(c); diff --git a/source4/libcli/raw/clitransport.c b/source4/libcli/raw/clitransport.c index 14c0779968..55a7e25f72 100644 --- a/source4/libcli/raw/clitransport.c +++ b/source4/libcli/raw/clitransport.c @@ -1,7 +1,8 @@ /* Unix SMB/CIFS implementation. SMB client transport context management functions - Copyright (C) Andrew Tridgell 1994-2003 + + Copyright (C) Andrew Tridgell 1994-2005 Copyright (C) James Myers 2003 This program is free software; you can redistribute it and/or modify diff --git a/source4/libcli/raw/clitree.c b/source4/libcli/raw/clitree.c index 7339ca07f1..c6b3fa5ad9 100644 --- a/source4/libcli/raw/clitree.c +++ b/source4/libcli/raw/clitree.c @@ -1,7 +1,9 @@ /* Unix SMB/CIFS implementation. + SMB client tree context management functions - Copyright (C) Andrew Tridgell 1994-1998 + + Copyright (C) Andrew Tridgell 1994-2005 Copyright (C) James Myers 2003 This program is free software; you can redistribute it and/or modify @@ -152,8 +154,7 @@ NTSTATUS smb_tree_disconnect(struct smbcli_tree *tree) /* - a convenient function to establish a smbcli_tree from scratch, using reasonable default - parameters + a convenient function to establish a smbcli_tree from scratch */ NTSTATUS smbcli_tree_full_connection(TALLOC_CTX *parent_ctx, struct smbcli_tree **ret_tree, @@ -162,146 +163,6 @@ NTSTATUS smbcli_tree_full_connection(TALLOC_CTX *parent_ctx, const char *service, const char *service_type, const char *user, const char *domain, const char *password) -{ - struct smbcli_socket *sock; - struct smbcli_transport *transport; - struct smbcli_session *session; - struct smbcli_tree *tree; - NTSTATUS status; - struct nmb_name calling; - struct nmb_name called; - union smb_sesssetup setup; - union smb_tcon tcon; - TALLOC_CTX *mem_ctx; - char *in_path = NULL; - - *ret_tree = NULL; - - sock = smbcli_sock_init(parent_ctx); - if (!sock) { - return NT_STATUS_NO_MEMORY; - } - - /* open a TCP socket to the server */ - if (!smbcli_sock_connect_byname(sock, dest_host, port)) { - talloc_free(sock); - DEBUG(2,("Failed to establish socket connection - %s\n", strerror(errno))); - return NT_STATUS_UNSUCCESSFUL; - } - - transport = smbcli_transport_init(sock); - talloc_free(sock); - if (!transport) { - return NT_STATUS_NO_MEMORY; - } - - /* send a NBT session request, if applicable */ - make_nmb_name(&calling, my_name, 0x0); - choose_called_name(&called, dest_host, 0x20); - - if (!smbcli_transport_connect(transport, &calling, &called)) { - talloc_free(transport); - return NT_STATUS_UNSUCCESSFUL; - } - - - /* negotiate protocol options with the server */ - status = smb_raw_negotiate(transport, lp_maxprotocol()); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(transport); - return status; - } - - session = smbcli_session_init(transport); - talloc_free(transport); - if (!session) { - return NT_STATUS_NO_MEMORY; - } - - /* prepare a session setup to establish a security context */ - setup.generic.level = RAW_SESSSETUP_GENERIC; - setup.generic.in.sesskey = transport->negotiate.sesskey; - setup.generic.in.capabilities = transport->negotiate.capabilities; - if (!user || !user[0]) { - setup.generic.in.password = NULL; - setup.generic.in.user = ""; - setup.generic.in.domain = ""; - } else { - setup.generic.in.password = password; - setup.generic.in.user = user; - setup.generic.in.domain = domain; - } - - mem_ctx = talloc_init("tcon"); - if (!mem_ctx) { - return NT_STATUS_NO_MEMORY; - } - - status = smb_raw_session_setup(session, mem_ctx, &setup); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(session); - talloc_free(mem_ctx); - return status; - } - - session->vuid = setup.generic.out.vuid; - - tree = smbcli_tree_init(session); - talloc_free(session); - if (!tree) { - talloc_free(mem_ctx); - return NT_STATUS_NO_MEMORY; - } - - /* connect to a share using a tree connect */ - tcon.generic.level = RAW_TCON_TCONX; - tcon.tconx.in.flags = 0; - tcon.tconx.in.password = data_blob(NULL, 0); - asprintf(&in_path, "\\\\%s\\%s", dest_host, service); - tcon.tconx.in.path = in_path; - if (!service_type) { - if (strequal(service, "IPC$")) - service_type = "IPC"; - else - service_type = "?????"; - } - tcon.tconx.in.device = service_type; - - status = smb_tree_connect(tree, mem_ctx, &tcon); - - SAFE_FREE(in_path); - - if (!NT_STATUS_IS_OK(status)) { - talloc_free(tree); - talloc_free(mem_ctx); - return status; - } - - tree->tid = tcon.tconx.out.tid; - if (tcon.tconx.out.dev_type) { - tree->device = talloc_strdup(tree, tcon.tconx.out.dev_type); - } - if (tcon.tconx.out.fs_type) { - tree->fs_type = talloc_strdup(tree, tcon.tconx.out.fs_type); - } - - talloc_free(mem_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; -- cgit