diff options
author | Andrew Tridgell <tridge@samba.org> | 2005-01-16 11:15:08 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:08:53 -0500 |
commit | 7cbc768376ed0a839afca64aeea99cd53d0fbc6f (patch) | |
tree | 116b18a6fc59ec306275f32c136201f83708e5fc /source4/libcli/composite/sesssetup.c | |
parent | 6eabc2a711446819e0694bd56eb71ea7f101ae66 (diff) | |
download | samba-7cbc768376ed0a839afca64aeea99cd53d0fbc6f.tar.gz samba-7cbc768376ed0a839afca64aeea99cd53d0fbc6f.tar.bz2 samba-7cbc768376ed0a839afca64aeea99cd53d0fbc6f.zip |
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)
Diffstat (limited to 'source4/libcli/composite/sesssetup.c')
-rw-r--r-- | source4/libcli/composite/sesssetup.c | 403 |
1 files changed, 403 insertions, 0 deletions
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); +} |