X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source3%2Fsmbd%2Fsesssetup.c;h=320d2835752326f00879fda066cc36d396e11350;hb=5e54558c6dea67b56bbfaba5698f3a434d3dffb6;hp=40ea28a86d514fd50f9661e45e8a19e5dc64ee9a;hpb=2257618d7f282ebc7b99279728b4f05c6593c02b;p=vlendec%2Fsamba-autobuild%2F.git diff --git a/source3/smbd/sesssetup.c b/source3/smbd/sesssetup.c index 40ea28a86d5..320d2835752 100644 --- a/source3/smbd/sesssetup.c +++ b/source3/smbd/sesssetup.c @@ -8,7 +8,7 @@ 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 + 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, @@ -17,15 +17,18 @@ 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. + along with this program. If not, see . */ #include "includes.h" -uint32 global_client_caps = 0; +extern struct auth_context *negprot_global_auth_context; +extern BOOL global_encrypted_passwords_negotiated; +extern BOOL global_spnego_negotiated; +extern enum protocol_types Protocol; +extern int max_send; -static struct auth_ntlmssp_state *global_ntlmssp_state; +uint32 global_client_caps = 0; /* on a logon error possibly map the error to success if "map to guest" @@ -71,31 +74,56 @@ static int add_signature(char *outbuf, char *p) return PTR_DIFF(p, start); } +/**************************************************************************** + Start the signing engine if needed. Don't fail signing here. +****************************************************************************/ + +static void sessionsetup_start_signing_engine(const auth_serversupplied_info *server_info, char *inbuf) +{ + if (!server_info->guest && !srv_signing_started()) { + /* We need to start the signing engine + * here but a W2K client sends the old + * "BSRSPYL " signature instead of the + * correct one. Subsequent packets will + * be correct. + */ + srv_check_sign_mac(inbuf, False); + } +} + /**************************************************************************** Send a security blob via a session setup reply. ****************************************************************************/ -static BOOL reply_sesssetup_blob(connection_struct *conn, char *outbuf, - DATA_BLOB blob, NTSTATUS nt_status) +static BOOL reply_sesssetup_blob(connection_struct *conn, + const char *inbuf, + char *outbuf, + DATA_BLOB blob, + NTSTATUS nt_status) { char *p; - set_message(outbuf,4,0,True); + if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + ERROR_NT(nt_status_squash(nt_status)); + } else { + set_message(inbuf,outbuf,4,0,True); - nt_status = nt_status_squash(nt_status); - SIVAL(outbuf, smb_rcls, NT_STATUS_V(nt_status)); - SSVAL(outbuf, smb_vwv0, 0xFF); /* no chaining possible */ - SSVAL(outbuf, smb_vwv3, blob.length); - p = smb_buf(outbuf); + nt_status = nt_status_squash(nt_status); + SIVAL(outbuf, smb_rcls, NT_STATUS_V(nt_status)); + SSVAL(outbuf, smb_vwv0, 0xFF); /* no chaining possible */ + SSVAL(outbuf, smb_vwv3, blob.length); + p = smb_buf(outbuf); - /* should we cap this? */ - memcpy(p, blob.data, blob.length); - p += blob.length; + /* should we cap this? */ + memcpy(p, blob.data, blob.length); + p += blob.length; - p += add_signature( outbuf, p ); + p += add_signature( outbuf, p ); - set_message_end(outbuf,p); + set_message_end(inbuf,outbuf,p); + } + show_msg(outbuf); return send_smb(smbd_server_fd(),outbuf); } @@ -132,49 +160,167 @@ static NTSTATUS check_guest_password(auth_serversupplied_info **server_info) #ifdef HAVE_KRB5 + +#if 0 +/* Experiment that failed. See "only happens with a KDC" comment below. */ /**************************************************************************** -reply to a session setup spnego negotiate packet for kerberos + Cerate a clock skew error blob for a Windows client. ****************************************************************************/ + +static BOOL make_krb5_skew_error(DATA_BLOB *pblob_out) +{ + krb5_context context = NULL; + krb5_error_code kerr = 0; + krb5_data reply; + krb5_principal host_princ = NULL; + char *host_princ_s = NULL; + BOOL ret = False; + + *pblob_out = data_blob_null; + + initialize_krb5_error_table(); + kerr = krb5_init_context(&context); + if (kerr) { + return False; + } + /* Create server principal. */ + asprintf(&host_princ_s, "%s$@%s", global_myname(), lp_realm()); + if (!host_princ_s) { + goto out; + } + strlower_m(host_princ_s); + + kerr = smb_krb5_parse_name(context, host_princ_s, &host_princ); + if (kerr) { + DEBUG(10,("make_krb5_skew_error: smb_krb5_parse_name failed for name %s: Error %s\n", + host_princ_s, error_message(kerr) )); + goto out; + } + + kerr = smb_krb5_mk_error(context, KRB5KRB_AP_ERR_SKEW, host_princ, &reply); + if (kerr) { + DEBUG(10,("make_krb5_skew_error: smb_krb5_mk_error failed: Error %s\n", + error_message(kerr) )); + goto out; + } + + *pblob_out = data_blob(reply.data, reply.length); + kerberos_free_data_contents(context,&reply); + ret = True; + + out: + + if (host_princ_s) { + SAFE_FREE(host_princ_s); + } + if (host_princ) { + krb5_free_principal(context, host_princ); + } + krb5_free_context(context); + return ret; +} +#endif + +/**************************************************************************** + Reply to a session setup spnego negotiate packet for kerberos. +****************************************************************************/ + static int reply_spnego_kerberos(connection_struct *conn, char *inbuf, char *outbuf, int length, int bufsize, - DATA_BLOB *secblob) + DATA_BLOB *secblob, + BOOL *p_invalidate_vuid) { + TALLOC_CTX *mem_ctx; DATA_BLOB ticket; char *client, *p, *domain; fstring netbios_domain_name; struct passwd *pw; - char *user; + fstring user; int sess_vuid; NTSTATUS ret; - DATA_BLOB auth_data; + PAC_DATA *pac_data; DATA_BLOB ap_rep, ap_rep_wrapped, response; auth_serversupplied_info *server_info = NULL; - DATA_BLOB session_key = data_blob(NULL, 0); + DATA_BLOB session_key = data_blob_null; uint8 tok_id[2]; - DATA_BLOB nullblob = data_blob(NULL, 0); + DATA_BLOB nullblob = data_blob_null; fstring real_username; + BOOL map_domainuser_to_guest = False; + BOOL username_was_mapped; + PAC_LOGON_INFO *logon_info = NULL; ZERO_STRUCT(ticket); - ZERO_STRUCT(auth_data); + ZERO_STRUCT(pac_data); ZERO_STRUCT(ap_rep); ZERO_STRUCT(ap_rep_wrapped); ZERO_STRUCT(response); + /* Normally we will always invalidate the intermediate vuid. */ + *p_invalidate_vuid = True; + + mem_ctx = talloc_init("reply_spnego_kerberos"); + if (mem_ctx == NULL) { + return ERROR_NT(nt_status_squash(NT_STATUS_NO_MEMORY)); + } + if (!spnego_parse_krb5_wrap(*secblob, &ticket, tok_id)) { - return ERROR_NT(NT_STATUS_LOGON_FAILURE); + talloc_destroy(mem_ctx); + return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } - ret = ads_verify_ticket(lp_realm(), &ticket, &client, &auth_data, &ap_rep, &session_key); + ret = ads_verify_ticket(mem_ctx, lp_realm(), 0, &ticket, + &client, &pac_data, &ap_rep, + &session_key, True); data_blob_free(&ticket); if (!NT_STATUS_IS_OK(ret)) { - DEBUG(1,("Failed to verify incoming ticket!\n")); - return ERROR_NT(NT_STATUS_LOGON_FAILURE); - } +#if 0 + /* Experiment that failed. See "only happens with a KDC" comment below. */ + + if (NT_STATUS_EQUAL(ret, NT_STATUS_TIME_DIFFERENCE_AT_DC)) { + + /* + * Windows in this case returns NT_STATUS_MORE_PROCESSING_REQUIRED + * with a negTokenTarg blob containing an krb5_error struct ASN1 encoded + * containing KRB5KRB_AP_ERR_SKEW. The client then fixes its + * clock and continues rather than giving an error. JRA. + * -- Looks like this only happens with a KDC. JRA. + */ + + BOOL ok = make_krb5_skew_error(&ap_rep); + if (!ok) { + talloc_destroy(mem_ctx); + return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); + } + ap_rep_wrapped = spnego_gen_krb5_wrap(ap_rep, TOK_ID_KRB_ERROR); + response = spnego_gen_auth_response(&ap_rep_wrapped, ret, OID_KERBEROS5_OLD); + reply_sesssetup_blob(conn, inbuf, outbuf, response, NT_STATUS_MORE_PROCESSING_REQUIRED); + + /* + * In this one case we don't invalidate the intermediate vuid. + * as we're expecting the client to re-use it for the next + * sessionsetupX packet. JRA. + */ - data_blob_free(&auth_data); + *p_invalidate_vuid = False; + + data_blob_free(&ap_rep); + data_blob_free(&ap_rep_wrapped); + data_blob_free(&response); + talloc_destroy(mem_ctx); + return -1; /* already replied */ + } +#else + if (!NT_STATUS_EQUAL(ret, NT_STATUS_TIME_DIFFERENCE_AT_DC)) { + ret = NT_STATUS_LOGON_FAILURE; + } +#endif + DEBUG(1,("Failed to verify incoming ticket with error %s!\n", nt_errstr(ret))); + talloc_destroy(mem_ctx); + return ERROR_NT(nt_status_squash(ret)); + } DEBUG(3,("Ticket name is [%s]\n", client)); @@ -184,17 +330,29 @@ static int reply_spnego_kerberos(connection_struct *conn, data_blob_free(&ap_rep); data_blob_free(&session_key); SAFE_FREE(client); - return ERROR_NT(NT_STATUS_LOGON_FAILURE); + talloc_destroy(mem_ctx); + return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } *p = 0; + + /* save the PAC data if we have it */ + + if (pac_data) { + logon_info = get_logon_info_from_pac(pac_data); + if (logon_info) { + netsamlogon_cache_store( client, &logon_info->info3 ); + } + } + if (!strequal(p+1, lp_realm())) { DEBUG(3,("Ticket for foreign realm %s@%s\n", client, p+1)); if (!lp_allow_trusted_domains()) { data_blob_free(&ap_rep); data_blob_free(&session_key); SAFE_FREE(client); - return ERROR_NT(NT_STATUS_LOGON_FAILURE); + talloc_destroy(mem_ctx); + return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } } @@ -203,7 +361,14 @@ static int reply_spnego_kerberos(connection_struct *conn, domain = p+1; - { + if (logon_info && logon_info->info3.hdr_logon_dom.uni_str_len) { + + unistr2_to_ascii(netbios_domain_name, &logon_info->info3.uni_logon_dom, -1); + domain = netbios_domain_name; + DEBUG(10, ("Mapped to [%s] (using PAC)\n", domain)); + + } else { + /* If we have winbind running, we can (and must) shorten the username by using the short netbios name. Otherwise we will have inconsistent user names. With Kerberos, we get the @@ -222,7 +387,7 @@ static int reply_spnego_kerberos(connection_struct *conn, fstrcpy(wb_request.domain_name, domain); - wb_result = winbindd_request(WINBINDD_DOMAIN_INFO, + wb_result = winbindd_request_response(WINBINDD_DOMAIN_INFO, &wb_request, &wb_response); if (wb_result == NSS_STATUS_SUCCESS) { @@ -231,67 +396,135 @@ static int reply_spnego_kerberos(connection_struct *conn, wb_response.data.domain_info.name); domain = netbios_domain_name; - DEBUG(10, ("Mapped to [%s]\n", domain)); + DEBUG(10, ("Mapped to [%s] (using Winbind)\n", domain)); } else { DEBUG(3, ("Could not find short name -- winbind " "not running?\n")); } } - asprintf(&user, "%s%c%s", domain, *lp_winbind_separator(), client); + fstr_sprintf(user, "%s%c%s", domain, *lp_winbind_separator(), client); /* lookup the passwd struct, create a new user if necessary */ - map_username( user ); + username_was_mapped = map_username( user ); + + pw = smb_getpwnam( mem_ctx, user, real_username, True ); + + if (pw) { + /* if a real user check pam account restrictions */ + /* only really perfomed if "obey pam restriction" is true */ + /* do this before an eventual mappign to guest occurs */ + ret = smb_pam_accountcheck(pw->pw_name); + if ( !NT_STATUS_IS_OK(ret)) { + DEBUG(1, ("PAM account restriction prevents user login\n")); + data_blob_free(&ap_rep); + data_blob_free(&session_key); + TALLOC_FREE(mem_ctx); + return ERROR_NT(nt_status_squash(ret)); + } + } - pw = smb_getpwnam( user, real_username, True ); - if (!pw) { - DEBUG(1,("Username %s is invalid on this system\n",user)); - SAFE_FREE(user); - SAFE_FREE(client); - data_blob_free(&ap_rep); - data_blob_free(&session_key); - return ERROR_NT(NT_STATUS_LOGON_FAILURE); + + /* this was originally the behavior of Samba 2.2, if a user + did not have a local uid but has been authenticated, then + map them to a guest account */ + + if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_UID){ + map_domainuser_to_guest = True; + fstrcpy(user,lp_guestaccount()); + pw = smb_getpwnam( mem_ctx, user, real_username, True ); + } + + /* extra sanity check that the guest account is valid */ + + if ( !pw ) { + DEBUG(1,("Username %s is invalid on this system\n", user)); + SAFE_FREE(client); + data_blob_free(&ap_rep); + data_blob_free(&session_key); + TALLOC_FREE(mem_ctx); + return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); + } } /* setup the string used by %U */ sub_set_smb_name( real_username ); reload_services(True); - - if (!NT_STATUS_IS_OK(ret = make_server_info_pw(&server_info, real_username, pw))) - { - DEBUG(1,("make_server_info_from_pw failed!\n")); - SAFE_FREE(user); - SAFE_FREE(client); - data_blob_free(&ap_rep); - data_blob_free(&session_key); - return ERROR_NT(ret); - } - /* make_server_info_pw does not set the domain. Without this we end up - * with the local netbios name in substitutions for %D. */ + if ( map_domainuser_to_guest ) { + make_server_info_guest(&server_info); + } else if (logon_info) { + /* pass the unmapped username here since map_username() + will be called again from inside make_server_info_info3() */ + + ret = make_server_info_info3(mem_ctx, client, domain, + &server_info, &logon_info->info3); + if ( !NT_STATUS_IS_OK(ret) ) { + DEBUG(1,("make_server_info_info3 failed: %s!\n", + nt_errstr(ret))); + SAFE_FREE(client); + data_blob_free(&ap_rep); + data_blob_free(&session_key); + TALLOC_FREE(mem_ctx); + return ERROR_NT(nt_status_squash(ret)); + } + + } else { + ret = make_server_info_pw(&server_info, real_username, pw); + + if ( !NT_STATUS_IS_OK(ret) ) { + DEBUG(1,("make_server_info_pw failed: %s!\n", + nt_errstr(ret))); + SAFE_FREE(client); + data_blob_free(&ap_rep); + data_blob_free(&session_key); + TALLOC_FREE(mem_ctx); + return ERROR_NT(nt_status_squash(ret)); + } + + /* make_server_info_pw does not set the domain. Without this + * we end up with the local netbios name in substitutions for + * %D. */ + + if (server_info->sam_account != NULL) { + pdb_set_domain(server_info->sam_account, domain, PDB_SET); + } + } - if (server_info->sam_account != NULL) { - pdb_set_domain(server_info->sam_account, domain, PDB_SET); - } + server_info->was_mapped |= username_was_mapped; + + /* we need to build the token for the user. make_server_info_guest() + already does this */ + + if ( !server_info->ptok ) { + ret = create_local_token( server_info ); + if ( !NT_STATUS_IS_OK(ret) ) { + SAFE_FREE(client); + data_blob_free(&ap_rep); + data_blob_free(&session_key); + TALLOC_FREE( mem_ctx ); + TALLOC_FREE( server_info ); + return ERROR_NT(nt_status_squash(ret)); + } + } /* register_vuid keeps the server info */ /* register_vuid takes ownership of session_key, no need to free after this. A better interface would copy it.... */ sess_vuid = register_vuid(server_info, session_key, nullblob, client); - SAFE_FREE(user); SAFE_FREE(client); - if (sess_vuid == -1) { + if (sess_vuid == UID_FIELD_INVALID ) { ret = NT_STATUS_LOGON_FAILURE; } else { /* current_user_info is changed on new vuid */ reload_services( True ); - set_message(outbuf,4,0,True); + set_message(inbuf,outbuf,4,0,True); SSVAL(outbuf, smb_vwv3, 0); if (server_info->guest) { @@ -300,29 +533,22 @@ static int reply_spnego_kerberos(connection_struct *conn, SSVAL(outbuf, smb_uid, sess_vuid); - if (!server_info->guest && !srv_signing_started()) { - /* We need to start the signing engine - * here but a W2K client sends the old - * "BSRSPYL " signature instead of the - * correct one. Subsequent packets will - * be correct. - */ - srv_check_sign_mac(inbuf, False); - } + sessionsetup_start_signing_engine(server_info, inbuf); } /* wrap that up in a nice GSS-API wrapping */ if (NT_STATUS_IS_OK(ret)) { ap_rep_wrapped = spnego_gen_krb5_wrap(ap_rep, TOK_ID_KRB_AP_REP); } else { - ap_rep_wrapped = data_blob(NULL, 0); + ap_rep_wrapped = data_blob_null; } response = spnego_gen_auth_response(&ap_rep_wrapped, ret, OID_KERBEROS5_OLD); - reply_sesssetup_blob(conn, outbuf, response, ret); + reply_sesssetup_blob(conn, inbuf, outbuf, response, ret); data_blob_free(&ap_rep); data_blob_free(&ap_rep_wrapped); data_blob_free(&response); + TALLOC_FREE(mem_ctx); return -1; /* already replied */ } @@ -332,11 +558,15 @@ static int reply_spnego_kerberos(connection_struct *conn, Send a session setup reply, wrapped in SPNEGO. Get vuid and check first. End the NTLMSSP exchange context if we are OK/complete fail + This should be split into two functions, one to handle each + leg of the NTLM auth steps. ***************************************************************************/ static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *inbuf, char *outbuf, + uint16 vuid, AUTH_NTLMSSP_STATE **auth_ntlmssp_state, - DATA_BLOB *ntlmssp_blob, NTSTATUS nt_status) + DATA_BLOB *ntlmssp_blob, NTSTATUS nt_status, + BOOL wrap) { BOOL ret; DATA_BLOB response; @@ -353,21 +583,21 @@ static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *inbuf, char *out if (NT_STATUS_IS_OK(nt_status)) { int sess_vuid; - DATA_BLOB nullblob = data_blob(NULL, 0); + DATA_BLOB nullblob = data_blob_null; DATA_BLOB session_key = data_blob((*auth_ntlmssp_state)->ntlmssp_state->session_key.data, (*auth_ntlmssp_state)->ntlmssp_state->session_key.length); /* register_vuid keeps the server info */ sess_vuid = register_vuid(server_info, session_key, nullblob, (*auth_ntlmssp_state)->ntlmssp_state->user); (*auth_ntlmssp_state)->server_info = NULL; - if (sess_vuid == -1) { + if (sess_vuid == UID_FIELD_INVALID ) { nt_status = NT_STATUS_LOGON_FAILURE; } else { /* current_user_info is changed on new vuid */ reload_services( True ); - set_message(outbuf,4,0,True); + set_message(inbuf,outbuf,4,0,True); SSVAL(outbuf, smb_vwv3, 0); if (server_info->guest) { @@ -376,53 +606,48 @@ static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *inbuf, char *out SSVAL(outbuf,smb_uid,sess_vuid); - if (!server_info->guest && !srv_signing_started()) { - /* We need to start the signing engine - * here but a W2K client sends the old - * "BSRSPYL " signature instead of the - * correct one. Subsequent packets will - * be correct. - */ - - srv_check_sign_mac(inbuf, False); - } + sessionsetup_start_signing_engine(server_info, inbuf); } } - response = spnego_gen_auth_response(ntlmssp_blob, nt_status, OID_NTLMSSP); - ret = reply_sesssetup_blob(conn, outbuf, response, nt_status); - data_blob_free(&response); + if (wrap) { + response = spnego_gen_auth_response(ntlmssp_blob, nt_status, OID_NTLMSSP); + } else { + response = *ntlmssp_blob; + } + + ret = reply_sesssetup_blob(conn, inbuf, outbuf, response, nt_status); + if (wrap) { + data_blob_free(&response); + } /* NT_STATUS_MORE_PROCESSING_REQUIRED from our NTLMSSP code tells us, and the other end, that we are not finished yet. */ if (!ret || !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + /* NB. This is *NOT* an error case. JRA */ auth_ntlmssp_end(auth_ntlmssp_state); + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); } return ret; } /**************************************************************************** - Reply to a session setup spnego negotiate packet. + Is this a krb5 mechanism ? ****************************************************************************/ -static int reply_spnego_negotiate(connection_struct *conn, - char *inbuf, - char *outbuf, - int length, int bufsize, - DATA_BLOB blob1) +NTSTATUS parse_spnego_mechanisms(DATA_BLOB blob_in, DATA_BLOB *pblob_out, BOOL *p_is_krb5) { char *OIDs[ASN1_MAX_OIDS]; - DATA_BLOB secblob; int i; - DATA_BLOB chal; - BOOL got_kerberos = False; - NTSTATUS nt_status; + + *p_is_krb5 = False; /* parse out the OIDs and the first sec blob */ - if (!parse_negTokenTarg(blob1, OIDs, &secblob)) { - return ERROR_NT(NT_STATUS_LOGON_FAILURE); + if (!parse_negTokenTarg(blob_in, OIDs, pblob_out)) { + return NT_STATUS_LOGON_FAILURE; } /* only look at the first OID for determining the mechToken -- @@ -434,80 +659,154 @@ static int reply_spnego_negotiate(connection_struct *conn, server sent back krb5/mskrb5/ntlmssp as mechtypes, but the client (2ksp3) replied with ntlmssp/mskrb5/krb5 and an NTLMSSP mechtoken. --jerry */ - + +#ifdef HAVE_KRB5 if (strcmp(OID_KERBEROS5, OIDs[0]) == 0 || strcmp(OID_KERBEROS5_OLD, OIDs[0]) == 0) { - got_kerberos = True; + *p_is_krb5 = True; } +#endif for (i=0;OIDs[i];i++) { - DEBUG(3,("Got OID %s\n", OIDs[i])); + DEBUG(5,("parse_spnego_mechanisms: Got OID %s\n", OIDs[i])); free(OIDs[i]); } - DEBUG(3,("Got secblob of size %lu\n", (unsigned long)secblob.length)); + return NT_STATUS_OK; +} + +/**************************************************************************** + Reply to a session setup spnego negotiate packet. +****************************************************************************/ + +static int reply_spnego_negotiate(connection_struct *conn, + char *inbuf, + char *outbuf, + uint16 vuid, + int length, int bufsize, + DATA_BLOB blob1, + AUTH_NTLMSSP_STATE **auth_ntlmssp_state) +{ + DATA_BLOB secblob; + DATA_BLOB chal; + BOOL got_kerberos_mechanism = False; + NTSTATUS status; + + status = parse_spnego_mechanisms(blob1, &secblob, &got_kerberos_mechanism); + if (!NT_STATUS_IS_OK(status)) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + return ERROR_NT(nt_status_squash(status)); + } + + DEBUG(3,("reply_spnego_negotiate: Got secblob of size %lu\n", (unsigned long)secblob.length)); #ifdef HAVE_KRB5 - if (got_kerberos && (SEC_ADS == lp_security())) { + if ( got_kerberos_mechanism && ((lp_security()==SEC_ADS) || lp_use_kerberos_keytab()) ) { + BOOL destroy_vuid = True; int ret = reply_spnego_kerberos(conn, inbuf, outbuf, - length, bufsize, &secblob); + length, bufsize, &secblob, &destroy_vuid); data_blob_free(&secblob); + if (destroy_vuid) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + } return ret; } #endif - if (global_ntlmssp_state) { - auth_ntlmssp_end(&global_ntlmssp_state); + if (*auth_ntlmssp_state) { + auth_ntlmssp_end(auth_ntlmssp_state); } - nt_status = auth_ntlmssp_start(&global_ntlmssp_state); - if (!NT_STATUS_IS_OK(nt_status)) { - return ERROR_NT(nt_status); + status = auth_ntlmssp_start(auth_ntlmssp_state); + if (!NT_STATUS_IS_OK(status)) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + return ERROR_NT(nt_status_squash(status)); } - nt_status = auth_ntlmssp_update(global_ntlmssp_state, + status = auth_ntlmssp_update(*auth_ntlmssp_state, secblob, &chal); data_blob_free(&secblob); - reply_spnego_ntlmssp(conn, inbuf, outbuf, &global_ntlmssp_state, - &chal, nt_status); - + reply_spnego_ntlmssp(conn, inbuf, outbuf, vuid, auth_ntlmssp_state, + &chal, status, True); + data_blob_free(&chal); /* already replied */ return -1; } - + /**************************************************************************** Reply to a session setup spnego auth packet. ****************************************************************************/ static int reply_spnego_auth(connection_struct *conn, char *inbuf, char *outbuf, + uint16 vuid, int length, int bufsize, - DATA_BLOB blob1) + DATA_BLOB blob1, + AUTH_NTLMSSP_STATE **auth_ntlmssp_state) { - DATA_BLOB auth, auth_reply; - NTSTATUS nt_status = NT_STATUS_INVALID_PARAMETER; + DATA_BLOB auth = data_blob_null; + DATA_BLOB auth_reply = data_blob_null; + DATA_BLOB secblob = data_blob_null; + NTSTATUS status = NT_STATUS_INVALID_PARAMETER; if (!spnego_parse_auth(blob1, &auth)) { #if 0 file_save("auth.dat", blob1.data, blob1.length); #endif - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + + return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); } + + if (auth.data[0] == ASN1_APPLICATION(0)) { + /* Might be a second negTokenTarg packet */ + + BOOL got_krb5_mechanism = False; + status = parse_spnego_mechanisms(auth, &secblob, &got_krb5_mechanism); + if (NT_STATUS_IS_OK(status)) { + DEBUG(3,("reply_spnego_auth: Got secblob of size %lu\n", (unsigned long)secblob.length)); +#ifdef HAVE_KRB5 + if ( got_krb5_mechanism && ((lp_security()==SEC_ADS) || lp_use_kerberos_keytab()) ) { + BOOL destroy_vuid = True; + int ret = reply_spnego_kerberos(conn, inbuf, outbuf, + length, bufsize, &secblob, &destroy_vuid); + data_blob_free(&secblob); + data_blob_free(&auth); + if (destroy_vuid) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + } + return ret; + } +#endif + } + } + + /* If we get here it wasn't a negTokenTarg auth packet. */ + data_blob_free(&secblob); - if (!global_ntlmssp_state) { + if (!*auth_ntlmssp_state) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + /* auth before negotiatiate? */ - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); } - nt_status = auth_ntlmssp_update(global_ntlmssp_state, - auth, &auth_reply); + status = auth_ntlmssp_update(*auth_ntlmssp_state, + auth, &auth_reply); data_blob_free(&auth); - reply_spnego_ntlmssp(conn, inbuf, outbuf, &global_ntlmssp_state, - &auth_reply, nt_status); + reply_spnego_ntlmssp(conn, inbuf, outbuf, vuid, + auth_ntlmssp_state, + &auth_reply, status, True); data_blob_free(&auth_reply); @@ -515,8 +814,202 @@ static int reply_spnego_auth(connection_struct *conn, char *inbuf, char *outbuf, return -1; } +/**************************************************************************** + List to store partial SPNEGO auth fragments. +****************************************************************************/ + +static struct pending_auth_data *pd_list; + +/**************************************************************************** + Delete an entry on the list. +****************************************************************************/ + +static void delete_partial_auth(struct pending_auth_data *pad) +{ + if (!pad) { + return; + } + DLIST_REMOVE(pd_list, pad); + data_blob_free(&pad->partial_data); + SAFE_FREE(pad); +} + +/**************************************************************************** + Search for a partial SPNEGO auth fragment matching an smbpid. +****************************************************************************/ + +static struct pending_auth_data *get_pending_auth_data(uint16 smbpid) +{ + struct pending_auth_data *pad; + + for (pad = pd_list; pad; pad = pad->next) { + if (pad->smbpid == smbpid) { + break; + } + } + return pad; +} + +/**************************************************************************** + Check the size of an SPNEGO blob. If we need more return NT_STATUS_MORE_PROCESSING_REQUIRED, + else return NT_STATUS_OK. Don't allow the blob to be more than 64k. +****************************************************************************/ + +static NTSTATUS check_spnego_blob_complete(uint16 smbpid, uint16 vuid, DATA_BLOB *pblob) +{ + struct pending_auth_data *pad = NULL; + ASN1_DATA data; + size_t needed_len = 0; + + pad = get_pending_auth_data(smbpid); + + /* Ensure we have some data. */ + if (pblob->length == 0) { + /* Caller can cope. */ + DEBUG(2,("check_spnego_blob_complete: zero blob length !\n")); + delete_partial_auth(pad); + return NT_STATUS_OK; + } + + /* Were we waiting for more data ? */ + if (pad) { + DATA_BLOB tmp_blob; + size_t copy_len = MIN(65536, pblob->length); + + /* Integer wrap paranoia.... */ + + if (pad->partial_data.length + copy_len < pad->partial_data.length || + pad->partial_data.length + copy_len < copy_len) { + + DEBUG(2,("check_spnego_blob_complete: integer wrap " + "pad->partial_data.length = %u, " + "copy_len = %u\n", + (unsigned int)pad->partial_data.length, + (unsigned int)copy_len )); + + delete_partial_auth(pad); + return NT_STATUS_INVALID_PARAMETER; + } + + DEBUG(10,("check_spnego_blob_complete: " + "pad->partial_data.length = %u, " + "pad->needed_len = %u, " + "copy_len = %u, " + "pblob->length = %u,\n", + (unsigned int)pad->partial_data.length, + (unsigned int)pad->needed_len, + (unsigned int)copy_len, + (unsigned int)pblob->length )); + + tmp_blob = data_blob(NULL, + pad->partial_data.length + copy_len); + + /* Concatenate the two (up to copy_len) bytes. */ + memcpy(tmp_blob.data, + pad->partial_data.data, + pad->partial_data.length); + memcpy(tmp_blob.data + pad->partial_data.length, + pblob->data, + copy_len); + + /* Replace the partial data. */ + data_blob_free(&pad->partial_data); + pad->partial_data = tmp_blob; + ZERO_STRUCT(tmp_blob); + + /* Are we done ? */ + if (pblob->length >= pad->needed_len) { + /* Yes, replace pblob. */ + data_blob_free(pblob); + *pblob = pad->partial_data; + ZERO_STRUCT(pad->partial_data); + delete_partial_auth(pad); + return NT_STATUS_OK; + } + + /* Still need more data. */ + pad->needed_len -= copy_len; + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } + + if ((pblob->data[0] != ASN1_APPLICATION(0)) && + (pblob->data[0] != ASN1_CONTEXT(1))) { + /* Not something we can determine the + * length of. + */ + return NT_STATUS_OK; + } + + /* This is a new SPNEGO sessionsetup - see if + * the data given in this blob is enough. + */ + + asn1_load(&data, *pblob); + asn1_start_tag(&data, pblob->data[0]); + if (data.has_error || data.nesting == NULL) { + asn1_free(&data); + /* Let caller catch. */ + return NT_STATUS_OK; + } + + /* Integer wrap paranoia.... */ + + if (data.nesting->taglen + data.nesting->start < data.nesting->taglen || + data.nesting->taglen + data.nesting->start < data.nesting->start) { + + DEBUG(2,("check_spnego_blob_complete: integer wrap " + "data.nesting->taglen = %u, " + "data.nesting->start = %u\n", + (unsigned int)data.nesting->taglen, + (unsigned int)data.nesting->start )); + + asn1_free(&data); + return NT_STATUS_INVALID_PARAMETER; + } + + /* Total length of the needed asn1 is the tag length + * plus the current offset. */ + + needed_len = data.nesting->taglen + data.nesting->start; + asn1_free(&data); + + DEBUG(10,("check_spnego_blob_complete: needed_len = %u, " + "pblob->length = %u\n", + (unsigned int)needed_len, + (unsigned int)pblob->length )); + + if (needed_len <= pblob->length) { + /* Nothing to do - blob is complete. */ + return NT_STATUS_OK; + } + + /* Refuse the blob if it's bigger than 64k. */ + if (needed_len > 65536) { + DEBUG(2,("check_spnego_blob_complete: needed_len too large (%u)\n", + (unsigned int)needed_len )); + return NT_STATUS_INVALID_PARAMETER; + } + + /* We must store this blob until complete. */ + if (!(pad = SMB_MALLOC_P(struct pending_auth_data))) { + return NT_STATUS_NO_MEMORY; + } + pad->needed_len = needed_len - pblob->length; + pad->partial_data = data_blob(pblob->data, pblob->length); + if (pad->partial_data.data == NULL) { + SAFE_FREE(pad); + return NT_STATUS_NO_MEMORY; + } + pad->smbpid = smbpid; + pad->vuid = vuid; + DLIST_ADD(pd_list, pad); + + return NT_STATUS_MORE_PROCESSING_REQUIRED; +} + /**************************************************************************** Reply to a session setup command. + conn POINTER CAN BE NULL HERE ! ****************************************************************************/ static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf, @@ -531,6 +1024,11 @@ static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf, char *p2; uint16 data_blob_len = SVAL(inbuf, smb_vwv7); enum remote_arch_types ra_type = get_remote_arch(); + int vuid = SVAL(inbuf,smb_uid); + user_struct *vuser = NULL; + NTSTATUS status = NT_STATUS_OK; + uint16 smbpid = SVAL(inbuf,smb_pid); + uint16 smb_flag2 = SVAL(inbuf, smb_flg2); DEBUG(3,("Doing spnego session setup\n")); @@ -547,7 +1045,7 @@ static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf, if (data_blob_len == 0) { /* an invalid request */ - return ERROR_NT(NT_STATUS_LOGON_FAILURE); + return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } bufrem = smb_bufrem(inbuf, p); @@ -559,42 +1057,120 @@ static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf, #endif p2 = inbuf + smb_vwv13 + data_blob_len; - p2 += srvstr_pull_buf(inbuf, native_os, p2, sizeof(native_os), STR_TERMINATE); - p2 += srvstr_pull_buf(inbuf, native_lanman, p2, sizeof(native_lanman), STR_TERMINATE); - p2 += srvstr_pull_buf(inbuf, primary_domain, p2, sizeof(primary_domain), STR_TERMINATE); + p2 += srvstr_pull_buf(inbuf, smb_flag2, native_os, p2, + sizeof(native_os), STR_TERMINATE); + p2 += srvstr_pull_buf(inbuf, smb_flag2, native_lanman, p2, + sizeof(native_lanman), STR_TERMINATE); + p2 += srvstr_pull_buf(inbuf, smb_flag2, primary_domain, p2, + sizeof(primary_domain), STR_TERMINATE); 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) ) + if ( !strlen(native_lanman) ) { ra_lanman_string( primary_domain ); - else + } else { ra_lanman_string( native_lanman ); + } } + vuser = get_partial_auth_user_struct(vuid); + if (!vuser) { + struct pending_auth_data *pad = get_pending_auth_data(smbpid); + if (pad) { + DEBUG(10,("reply_sesssetup_and_X_spnego: found pending vuid %u\n", + (unsigned int)pad->vuid )); + vuid = pad->vuid; + vuser = get_partial_auth_user_struct(vuid); + } + } + + if (!vuser) { + vuid = register_vuid(NULL, data_blob_null, data_blob_null, NULL); + if (vuid == UID_FIELD_INVALID ) { + data_blob_free(&blob1); + return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); + } + + vuser = get_partial_auth_user_struct(vuid); + } + + if (!vuser) { + data_blob_free(&blob1); + return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); + } + + SSVAL(outbuf,smb_uid,vuid); + + /* Large (greater than 4k) SPNEGO blobs are split into multiple + * sessionsetup requests as the Windows limit on the security blob + * field is 4k. Bug #4400. JRA. + */ + + status = check_spnego_blob_complete(smbpid, vuid, &blob1); + if (!NT_STATUS_IS_OK(status)) { + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + /* Real error - kill the intermediate vuid */ + invalidate_vuid(vuid); + } + data_blob_free(&blob1); + return ERROR_NT(nt_status_squash(status)); + } + if (blob1.data[0] == ASN1_APPLICATION(0)) { /* its a negTokenTarg packet */ - ret = reply_spnego_negotiate(conn, inbuf, outbuf, length, bufsize, blob1); + ret = reply_spnego_negotiate(conn, inbuf, outbuf, vuid, length, bufsize, blob1, + &vuser->auth_ntlmssp_state); data_blob_free(&blob1); return ret; } if (blob1.data[0] == ASN1_CONTEXT(1)) { /* its a auth packet */ - ret = reply_spnego_auth(conn, inbuf, outbuf, length, bufsize, blob1); + ret = reply_spnego_auth(conn, inbuf, outbuf, vuid, length, bufsize, blob1, + &vuser->auth_ntlmssp_state); data_blob_free(&blob1); return ret; } + if (strncmp((char *)(blob1.data), "NTLMSSP", 7) == 0) { + DATA_BLOB chal; + if (!vuser->auth_ntlmssp_state) { + status = auth_ntlmssp_start(&vuser->auth_ntlmssp_state); + if (!NT_STATUS_IS_OK(status)) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + data_blob_free(&blob1); + return ERROR_NT(nt_status_squash(status)); + } + } + + status = auth_ntlmssp_update(vuser->auth_ntlmssp_state, + blob1, &chal); + + data_blob_free(&blob1); + + reply_spnego_ntlmssp(conn, inbuf, outbuf, vuid, + &vuser->auth_ntlmssp_state, + &chal, status, False); + data_blob_free(&chal); + return -1; + } + /* what sort of packet is this? */ DEBUG(1,("Unknown packet in reply_sesssetup_and_X_spnego\n")); data_blob_free(&blob1); - return ERROR_NT(NT_STATUS_LOGON_FAILURE); + return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } /**************************************************************************** @@ -603,6 +1179,30 @@ static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf, a new session setup with VC==0 is ignored. ****************************************************************************/ +static int shutdown_other_smbds(struct db_record *rec, + const struct connections_key *key, + const struct connections_data *crec, + void *private_data) +{ + const char *ip = (const char *)private_data; + + if (!process_exists(crec->pid)) { + return 0; + } + + if (procid_is_me(&crec->pid)) { + return 0; + } + + if (strcmp(ip, crec->addr) != 0) { + return 0; + } + + messaging_send(smbd_messaging_context(), crec->pid, MSG_SHUTDOWN, + &data_blob_null); + return 0; +} + static void setup_new_vc_session(void) { DEBUG(2,("setup_new_vc_session: New VC == 0, if NT4.x compatible we would close all old resources.\n")); @@ -610,6 +1210,9 @@ static void setup_new_vc_session(void) conn_close_all(); invalidate_all_vuids(); #endif + if (lp_reset_on_zero_vc()) { + connections_forall(shutdown_other_smbds, client_addr()); + } } /**************************************************************************** @@ -631,14 +1234,9 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, fstring native_lanman; fstring primary_domain; static BOOL done_sesssetup = False; - extern BOOL global_encrypted_passwords_negotiated; - extern BOOL global_spnego_negotiated; - extern enum protocol_types Protocol; - extern int max_send; - auth_usersupplied_info *user_info = NULL; - extern struct auth_context *negprot_global_auth_context; auth_serversupplied_info *server_info = NULL; + uint16 smb_flag2 = SVAL(inbuf, smb_flg2); NTSTATUS nt_status; @@ -652,15 +1250,15 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, ZERO_STRUCT(nt_resp); ZERO_STRUCT(plaintext_password); - DEBUG(3,("wct=%d flg2=0x%x\n", CVAL(inbuf, smb_wct), SVAL(inbuf, smb_flg2))); + DEBUG(3,("wct=%d flg2=0x%x\n", CVAL(inbuf, smb_wct), smb_flag2)); /* a SPNEGO session setup has 12 command words, whereas a normal NT1 session setup has 13. See the cifs spec. */ if (CVAL(inbuf, smb_wct) == 12 && - (SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY)) { + (smb_flag2 & FLAGS2_EXTENDED_SECURITY)) { if (!global_spnego_negotiated) { DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at SPNEGO session setup when it was not negoitiated.\n")); - return ERROR_NT(NT_STATUS_UNSUCCESSFUL); + return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } if (SVAL(inbuf,smb_vwv4) == 0) { @@ -673,8 +1271,12 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, if (Protocol < PROTOCOL_NT1) { uint16 passlen1 = SVAL(inbuf,smb_vwv7); + + /* 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 > smb_bufrem(inbuf, smb_buf(inbuf)))) { - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); } if (doencrypt) { @@ -685,7 +1287,9 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, plaintext_password.data[passlen1] = 0; } - srvstr_pull_buf(inbuf, user, smb_buf(inbuf)+passlen1, sizeof(user), STR_TERMINATE); + srvstr_pull_buf(inbuf, smb_flag2, user, + smb_buf(inbuf)+passlen1, sizeof(user), + STR_TERMINATE); *domain = 0; } else { @@ -735,11 +1339,11 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, /* check for nasty tricks */ if (passlen1 > MAX_PASS_LEN || passlen1 > smb_bufrem(inbuf, p)) { - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); } if (passlen2 > MAX_PASS_LEN || passlen2 > smb_bufrem(inbuf, p+passlen1)) { - return ERROR_NT(NT_STATUS_INVALID_PARAMETER); + return ERROR_NT(nt_status_squash(NT_STATUS_INVALID_PARAMETER)); } /* Save the lanman2 password and the NT md4 password. */ @@ -753,7 +1357,7 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, nt_resp = data_blob(p+passlen1, passlen2); } else { pstring pass; - BOOL unic=SVAL(inbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS; + BOOL unic= smb_flag2 & FLAGS2_UNICODE_STRINGS; #if 0 /* This was the previous fix. Not sure if it's still valid. JRA. */ @@ -765,21 +1369,28 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, if (unic && (passlen2 == 0) && passlen1) { /* Only a ascii plaintext password was sent. */ - srvstr_pull(inbuf, pass, smb_buf(inbuf), sizeof(pass), - passlen1, STR_TERMINATE|STR_ASCII); + srvstr_pull(inbuf, smb_flag2, pass, + smb_buf(inbuf), sizeof(pass), + passlen1, STR_TERMINATE|STR_ASCII); } else { - srvstr_pull(inbuf, pass, smb_buf(inbuf), - sizeof(pass), unic ? passlen2 : passlen1, - STR_TERMINATE); + srvstr_pull(inbuf, smb_flag2, pass, + smb_buf(inbuf), sizeof(pass), + unic ? passlen2 : passlen1, + STR_TERMINATE); } plaintext_password = data_blob(pass, strlen(pass)+1); } p += passlen1 + passlen2; - p += srvstr_pull_buf(inbuf, user, p, sizeof(user), STR_TERMINATE); - p += srvstr_pull_buf(inbuf, domain, p, sizeof(domain), STR_TERMINATE); - p += srvstr_pull_buf(inbuf, native_os, p, sizeof(native_os), STR_TERMINATE); - p += srvstr_pull_buf(inbuf, native_lanman, p, sizeof(native_lanman), STR_TERMINATE); + p += srvstr_pull_buf(inbuf, smb_flag2, user, p, + sizeof(user), STR_TERMINATE); + p += srvstr_pull_buf(inbuf, smb_flag2, domain, p, + sizeof(domain), STR_TERMINATE); + p += srvstr_pull_buf(inbuf, smb_flag2, native_os, + p, sizeof(native_os), STR_TERMINATE); + p += srvstr_pull_buf(inbuf, smb_flag2, + native_lanman, p, sizeof(native_lanman), + STR_TERMINATE); /* 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 @@ -789,7 +1400,10 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, byte_count = SVAL(inbuf, smb_vwv13); if ( PTR_DIFF(p, save_p) < byte_count) - p += srvstr_pull_buf(inbuf, primary_domain, p, sizeof(primary_domain), STR_TERMINATE); + p += srvstr_pull_buf(inbuf, smb_flag2, + primary_domain, p, + sizeof(primary_domain), + STR_TERMINATE); else fstrcpy( primary_domain, "null" ); @@ -817,12 +1431,9 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, /* 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")); - return ERROR_NT(NT_STATUS_UNSUCCESSFUL); + return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } fstrcpy(sub_user, user); - - /* setup the string used by %U */ - sub_set_smb_name(user); } else { fstrcpy(sub_user, lp_guestaccount()); } @@ -840,6 +1451,7 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, map_username(sub_user); add_session_user(sub_user); + add_session_workgroup(domain); /* Then force it to null for the benfit of the code below */ *user = 0; } @@ -851,7 +1463,7 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, } else if (doencrypt) { if (!negprot_global_auth_context) { DEBUG(0, ("reply_sesssetup_and_X: Attempted encrypted session setup without negprot denied!\n")); - return ERROR_NT(NT_STATUS_LOGON_FAILURE); + return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); } nt_status = make_user_info_for_reply_enc(&user_info, user, domain, lm_resp, nt_resp); @@ -863,7 +1475,10 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, } else { struct auth_context *plaintext_auth_context = NULL; const uint8 *chal; - if (NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&plaintext_auth_context))) { + + nt_status = make_auth_context_subsystem(&plaintext_auth_context); + + if (NT_STATUS_IS_OK(nt_status)) { chal = plaintext_auth_context->get_ntlm_challenge(plaintext_auth_context); if (!make_user_info_for_reply(&user_info, @@ -895,20 +1510,35 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, return ERROR_NT(nt_status_squash(nt_status)); } + /* Ensure we can't possible take a code path leading to a null defref. */ + if (!server_info) { + return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); + } + + nt_status = create_local_token(server_info); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(10, ("create_local_token failed: %s\n", + nt_errstr(nt_status))); + data_blob_free(&nt_resp); + data_blob_free(&lm_resp); + data_blob_clear_free(&plaintext_password); + return ERROR_NT(nt_status_squash(nt_status)); + } + if (server_info->user_session_key.data) { session_key = data_blob(server_info->user_session_key.data, server_info->user_session_key.length); } else { - session_key = data_blob(NULL, 0); + session_key = data_blob_null; } data_blob_clear_free(&plaintext_password); /* it's ok - setup a reply */ - set_message(outbuf,3,0,True); + set_message(inbuf,outbuf,3,0,True); if (Protocol >= PROTOCOL_NT1) { char *p = smb_buf( outbuf ); p += add_signature( outbuf, p ); - set_message_end( outbuf, p ); + set_message_end(inbuf, outbuf, p ); /* perhaps grab OS version here?? */ } @@ -919,22 +1549,30 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, /* register the name and uid as being validated, so further connections to a uid can get through without a password, on the same VC */ - /* register_vuid keeps the server info */ - sess_vuid = register_vuid(server_info, session_key, nt_resp.data ? nt_resp : lm_resp, sub_user); - data_blob_free(&nt_resp); - data_blob_free(&lm_resp); - - if (sess_vuid == -1) { - return ERROR_NT(NT_STATUS_LOGON_FAILURE); - } + if (lp_security() == SEC_SHARE) { + sess_vuid = UID_FIELD_INVALID; + data_blob_free(&session_key); + TALLOC_FREE(server_info); + } else { + /* register_vuid keeps the server info */ + sess_vuid = register_vuid(server_info, session_key, + nt_resp.data ? nt_resp : lm_resp, + sub_user); + if (sess_vuid == UID_FIELD_INVALID) { + data_blob_free(&nt_resp); + data_blob_free(&lm_resp); + return ERROR_NT(nt_status_squash(NT_STATUS_LOGON_FAILURE)); + } - /* current_user_info is changed on new vuid */ - reload_services( True ); + /* current_user_info is changed on new vuid */ + reload_services( True ); - if (!server_info->guest && !srv_signing_started() && !srv_check_sign_mac(inbuf, True)) { - exit_server("reply_sesssetup_and_X: bad smb signature"); + sessionsetup_start_signing_engine(server_info, inbuf); } + data_blob_free(&nt_resp); + data_blob_free(&lm_resp); + SSVAL(outbuf,smb_uid,sess_vuid); SSVAL(inbuf,smb_uid,sess_vuid);