diff options
Diffstat (limited to 'source3/smbd/smb1_sesssetup.c')
-rw-r--r-- | source3/smbd/smb1_sesssetup.c | 1118 |
1 files changed, 1118 insertions, 0 deletions
diff --git a/source3/smbd/smb1_sesssetup.c b/source3/smbd/smb1_sesssetup.c new file mode 100644 index 0000000..a812d37 --- /dev/null +++ b/source3/smbd/smb1_sesssetup.c @@ -0,0 +1,1118 @@ +/* + Unix SMB/CIFS implementation. + handle SMBsessionsetup + Copyright (C) Andrew Tridgell 1998-2001 + Copyright (C) Andrew Bartlett 2001 + Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002 + Copyright (C) Luke Howard 2003 + Copyright (C) Volker Lendecke 2007 + Copyright (C) Jeremy Allison 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../lib/tsocket/tsocket.h" +#include "lib/util/server_id.h" +#include "smbd/smbd.h" +#include "smbd/globals.h" +#include "auth.h" +#include "messages.h" +#include "smbprofile.h" +#include "../libcli/security/security.h" +#include "auth/gensec/gensec.h" +#include "../libcli/smb/smb_signing.h" +#include "lib/util/string_wrappers.h" +#include "source3/lib/substitute.h" + +/**************************************************************************** + Add the standard 'Samba' signature to the end of the session setup. +****************************************************************************/ + +static int push_signature(uint8_t **outbuf) +{ + char *lanman; + int result, tmp; + fstring native_os; + + result = 0; + + fstr_sprintf(native_os, "Windows %d.%d", SAMBA_MAJOR_NBT_ANNOUNCE_VERSION, + SAMBA_MINOR_NBT_ANNOUNCE_VERSION); + + tmp = message_push_string(outbuf, native_os, STR_TERMINATE); + + if (tmp == -1) return -1; + result += tmp; + + if (asprintf(&lanman, "Samba %s", samba_version_string()) != -1) { + tmp = message_push_string(outbuf, lanman, STR_TERMINATE); + SAFE_FREE(lanman); + } + else { + tmp = message_push_string(outbuf, "Samba", STR_TERMINATE); + } + + if (tmp == -1) return -1; + result += tmp; + + tmp = message_push_string(outbuf, lp_workgroup(), STR_TERMINATE); + + if (tmp == -1) return -1; + result += tmp; + + return result; +} + +/**************************************************************************** + Reply to a session setup command. + conn POINTER CAN BE NULL HERE ! +****************************************************************************/ + +static void reply_sesssetup_and_X_spnego(struct smb_request *req) +{ + const uint8_t *p; + DATA_BLOB in_blob; + DATA_BLOB out_blob = data_blob_null; + size_t bufrem; + char *tmp = NULL; + const char *native_os; + const char *native_lanman; + const char *primary_domain; + uint16_t data_blob_len = SVAL(req->vwv+7, 0); + enum remote_arch_types ra_type = get_remote_arch(); + uint64_t vuid = req->vuid; + NTSTATUS status = NT_STATUS_OK; + struct smbXsrv_connection *xconn = req->xconn; + struct smbd_server_connection *sconn = req->sconn; + uint16_t action = 0; + bool is_authenticated = false; + NTTIME now = timeval_to_nttime(&req->request_time); + struct smbXsrv_session *session = NULL; + uint16_t smb_bufsize = SVAL(req->vwv+2, 0); + uint32_t client_caps = IVAL(req->vwv+10, 0); + struct smbXsrv_session_auth0 *auth; + + DEBUG(3,("Doing spnego session setup\n")); + + if (!xconn->smb1.sessions.done_sesssetup) { + global_client_caps = client_caps; + + if (!(global_client_caps & CAP_STATUS32)) { + remove_from_common_flags2(FLAGS2_32_BIT_ERROR_CODES); + } + } + + p = req->buf; + + if (data_blob_len == 0) { + /* an invalid request */ + reply_nterror(req, nt_status_squash(NT_STATUS_LOGON_FAILURE)); + return; + } + + bufrem = smbreq_bufrem(req, p); + /* pull the spnego blob */ + in_blob = data_blob_const(p, MIN(bufrem, data_blob_len)); + +#if 0 + file_save("negotiate.dat", in_blob.data, in_blob.length); +#endif + + p = req->buf + in_blob.length; + + p += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p, + STR_TERMINATE); + native_os = tmp ? tmp : ""; + + p += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p, + STR_TERMINATE); + native_lanman = tmp ? tmp : ""; + + p += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p, + STR_TERMINATE); + primary_domain = tmp ? tmp : ""; + + DEBUG(3,("NativeOS=[%s] NativeLanMan=[%s] PrimaryDomain=[%s]\n", + native_os, native_lanman, primary_domain)); + + if ( ra_type == RA_WIN2K ) { + /* Vista sets neither the OS or lanman strings */ + + if ( !strlen(native_os) && !strlen(native_lanman) ) + set_remote_arch(RA_VISTA); + + /* Windows 2003 doesn't set the native lanman string, + but does set primary domain which is a bug I think */ + + if ( !strlen(native_lanman) ) { + ra_lanman_string( primary_domain ); + } else { + ra_lanman_string( native_lanman ); + } + } else if ( ra_type == RA_VISTA ) { + if ( strncmp(native_os, "Mac OS X", 8) == 0 ) { + set_remote_arch(RA_OSX); + } + } + + if (vuid != 0) { + status = smb1srv_session_lookup(xconn, + vuid, now, + &session); + if (NT_STATUS_EQUAL(status, NT_STATUS_USER_SESSION_DELETED)) { + reply_force_doserror(req, ERRSRV, ERRbaduid); + return; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) { + status = NT_STATUS_OK; + } + if (NT_STATUS_IS_OK(status)) { + session->status = NT_STATUS_MORE_PROCESSING_REQUIRED; + status = NT_STATUS_MORE_PROCESSING_REQUIRED; + TALLOC_FREE(session->pending_auth); + } + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + reply_nterror(req, nt_status_squash(status)); + return; + } + } + + if (session == NULL) { + /* create a new session */ + status = smbXsrv_session_create(xconn, + now, &session); + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, nt_status_squash(status)); + return; + } + } + + status = smbXsrv_session_find_auth(session, xconn, now, &auth); + if (!NT_STATUS_IS_OK(status)) { + status = smbXsrv_session_create_auth(session, xconn, now, + 0, /* flags */ + 0, /* security */ + &auth); + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, nt_status_squash(status)); + return; + } + } + + if (auth->gensec == NULL) { + status = auth_generic_prepare(session, + xconn->remote_address, + xconn->local_address, + "SMB", + &auth->gensec); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(session); + reply_nterror(req, nt_status_squash(status)); + return; + } + + gensec_want_feature(auth->gensec, GENSEC_FEATURE_SESSION_KEY); + gensec_want_feature(auth->gensec, GENSEC_FEATURE_UNIX_TOKEN); + gensec_want_feature(auth->gensec, GENSEC_FEATURE_SMB_TRANSPORT); + + status = gensec_start_mech_by_oid(auth->gensec, + GENSEC_OID_SPNEGO); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to start SPNEGO handler!\n")); + TALLOC_FREE(session);; + reply_nterror(req, nt_status_squash(status)); + return; + } + } + + become_root(); + status = gensec_update(auth->gensec, + talloc_tos(), + in_blob, &out_blob); + unbecome_root(); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + TALLOC_FREE(session); + reply_nterror(req, nt_status_squash(status)); + return; + } + + if (NT_STATUS_IS_OK(status) && session->global->auth_session_info == NULL) { + struct auth_session_info *session_info = NULL; + + status = gensec_session_info(auth->gensec, + session, + &session_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1,("Failed to generate session_info " + "(user and group token) for session setup: %s\n", + nt_errstr(status))); + data_blob_free(&out_blob); + TALLOC_FREE(session); + reply_nterror(req, nt_status_squash(status)); + return; + } + + if (security_session_user_level(session_info, NULL) == SECURITY_GUEST) { + action |= SMB_SETUP_GUEST; + } + + session->global->signing_algo = SMB2_SIGNING_MD5_SMB1; + session->global->encryption_cipher = 0; + session->global->channels[0].signing_algo = + session->global->signing_algo; + session->global->channels[0].encryption_cipher = + session->global->encryption_cipher; + + if (session_info->session_key.length > 0) { + struct smbXsrv_session *x = session; + + status = smb2_signing_key_sign_create(x->global, + x->global->signing_algo, + &session_info->session_key, + NULL, /* no derivation */ + &x->global->signing_key); + if (!NT_STATUS_IS_OK(status)) { + data_blob_free(&out_blob); + TALLOC_FREE(session); + reply_nterror(req, status); + return; + } + x->global->signing_key_blob = x->global->signing_key->blob; + + /* + * clear the session key + * the first tcon will add setup the application key + */ + data_blob_clear_free(&session_info->session_key); + } + + sconn->num_users++; + + if (security_session_user_level(session_info, NULL) >= SECURITY_USER) { + is_authenticated = true; + session->homes_snum = + register_homes_share(session_info->unix_info->unix_name); + } + + if (smb1_srv_is_signing_negotiated(xconn) && + is_authenticated && + smb2_signing_key_valid(session->global->signing_key)) + { + /* + * Try and turn on server signing on the first non-guest + * sessionsetup. + */ + smb1_srv_set_signing(xconn, + session->global->signing_key->blob, + data_blob_null); + } + + set_current_user_info(session_info->unix_info->sanitized_username, + session_info->unix_info->unix_name, + session_info->info->domain_name); + + session->status = NT_STATUS_OK; + session->global->auth_session_info = talloc_move(session->global, + &session_info); + session->global->auth_session_info_seqnum += 1; + session->global->channels[0].auth_session_info_seqnum = + session->global->auth_session_info_seqnum; + session->global->auth_time = now; + if (client_caps & CAP_DYNAMIC_REAUTH) { + session->global->expiration_time = + gensec_expire_time(auth->gensec); + } else { + session->global->expiration_time = + GENSEC_EXPIRE_TIME_INFINITY; + } + + if (!session_claim(session)) { + DEBUG(1, ("smb1: Failed to claim session for vuid=%llu\n", + (unsigned long long)session->global->session_wire_id)); + data_blob_free(&out_blob); + TALLOC_FREE(session); + reply_nterror(req, NT_STATUS_LOGON_FAILURE); + return; + } + + status = smbXsrv_session_update(session); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("smb1: Failed to update session for vuid=%llu - %s\n", + (unsigned long long)session->global->session_wire_id, + nt_errstr(status))); + data_blob_free(&out_blob); + TALLOC_FREE(session); + reply_nterror(req, NT_STATUS_LOGON_FAILURE); + return; + } + + if (!xconn->smb1.sessions.done_sesssetup) { + if (smb_bufsize < SMB_BUFFER_SIZE_MIN) { + reply_force_doserror(req, ERRSRV, ERRerror); + return; + } + xconn->smb1.sessions.max_send = smb_bufsize; + xconn->smb1.sessions.done_sesssetup = true; + } + + /* current_user_info is changed on new vuid */ + reload_services(sconn, conn_snum_used, true); + } else if (NT_STATUS_IS_OK(status)) { + struct auth_session_info *session_info = NULL; + + status = gensec_session_info(auth->gensec, + session, + &session_info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1,("Failed to generate session_info " + "(user and group token) for session setup: %s\n", + nt_errstr(status))); + data_blob_free(&out_blob); + TALLOC_FREE(session); + reply_nterror(req, nt_status_squash(status)); + return; + } + + if (security_session_user_level(session_info, NULL) == SECURITY_GUEST) { + action |= SMB_SETUP_GUEST; + } + + /* + * Keep the application key + */ + data_blob_clear_free(&session_info->session_key); + session_info->session_key = + session->global->auth_session_info->session_key; + talloc_steal(session_info, session_info->session_key.data); + TALLOC_FREE(session->global->auth_session_info); + + if (security_session_user_level(session_info, NULL) >= SECURITY_USER) { + session->homes_snum = + register_homes_share(session_info->unix_info->unix_name); + } + + set_current_user_info(session_info->unix_info->sanitized_username, + session_info->unix_info->unix_name, + session_info->info->domain_name); + + session->status = NT_STATUS_OK; + session->global->auth_session_info = talloc_move(session->global, + &session_info); + session->global->auth_session_info_seqnum += 1; + session->global->channels[0].auth_session_info_seqnum = + session->global->auth_session_info_seqnum; + session->global->auth_time = now; + if (client_caps & CAP_DYNAMIC_REAUTH) { + session->global->expiration_time = + gensec_expire_time(auth->gensec); + } else { + session->global->expiration_time = + GENSEC_EXPIRE_TIME_INFINITY; + } + + status = smbXsrv_session_update(session); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("smb1: Failed to update session for vuid=%llu - %s\n", + (unsigned long long)session->global->session_wire_id, + nt_errstr(status))); + data_blob_free(&out_blob); + TALLOC_FREE(session); + reply_nterror(req, NT_STATUS_LOGON_FAILURE); + return; + } + + conn_clear_vuid_caches(sconn, session->global->session_wire_id); + + /* current_user_info is changed on new vuid */ + reload_services(sconn, conn_snum_used, true); + } + + vuid = session->global->session_wire_id; + + reply_smb1_outbuf(req, 4, 0); + + SSVAL(req->outbuf, smb_uid, vuid); + SIVAL(req->outbuf, smb_rcls, NT_STATUS_V(status)); + SSVAL(req->outbuf, smb_vwv0, 0xFF); /* no chaining possible */ + SSVAL(req->outbuf, smb_vwv2, action); + SSVAL(req->outbuf, smb_vwv3, out_blob.length); + + if (message_push_blob(&req->outbuf, out_blob) == -1) { + data_blob_free(&out_blob); + TALLOC_FREE(session); + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + data_blob_free(&out_blob); + + if (push_signature(&req->outbuf) == -1) { + TALLOC_FREE(session); + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } +} + +/**************************************************************************** + On new VC == 0, shutdown *all* old connections and users. + It seems that only NT4.x does this. At W2K and above (XP etc.). + a new session setup with VC==0 is ignored. +****************************************************************************/ + +struct shutdown_state { + const char *ip; + size_t ip_length; + struct messaging_context *msg_ctx; +}; + +static int shutdown_other_smbds(struct smbXsrv_session_global0 *session, + void *private_data) +{ + struct shutdown_state *state = (struct shutdown_state *)private_data; + struct server_id self_pid = messaging_server_id(state->msg_ctx); + struct server_id pid = session->channels[0].server_id; + const char *addr = session->channels[0].remote_address; + const char *port_colon; + size_t addr_len; + struct server_id_buf tmp; + + DEBUG(10, ("shutdown_other_smbds: %s, %s\n", + server_id_str_buf(pid, &tmp), addr)); + + if (!process_exists(pid)) { + DEBUG(10, ("process does not exist\n")); + return 0; + } + + if (server_id_equal(&pid, &self_pid)) { + DEBUG(10, ("It's me\n")); + return 0; + } + + port_colon = strrchr(addr, ':'); + if (port_colon == NULL) { + DBG_DEBUG("addr %s in contains no port\n", addr); + return 0; + } + addr_len = port_colon - addr; + + if ((addr_len != state->ip_length) || + (strncmp(addr, state->ip, state->ip_length) != 0)) { + DEBUG(10, ("%s (%zu) does not match %s (%zu)\n", + state->ip, state->ip_length, addr, addr_len)); + return 0; + } + + DEBUG(1, ("shutdown_other_smbds: shutting down pid %u " + "(IP %s)\n", (unsigned int)procid_to_pid(&pid), + state->ip)); + + messaging_send(state->msg_ctx, pid, MSG_SHUTDOWN, + &data_blob_null); + return 0; +} + +static void setup_new_vc_session(struct smbd_server_connection *sconn) +{ + DEBUG(2,("setup_new_vc_session: New VC == 0, if NT4.x " + "compatible we would close all old resources.\n")); + + if (lp_reset_on_zero_vc()) { + char *addr; + const char *port_colon; + struct shutdown_state state; + + addr = tsocket_address_string( + sconn->remote_address, talloc_tos()); + if (addr == NULL) { + return; + } + state.ip = addr; + + port_colon = strrchr(addr, ':'); + if (port_colon == NULL) { + return; + } + state.ip_length = port_colon - addr; + state.msg_ctx = sconn->msg_ctx; + smbXsrv_session_global_traverse(shutdown_other_smbds, &state); + TALLOC_FREE(addr); + } +} + +/**************************************************************************** + Reply to a session setup command. +****************************************************************************/ + +struct reply_sesssetup_and_X_state { + struct smb_request *req; + struct auth4_context *auth_context; + struct auth_usersupplied_info *user_info; + const char *user; + const char *domain; + DATA_BLOB lm_resp; + DATA_BLOB nt_resp; + DATA_BLOB plaintext_password; +}; + +static int reply_sesssetup_and_X_state_destructor( + struct reply_sesssetup_and_X_state *state) +{ + data_blob_clear_free(&state->nt_resp); + data_blob_clear_free(&state->lm_resp); + data_blob_clear_free(&state->plaintext_password); + return 0; +} + +void reply_sesssetup_and_X(struct smb_request *req) +{ + struct reply_sesssetup_and_X_state *state = NULL; + uint64_t sess_vuid; + uint16_t smb_bufsize; + char *tmp = NULL; + fstring sub_user; /* Sanitised username for substitution */ + const char *native_os; + const char *native_lanman; + const char *primary_domain; + struct auth_session_info *session_info = NULL; + uint16_t smb_flag2 = req->flags2; + uint16_t action = 0; + bool is_authenticated = false; + NTTIME now = timeval_to_nttime(&req->request_time); + struct smbXsrv_session *session = NULL; + NTSTATUS nt_status; + struct smbXsrv_connection *xconn = req->xconn; + struct smbd_server_connection *sconn = req->sconn; + bool doencrypt = xconn->smb1.negprot.encrypted_passwords; + bool signing_allowed = false; + bool signing_mandatory = smb1_signing_is_mandatory( + xconn->smb1.signing_state); + + START_PROFILE(SMBsesssetupX); + + DEBUG(3,("wct=%d flg2=0x%x\n", req->wct, req->flags2)); + + state = talloc_zero(req, struct reply_sesssetup_and_X_state); + if (state == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + END_PROFILE(SMBsesssetupX); + return; + } + state->req = req; + talloc_set_destructor(state, reply_sesssetup_and_X_state_destructor); + + if (req->flags2 & FLAGS2_SMB_SECURITY_SIGNATURES) { + signing_allowed = true; + } + if (req->flags2 & FLAGS2_SMB_SECURITY_SIGNATURES_REQUIRED) { + signing_mandatory = true; + } + + /* + * We can call smb1_srv_set_signing_negotiated() each time. + * It finds out when it needs to turn into a noop + * itself. + */ + smb1_srv_set_signing_negotiated(xconn, + signing_allowed, + signing_mandatory); + + /* a SPNEGO session setup has 12 command words, whereas a normal + NT1 session setup has 13. See the cifs spec. */ + if (req->wct == 12 && + (req->flags2 & FLAGS2_EXTENDED_SECURITY)) { + + if (!xconn->smb1.negprot.spnego) { + DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt " + "at SPNEGO session setup when it was not " + "negotiated.\n")); + reply_nterror(req, nt_status_squash( + NT_STATUS_LOGON_FAILURE)); + END_PROFILE(SMBsesssetupX); + return; + } + + if (SVAL(req->vwv+4, 0) == 0) { + setup_new_vc_session(req->sconn); + } + + reply_sesssetup_and_X_spnego(req); + END_PROFILE(SMBsesssetupX); + return; + } + + smb_bufsize = SVAL(req->vwv+2, 0); + + if (get_Protocol() < PROTOCOL_NT1) { + uint16_t passlen1 = SVAL(req->vwv+7, 0); + + /* Never do NT status codes with protocols before NT1 as we + * don't get client caps. */ + remove_from_common_flags2(FLAGS2_32_BIT_ERROR_CODES); + + if ((passlen1 > MAX_PASS_LEN) || (passlen1 > req->buflen)) { + reply_nterror(req, nt_status_squash( + NT_STATUS_INVALID_PARAMETER)); + END_PROFILE(SMBsesssetupX); + return; + } + + if (doencrypt) { + state->lm_resp = data_blob_talloc(state, + req->buf, + passlen1); + } else { + state->plaintext_password = data_blob_talloc(state, + req->buf, + passlen1+1); + /* Ensure null termination */ + state->plaintext_password.data[passlen1] = 0; + } + + srvstr_pull_req_talloc(state, req, &tmp, + req->buf + passlen1, STR_TERMINATE); + state->user = tmp ? tmp : ""; + + state->domain = ""; + + } else { + uint16_t passlen1 = SVAL(req->vwv+7, 0); + uint16_t passlen2 = SVAL(req->vwv+8, 0); + enum remote_arch_types ra_type = get_remote_arch(); + const uint8_t *p = req->buf; + const uint8_t *save_p = req->buf; + uint16_t byte_count; + + if (!xconn->smb1.sessions.done_sesssetup) { + global_client_caps = IVAL(req->vwv+11, 0); + + if (!(global_client_caps & CAP_STATUS32)) { + remove_from_common_flags2( + FLAGS2_32_BIT_ERROR_CODES); + } + + /* client_caps is used as final determination if + * client is NT or Win95. This is needed to return + * the correct error codes in some circumstances. + */ + + if(ra_type == RA_WINNT || ra_type == RA_WIN2K || + ra_type == RA_WIN95) { + if(!(global_client_caps & (CAP_NT_SMBS| + CAP_STATUS32))) { + set_remote_arch( RA_WIN95); + } + } + } + + if (!doencrypt) { + /* both Win95 and WinNT stuff up the password + * lengths for non-encrypting systems. Uggh. + + if passlen1==24 its a win95 system, and its setting + the password length incorrectly. Luckily it still + works with the default code because Win95 will null + terminate the password anyway + + if passlen1>0 and passlen2>0 then maybe its a NT box + and its setting passlen2 to some random value which + really stuffs things up. we need to fix that one. */ + + if (passlen1 > 0 && passlen2 > 0 && passlen2 != 24 && + passlen2 != 1) { + passlen2 = 0; + } + } + + /* check for nasty tricks */ + if (passlen1 > MAX_PASS_LEN + || passlen1 > smbreq_bufrem(req, p)) { + reply_nterror(req, nt_status_squash( + NT_STATUS_INVALID_PARAMETER)); + END_PROFILE(SMBsesssetupX); + return; + } + + if (passlen2 > MAX_PASS_LEN + || passlen2 > smbreq_bufrem(req, p+passlen1)) { + reply_nterror(req, nt_status_squash( + NT_STATUS_INVALID_PARAMETER)); + END_PROFILE(SMBsesssetupX); + return; + } + + /* Save the lanman2 password and the NT md4 password. */ + + if ((doencrypt) && (passlen1 != 0) && (passlen1 != 24)) { + doencrypt = False; + } + + if (doencrypt) { + state->lm_resp = data_blob_talloc(state, p, passlen1); + state->nt_resp = data_blob_talloc(state, p+passlen1, passlen2); + } else { + char *pass = NULL; + bool unic= smb_flag2 & FLAGS2_UNICODE_STRINGS; + + if (unic && (passlen2 == 0) && passlen1) { + /* Only a ascii plaintext password was sent. */ + (void)srvstr_pull_talloc(state, + req->inbuf, + req->flags2, + &pass, + req->buf, + passlen1, + STR_TERMINATE|STR_ASCII); + } else { + (void)srvstr_pull_talloc(state, + req->inbuf, + req->flags2, + &pass, + req->buf, + unic ? passlen2 : passlen1, + STR_TERMINATE); + } + if (!pass) { + reply_nterror(req, nt_status_squash( + NT_STATUS_INVALID_PARAMETER)); + END_PROFILE(SMBsesssetupX); + return; + } + state->plaintext_password = data_blob_talloc(state, + pass, + strlen(pass)+1); + } + + p += passlen1 + passlen2; + + p += srvstr_pull_req_talloc(state, req, &tmp, p, + STR_TERMINATE); + state->user = tmp ? tmp : ""; + + p += srvstr_pull_req_talloc(state, req, &tmp, p, + STR_TERMINATE); + state->domain = tmp ? tmp : ""; + + p += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p, + STR_TERMINATE); + native_os = tmp ? tmp : ""; + + p += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p, + STR_TERMINATE); + native_lanman = tmp ? tmp : ""; + + /* not documented or decoded by Ethereal but there is one more + * string in the extra bytes which is the same as the + * PrimaryDomain when using extended security. Windows NT 4 + * and 2003 use this string to store the native lanman string. + * Windows 9x does not include a string here at all so we have + * to check if we have any extra bytes left */ + + byte_count = SVAL(req->vwv+13, 0); + if ( PTR_DIFF(p, save_p) < byte_count) { + p += srvstr_pull_req_talloc(talloc_tos(), req, &tmp, p, + STR_TERMINATE); + primary_domain = tmp ? tmp : ""; + } else { + primary_domain = talloc_strdup(talloc_tos(), "null"); + } + + DEBUG(3,("Domain=[%s] NativeOS=[%s] NativeLanMan=[%s] " + "PrimaryDomain=[%s]\n", + state->domain, native_os, native_lanman, primary_domain)); + + if ( ra_type == RA_WIN2K ) { + if ( strlen(native_lanman) == 0 ) + ra_lanman_string( primary_domain ); + else + ra_lanman_string( native_lanman ); + } + + } + + if (SVAL(req->vwv+4, 0) == 0) { + setup_new_vc_session(req->sconn); + } + + DEBUG(3,("sesssetupX:name=[%s]\\[%s]@[%s]\n", + state->domain, state->user, get_remote_machine_name())); + + if (*state->user) { + if (xconn->smb1.negprot.spnego) { + + /* This has to be here, because this is a perfectly + * valid behaviour for guest logons :-( */ + + DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt " + "at 'normal' session setup after " + "negotiating spnego.\n")); + reply_nterror(req, nt_status_squash( + NT_STATUS_LOGON_FAILURE)); + END_PROFILE(SMBsesssetupX); + return; + } + fstrcpy(sub_user, state->user); + } else { + fstrcpy(sub_user, ""); + } + + if (!*state->user) { + DEBUG(3,("Got anonymous request\n")); + + nt_status = make_auth4_context(state, &state->auth_context); + if (NT_STATUS_IS_OK(nt_status)) { + uint8_t chal[8]; + + state->auth_context->get_ntlm_challenge( + state->auth_context, chal); + + if (!make_user_info_guest(state, + sconn->remote_address, + sconn->local_address, + "SMB", &state->user_info)) { + nt_status = NT_STATUS_NO_MEMORY; + } + + if (NT_STATUS_IS_OK(nt_status)) { + state->user_info->auth_description = "guest"; + } + } + } else if (doencrypt) { + state->auth_context = xconn->smb1.negprot.auth_context; + if (state->auth_context == NULL) { + DEBUG(0, ("reply_sesssetup_and_X: Attempted encrypted " + "session setup without negprot denied!\n")); + reply_nterror(req, nt_status_squash( + NT_STATUS_LOGON_FAILURE)); + END_PROFILE(SMBsesssetupX); + return; + } + nt_status = make_user_info_for_reply_enc(state, + &state->user_info, + state->user, + state->domain, + sconn->remote_address, + sconn->local_address, + "SMB", + state->lm_resp, + state->nt_resp); + + if (NT_STATUS_IS_OK(nt_status)) { + state->user_info->auth_description = "bare-NTLM"; + } + } else { + nt_status = make_auth4_context(state, &state->auth_context); + if (NT_STATUS_IS_OK(nt_status)) { + uint8_t chal[8]; + + state->auth_context->get_ntlm_challenge( + state->auth_context, chal); + + if (!make_user_info_for_reply(state, + &state->user_info, + state->user, + state->domain, + sconn->remote_address, + sconn->local_address, + "SMB", + chal, + state->plaintext_password)) { + nt_status = NT_STATUS_NO_MEMORY; + } + + if (NT_STATUS_IS_OK(nt_status)) { + state->user_info->auth_description = "plaintext"; + } + } + } + + if (!NT_STATUS_IS_OK(nt_status)) { + reply_nterror(req, nt_status_squash(nt_status)); + END_PROFILE(SMBsesssetupX); + return; + } + + nt_status = auth_check_password_session_info(state->auth_context, + req, state->user_info, + &session_info); + TALLOC_FREE(state->user_info); + if (!NT_STATUS_IS_OK(nt_status)) { + reply_nterror(req, nt_status_squash(nt_status)); + END_PROFILE(SMBsesssetupX); + return; + } + + /* it's ok - setup a reply */ + reply_smb1_outbuf(req, 3, 0); + SSVAL(req->outbuf, smb_vwv0, 0xff); /* andx chain ends */ + SSVAL(req->outbuf, smb_vwv1, 0); /* no andx offset */ + + if (get_Protocol() >= PROTOCOL_NT1) { + push_signature(&req->outbuf); + /* perhaps grab OS version here?? */ + } + + if (security_session_user_level(session_info, NULL) == SECURITY_GUEST) { + action |= SMB_SETUP_GUEST; + } + + /* register the name and uid as being validated, so further connections + to a uid can get through without a password, on the same VC */ + + nt_status = smbXsrv_session_create(xconn, + now, &session); + if (!NT_STATUS_IS_OK(nt_status)) { + reply_nterror(req, nt_status_squash(nt_status)); + END_PROFILE(SMBsesssetupX); + return; + } + + session->global->signing_algo = SMB2_SIGNING_MD5_SMB1; + session->global->encryption_cipher = 0; + session->global->channels[0].signing_algo = + session->global->signing_algo; + session->global->channels[0].encryption_cipher = + session->global->encryption_cipher; + + if (session_info->session_key.length > 0) { + struct smbXsrv_session *x = session; + uint8_t session_key[16]; + NTSTATUS status; + + status = smb2_signing_key_sign_create(x->global, + x->global->signing_algo, + &session_info->session_key, + NULL, /* no derivation */ + &x->global->signing_key); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(session); + reply_nterror(req, status); + END_PROFILE(SMBsesssetupX); + return; + } + x->global->signing_key_blob = x->global->signing_key->blob; + + /* + * The application key is truncated/padded to 16 bytes + */ + ZERO_STRUCT(session_key); + memcpy(session_key, session->global->signing_key_blob.data, + MIN(session->global->signing_key_blob.length, + sizeof(session_key))); + session->global->application_key_blob = + data_blob_talloc(session->global, + session_key, + sizeof(session_key)); + ZERO_STRUCT(session_key); + if (session->global->application_key_blob.data == NULL) { + TALLOC_FREE(session); + reply_nterror(req, NT_STATUS_NO_MEMORY); + END_PROFILE(SMBsesssetupX); + return; + } + talloc_keep_secret(session->global->application_key_blob.data); + + /* + * Place the application key into the session_info + */ + data_blob_clear_free(&session_info->session_key); + session_info->session_key = data_blob_dup_talloc(session_info, + session->global->application_key_blob); + if (session_info->session_key.data == NULL) { + TALLOC_FREE(session); + reply_nterror(req, NT_STATUS_NO_MEMORY); + END_PROFILE(SMBsesssetupX); + return; + } + talloc_keep_secret(session_info->session_key.data); + } + + sconn->num_users++; + + if (security_session_user_level(session_info, NULL) >= SECURITY_USER) { + is_authenticated = true; + session->homes_snum = + register_homes_share(session_info->unix_info->unix_name); + } + + if (smb1_srv_is_signing_negotiated(xconn) && + is_authenticated && + smb2_signing_key_valid(session->global->signing_key)) + { + /* + * Try and turn on server signing on the first non-guest + * sessionsetup. + */ + smb1_srv_set_signing(xconn, + session->global->signing_key->blob, + state->nt_resp.data ? state->nt_resp : state->lm_resp); + } + + set_current_user_info(session_info->unix_info->sanitized_username, + session_info->unix_info->unix_name, + session_info->info->domain_name); + + session->status = NT_STATUS_OK; + session->global->auth_session_info = talloc_move(session->global, + &session_info); + session->global->auth_session_info_seqnum += 1; + session->global->channels[0].auth_session_info_seqnum = + session->global->auth_session_info_seqnum; + session->global->auth_time = now; + session->global->expiration_time = GENSEC_EXPIRE_TIME_INFINITY; + + nt_status = smbXsrv_session_update(session); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("smb1: Failed to update session for vuid=%llu - %s\n", + (unsigned long long)session->global->session_wire_id, + nt_errstr(nt_status))); + TALLOC_FREE(session); + reply_nterror(req, nt_status_squash(nt_status)); + END_PROFILE(SMBsesssetupX); + return; + } + + if (!session_claim(session)) { + DEBUG(1, ("smb1: Failed to claim session for vuid=%llu\n", + (unsigned long long)session->global->session_wire_id)); + TALLOC_FREE(session); + reply_nterror(req, NT_STATUS_LOGON_FAILURE); + END_PROFILE(SMBsesssetupX); + return; + } + + /* current_user_info is changed on new vuid */ + reload_services(sconn, conn_snum_used, true); + + sess_vuid = session->global->session_wire_id; + + SSVAL(req->outbuf,smb_vwv2,action); + SSVAL(req->outbuf,smb_uid,sess_vuid); + SSVAL(discard_const_p(char, req->inbuf),smb_uid,sess_vuid); + req->vuid = sess_vuid; + + if (!xconn->smb1.sessions.done_sesssetup) { + if (smb_bufsize < SMB_BUFFER_SIZE_MIN) { + reply_force_doserror(req, ERRSRV, ERRerror); + END_PROFILE(SMBsesssetupX); + return; + } + xconn->smb1.sessions.max_send = smb_bufsize; + xconn->smb1.sessions.done_sesssetup = true; + } + + TALLOC_FREE(state); + END_PROFILE(SMBsesssetupX); +} |