X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source3%2Fsmbd%2Fsmb2_sesssetup.c;h=29253d032171160cd2d9f2939aaa5e7c38cffd85;hb=4878769f8e6763fbb8347f2ea674c7a861d95564;hp=aa898eaad796af160272c8dc188b46e3c7f12322;hpb=915fe7981b48537bb000ae5f90e630caacf657e0;p=kai%2Fsamba.git diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index aa898eaad79..29253d03217 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -23,87 +23,132 @@ #include "smbd/smbd.h" #include "smbd/globals.h" #include "../libcli/smb/smb_common.h" -#include "../libcli/auth/spnego.h" #include "../auth/gensec/gensec.h" -#include "../auth/ntlmssp/ntlmssp.h" -#include "ntlmssp_wrap.h" -#include "../librpc/gen_ndr/krb5pac.h" -#include "libads/kerberos_proto.h" -#include "../lib/util/asn1.h" #include "auth.h" #include "../lib/tsocket/tsocket.h" #include "../libcli/security/security.h" +#include "../lib/util/tevent_ntstatus.h" -static NTSTATUS smbd_smb2_session_setup(struct smbd_smb2_request *smb2req, +static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbd_smb2_request *smb2req, uint64_t in_session_id, + uint8_t in_flags, uint8_t in_security_mode, - DATA_BLOB in_security_buffer, + uint64_t in_previous_session_id, + DATA_BLOB in_security_buffer); +static NTSTATUS smbd_smb2_session_setup_recv(struct tevent_req *req, uint16_t *out_session_flags, + TALLOC_CTX *mem_ctx, DATA_BLOB *out_security_buffer, uint64_t *out_session_id); +static void smbd_smb2_request_sesssetup_done(struct tevent_req *subreq); + NTSTATUS smbd_smb2_request_process_sesssetup(struct smbd_smb2_request *smb2req) { const uint8_t *inhdr; const uint8_t *inbody; - int i = smb2req->current_idx; - uint8_t *outhdr; - DATA_BLOB outbody; - DATA_BLOB outdyn; uint64_t in_session_id; + uint8_t in_flags; uint8_t in_security_mode; + uint64_t in_previous_session_id; uint16_t in_security_offset; uint16_t in_security_length; DATA_BLOB in_security_buffer; - uint16_t out_session_flags; - uint64_t out_session_id; - uint16_t out_security_offset; - DATA_BLOB out_security_buffer = data_blob_null; NTSTATUS status; + struct tevent_req *subreq; status = smbd_smb2_request_verify_sizes(smb2req, 0x19); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(smb2req, status); } - inhdr = (const uint8_t *)smb2req->in.vector[i+0].iov_base; - inbody = (const uint8_t *)smb2req->in.vector[i+1].iov_base; + inhdr = SMBD_SMB2_IN_HDR_PTR(smb2req); + inbody = SMBD_SMB2_IN_BODY_PTR(smb2req); + + in_session_id = BVAL(inhdr, SMB2_HDR_SESSION_ID); + in_flags = CVAL(inbody, 0x02); + in_security_mode = CVAL(inbody, 0x03); + /* Capabilities = IVAL(inbody, 0x04) */ + /* Channel = IVAL(inbody, 0x08) */ in_security_offset = SVAL(inbody, 0x0C); in_security_length = SVAL(inbody, 0x0E); + in_previous_session_id = BVAL(inbody, 0x10); - if (in_security_offset != (SMB2_HDR_BODY + smb2req->in.vector[i+1].iov_len)) { + if (in_security_offset != (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(smb2req))) { return smbd_smb2_request_error(smb2req, NT_STATUS_INVALID_PARAMETER); } - if (in_security_length > smb2req->in.vector[i+2].iov_len) { + if (in_security_length > SMBD_SMB2_IN_DYN_LEN(smb2req)) { return smbd_smb2_request_error(smb2req, NT_STATUS_INVALID_PARAMETER); } - in_session_id = BVAL(inhdr, SMB2_HDR_SESSION_ID); - in_security_mode = CVAL(inbody, 0x03); - in_security_buffer.data = (uint8_t *)smb2req->in.vector[i+2].iov_base; + in_security_buffer.data = SMBD_SMB2_IN_DYN_PTR(smb2req); in_security_buffer.length = in_security_length; - status = smbd_smb2_session_setup(smb2req, - in_session_id, - in_security_mode, - in_security_buffer, - &out_session_flags, - &out_security_buffer, - &out_session_id); + subreq = smbd_smb2_session_setup_send(smb2req, + smb2req->sconn->ev_ctx, + smb2req, + in_session_id, + in_flags, + in_security_mode, + in_previous_session_id, + in_security_buffer); + if (subreq == NULL) { + return smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY); + } + tevent_req_set_callback(subreq, smbd_smb2_request_sesssetup_done, smb2req); + + return smbd_smb2_request_pending_queue(smb2req, subreq, 500); +} + +static void smbd_smb2_request_sesssetup_done(struct tevent_req *subreq) +{ + struct smbd_smb2_request *smb2req = + tevent_req_callback_data(subreq, + struct smbd_smb2_request); + uint8_t *outhdr; + DATA_BLOB outbody; + DATA_BLOB outdyn; + uint16_t out_session_flags; + uint64_t out_session_id; + uint16_t out_security_offset; + DATA_BLOB out_security_buffer = data_blob_null; + NTSTATUS status; + NTSTATUS error; /* transport error */ + + status = smbd_smb2_session_setup_recv(subreq, + &out_session_flags, + smb2req, + &out_security_buffer, + &out_session_id); + TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { status = nt_status_squash(status); - return smbd_smb2_request_error(smb2req, status); + error = smbd_smb2_request_error(smb2req, status); + if (!NT_STATUS_IS_OK(error)) { + smbd_server_connection_terminate(smb2req->sconn, + nt_errstr(error)); + return; + } + return; } out_security_offset = SMB2_HDR_BODY + 0x08; - outhdr = (uint8_t *)smb2req->out.vector[i].iov_base; + outhdr = SMBD_SMB2_OUT_HDR_PTR(smb2req); outbody = data_blob_talloc(smb2req->out.vector, NULL, 0x08); if (outbody.data == NULL) { - return smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY); + error = smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY); + if (!NT_STATUS_IS_OK(error)) { + smbd_server_connection_terminate(smb2req->sconn, + nt_errstr(error)); + return; + } + return; } SBVAL(outhdr, SMB2_HDR_SESSION_ID, out_session_id); @@ -118,366 +163,200 @@ NTSTATUS smbd_smb2_request_process_sesssetup(struct smbd_smb2_request *smb2req) outdyn = out_security_buffer; - return smbd_smb2_request_done_ex(smb2req, status, outbody, &outdyn, - __location__); -} - -static int smbd_smb2_session_destructor(struct smbd_smb2_session *session) -{ - if (session->sconn == NULL) { - return 0; - } - - /* first free all tcons */ - while (session->tcons.list) { - talloc_free(session->tcons.list); + error = smbd_smb2_request_done_ex(smb2req, status, outbody, &outdyn, + __location__); + if (!NT_STATUS_IS_OK(error)) { + smbd_server_connection_terminate(smb2req->sconn, + nt_errstr(error)); + return; } - - idr_remove(session->sconn->smb2.sessions.idtree, session->vuid); - DLIST_REMOVE(session->sconn->smb2.sessions.list, session); - invalidate_vuid(session->sconn, session->vuid); - - session->vuid = 0; - session->status = NT_STATUS_USER_SESSION_DELETED; - session->sconn = NULL; - - return 0; } -#ifdef HAVE_KRB5 -static NTSTATUS smbd_smb2_session_setup_krb5(struct smbd_smb2_session *session, +static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session, struct smbd_smb2_request *smb2req, uint8_t in_security_mode, - const DATA_BLOB *secblob, - const char *mechOID, + struct auth_session_info *session_info, uint16_t *out_session_flags, - DATA_BLOB *out_security_buffer, uint64_t *out_session_id) { - DATA_BLOB ap_rep = data_blob_null; - DATA_BLOB ap_rep_wrapped = data_blob_null; - DATA_BLOB ticket = data_blob_null; - DATA_BLOB session_key = data_blob_null; - DATA_BLOB secblob_out = data_blob_null; - uint8 tok_id[2]; - struct PAC_LOGON_INFO *logon_info = NULL; - char *principal = NULL; - char *user = NULL; - char *domain = NULL; - struct passwd *pw = NULL; NTSTATUS status; - char *real_username; - bool username_was_mapped = false; - bool map_domainuser_to_guest = false; bool guest = false; + uint8_t session_key[16]; + struct smbXsrv_session *x = session; + struct smbXsrv_connection *conn = session->connection; - if (!spnego_parse_krb5_wrap(talloc_tos(), *secblob, &ticket, tok_id)) { - status = NT_STATUS_LOGON_FAILURE; - goto fail; - } - - status = ads_verify_ticket(smb2req, lp_realm(), 0, &ticket, - &principal, &logon_info, &ap_rep, - &session_key, true); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1,("smb2: Failed to verify incoming ticket with error %s!\n", - nt_errstr(status))); - if (!NT_STATUS_EQUAL(status, NT_STATUS_TIME_DIFFERENCE_AT_DC)) { - status = NT_STATUS_LOGON_FAILURE; - } - goto fail; - } - - status = get_user_from_kerberos_info(talloc_tos(), - session->sconn->remote_hostname, - principal, logon_info, - &username_was_mapped, - &map_domainuser_to_guest, - &user, &domain, - &real_username, &pw); - if (!NT_STATUS_IS_OK(status)) { - goto fail; - } - - /* save the PAC data if we have it */ - if (logon_info) { - netsamlogon_cache_store(user, &logon_info->info3); - } - - /* setup the string used by %U */ - sub_set_smb_name(real_username); - - /* reload services so that the new %U is taken into account */ - reload_services(smb2req->sconn->msg_ctx, smb2req->sconn->sock, true); - - status = make_session_info_krb5(session, - user, domain, real_username, pw, - logon_info, map_domainuser_to_guest, - username_was_mapped, - &session_key, - &session->session_info); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("smb2: make_server_info_krb5 failed\n")); - goto fail; + if ((in_security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) || + lp_server_signing() == SMB_SIGNING_REQUIRED) { + x->global->signing_required = true; } - if ((in_security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) || - lp_server_signing() == Required) { - session->do_signing = true; + if (lp_smb_encrypt(-1) == SMB_SIGNING_REQUIRED) { + x->global->encryption_required = true; } - if (security_session_user_level(session->session_info, NULL) < SECURITY_USER) { + if (security_session_user_level(session_info, NULL) < SECURITY_USER) { /* we map anonymous to guest internally */ *out_session_flags |= SMB2_SESSION_FLAG_IS_GUEST; *out_session_flags |= SMB2_SESSION_FLAG_IS_NULL; /* force no signing */ - session->do_signing = false; + x->global->signing_required = false; guest = true; } - session->session_key = session->session_info->session_key; - - session->compat_vuser = talloc_zero(session, user_struct); - if (session->compat_vuser == NULL) { - status = NT_STATUS_NO_MEMORY; - goto fail; + if (guest && x->global->encryption_required) { + DEBUG(1,("reject guest session as encryption is required\n")); + return NT_STATUS_ACCESS_DENIED; } - session->compat_vuser->auth_ntlmssp_state = NULL; - session->compat_vuser->homes_snum = -1; - session->compat_vuser->session_info = session->session_info; - session->compat_vuser->session_keystr = NULL; - session->compat_vuser->vuid = session->vuid; - DLIST_ADD(session->sconn->smb1.sessions.validated_users, session->compat_vuser); - if (security_session_user_level(session->session_info, NULL) >= SECURITY_USER) { - session->compat_vuser->homes_snum = - register_homes_share(session->session_info->unix_info->unix_name); + if (!(conn->smb2.server.capabilities & SMB2_CAP_ENCRYPTION)) { + if (x->global->encryption_required) { + DEBUG(1,("reject session with dialect[0x%04X] " + "as encryption is required\n", + conn->smb2.server.dialect)); + return NT_STATUS_ACCESS_DENIED; + } } - if (!session_claim(session->sconn, session->compat_vuser)) { - DEBUG(1, ("smb2: Failed to claim session " - "for vuid=%d\n", - session->compat_vuser->vuid)); - goto fail; + if (x->global->encryption_required) { + *out_session_flags |= SMB2_SESSION_FLAG_ENCRYPT_DATA; } - session->status = NT_STATUS_OK; + ZERO_STRUCT(session_key); + memcpy(session_key, session_info->session_key.data, + MIN(session_info->session_key.length, sizeof(session_key))); - /* - * we attach the session to the request - * so that the response can be signed - */ - smb2req->session = session; - if (!guest) { - smb2req->do_signing = true; + x->global->signing_key = data_blob_talloc(x->global, + session_key, + sizeof(session_key)); + if (x->global->signing_key.data == NULL) { + ZERO_STRUCT(session_key); + return NT_STATUS_NO_MEMORY; } - global_client_caps |= (CAP_LEVEL_II_OPLOCKS|CAP_STATUS32); - status = NT_STATUS_OK; - - /* wrap that up in a nice GSS-API wrapping */ - ap_rep_wrapped = spnego_gen_krb5_wrap(talloc_tos(), ap_rep, - TOK_ID_KRB_AP_REP); - - secblob_out = spnego_gen_auth_response( - talloc_tos(), - &ap_rep_wrapped, - status, - mechOID); + if (conn->protocol >= PROTOCOL_SMB2_24) { + const DATA_BLOB label = data_blob_string_const_null("SMB2AESCMAC"); + const DATA_BLOB context = data_blob_string_const_null("SmbSign"); - *out_security_buffer = data_blob_talloc(smb2req, - secblob_out.data, - secblob_out.length); - if (secblob_out.data && out_security_buffer->data == NULL) { - status = NT_STATUS_NO_MEMORY; - goto fail; + smb2_key_derivation(session_key, sizeof(session_key), + label.data, label.length, + context.data, context.length, + x->global->signing_key.data); } - data_blob_free(&ap_rep); - data_blob_free(&ap_rep_wrapped); - data_blob_free(&ticket); - data_blob_free(&session_key); - data_blob_free(&secblob_out); - - *out_session_id = session->vuid; - - return NT_STATUS_OK; - - fail: - - data_blob_free(&ap_rep); - data_blob_free(&ap_rep_wrapped); - data_blob_free(&ticket); - data_blob_free(&session_key); - data_blob_free(&secblob_out); - - ap_rep_wrapped = data_blob_null; - secblob_out = spnego_gen_auth_response( - talloc_tos(), - &ap_rep_wrapped, - status, - mechOID); - - *out_security_buffer = data_blob_talloc(smb2req, - secblob_out.data, - secblob_out.length); - data_blob_free(&secblob_out); - return status; -} -#endif - -static NTSTATUS smbd_smb2_spnego_negotiate(struct smbd_smb2_session *session, - struct smbd_smb2_request *smb2req, - uint8_t in_security_mode, - DATA_BLOB in_security_buffer, - uint16_t *out_session_flags, - DATA_BLOB *out_security_buffer, - uint64_t *out_session_id) -{ - DATA_BLOB secblob_in = data_blob_null; - DATA_BLOB chal_out = data_blob_null; - char *kerb_mech = NULL; - NTSTATUS status; - - /* Ensure we have no old NTLM state around. */ - TALLOC_FREE(session->auth_ntlmssp_state); - - status = parse_spnego_mechanisms(talloc_tos(), in_security_buffer, - &secblob_in, &kerb_mech); - if (!NT_STATUS_IS_OK(status)) { - goto out; - } + if (conn->protocol >= PROTOCOL_SMB2_24) { + const DATA_BLOB label = data_blob_string_const_null("SMB2AESCCM"); + const DATA_BLOB context = data_blob_string_const_null("ServerIn "); -#ifdef HAVE_KRB5 - if (kerb_mech && ((lp_security()==SEC_ADS) || - USE_KERBEROS_KEYTAB) ) { - status = smbd_smb2_session_setup_krb5(session, - smb2req, - in_security_mode, - &secblob_in, - kerb_mech, - out_session_flags, - out_security_buffer, - out_session_id); + x->global->decryption_key = data_blob_talloc(x->global, + session_key, + sizeof(session_key)); + if (x->global->decryption_key.data == NULL) { + ZERO_STRUCT(session_key); + return NT_STATUS_NO_MEMORY; + } - goto out; + smb2_key_derivation(session_key, sizeof(session_key), + label.data, label.length, + context.data, context.length, + x->global->decryption_key.data); } -#endif - - if (kerb_mech) { - /* The mechtoken is a krb5 ticket, but - * we need to fall back to NTLM. */ - - DEBUG(3,("smb2: Got krb5 ticket in SPNEGO " - "but set to downgrade to NTLMSSP\n")); - - status = NT_STATUS_MORE_PROCESSING_REQUIRED; - } else { - /* Fall back to NTLMSSP. */ - status = auth_ntlmssp_prepare(session->sconn->remote_address, - &session->auth_ntlmssp_state); - if (!NT_STATUS_IS_OK(status)) { - goto out; - } - auth_ntlmssp_want_feature(session->auth_ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY); + if (conn->protocol >= PROTOCOL_SMB2_24) { + const DATA_BLOB label = data_blob_string_const_null("SMB2AESCCM"); + const DATA_BLOB context = data_blob_string_const_null("ServerOut"); - status = auth_ntlmssp_start(session->auth_ntlmssp_state); - if (!NT_STATUS_IS_OK(status)) { - goto out; + x->global->encryption_key = data_blob_talloc(x->global, + session_key, + sizeof(session_key)); + if (x->global->encryption_key.data == NULL) { + ZERO_STRUCT(session_key); + return NT_STATUS_NO_MEMORY; } - status = auth_ntlmssp_update(session->auth_ntlmssp_state, - talloc_tos(), - secblob_in, - &chal_out); - } + smb2_key_derivation(session_key, sizeof(session_key), + label.data, label.length, + context.data, context.length, + x->global->encryption_key.data); - if (!NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, - NT_STATUS_MORE_PROCESSING_REQUIRED)) { - goto out; + generate_random_buffer((uint8_t *)&x->nonce_high, sizeof(x->nonce_high)); + x->nonce_low = 1; } - *out_security_buffer = spnego_gen_auth_response(smb2req, - &chal_out, - status, - OID_NTLMSSP); - if (out_security_buffer->data == NULL) { - status = NT_STATUS_NO_MEMORY; - goto out; + x->global->application_key = data_blob_dup_talloc(x->global, + x->global->signing_key); + if (x->global->application_key.data == NULL) { + ZERO_STRUCT(session_key); + return NT_STATUS_NO_MEMORY; } - *out_session_id = session->vuid; - out: + if (conn->protocol >= PROTOCOL_SMB2_24) { + const DATA_BLOB label = data_blob_string_const_null("SMB2APP"); + const DATA_BLOB context = data_blob_string_const_null("SmbRpc"); - data_blob_free(&secblob_in); - data_blob_free(&chal_out); - TALLOC_FREE(kerb_mech); - if (!NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, - NT_STATUS_MORE_PROCESSING_REQUIRED)) { - TALLOC_FREE(session->auth_ntlmssp_state); - TALLOC_FREE(session); + smb2_key_derivation(session_key, sizeof(session_key), + label.data, label.length, + context.data, context.length, + x->global->application_key.data); } - return status; -} - -static NTSTATUS smbd_smb2_common_ntlmssp_auth_return(struct smbd_smb2_session *session, - struct smbd_smb2_request *smb2req, - uint8_t in_security_mode, - DATA_BLOB in_security_buffer, - uint16_t *out_session_flags, - uint64_t *out_session_id) -{ - bool guest = false; + ZERO_STRUCT(session_key); - if ((in_security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) || - lp_server_signing() == Required) { - session->do_signing = true; + x->global->channels[0].signing_key = data_blob_dup_talloc(x->global->channels, + x->global->signing_key); + if (x->global->channels[0].signing_key.data == NULL) { + return NT_STATUS_NO_MEMORY; } - if (security_session_user_level(session->session_info, NULL) < SECURITY_USER) { - /* we map anonymous to guest internally */ - *out_session_flags |= SMB2_SESSION_FLAG_IS_GUEST; - *out_session_flags |= SMB2_SESSION_FLAG_IS_NULL; - /* force no signing */ - session->do_signing = false; - guest = true; + data_blob_clear_free(&session_info->session_key); + session_info->session_key = data_blob_dup_talloc(session_info, + x->global->application_key); + if (session_info->session_key.data == NULL) { + return NT_STATUS_NO_MEMORY; } - session->session_key = session->session_info->session_key; - - session->compat_vuser = talloc_zero(session, user_struct); - if (session->compat_vuser == NULL) { - TALLOC_FREE(session->auth_ntlmssp_state); - TALLOC_FREE(session); + session->compat = talloc_zero(session, struct user_struct); + if (session->compat == NULL) { return NT_STATUS_NO_MEMORY; } - session->compat_vuser->auth_ntlmssp_state = session->auth_ntlmssp_state; - session->compat_vuser->homes_snum = -1; - session->compat_vuser->session_info = session->session_info; - session->compat_vuser->session_keystr = NULL; - session->compat_vuser->vuid = session->vuid; - DLIST_ADD(session->sconn->smb1.sessions.validated_users, session->compat_vuser); + session->compat->session = session; + session->compat->homes_snum = -1; + session->compat->session_info = session_info; + session->compat->session_keystr = NULL; + session->compat->vuid = session->global->session_wire_id; + DLIST_ADD(smb2req->sconn->users, session->compat); + smb2req->sconn->num_users++; - if (security_session_user_level(session->session_info, NULL) >= SECURITY_USER) { - session->compat_vuser->homes_snum = - register_homes_share(session->session_info->unix_info->unix_name); + if (security_session_user_level(session_info, NULL) >= SECURITY_USER) { + session->compat->homes_snum = + register_homes_share(session_info->unix_info->unix_name); } - if (!session_claim(session->sconn, session->compat_vuser)) { + if (!session_claim(smb2req->sconn, session)) { DEBUG(1, ("smb2: Failed to claim session " - "for vuid=%d\n", - session->compat_vuser->vuid)); - TALLOC_FREE(session->auth_ntlmssp_state); - TALLOC_FREE(session); + "for vuid=%llu\n", + (unsigned long long)session->compat->vuid)); return NT_STATUS_LOGON_FAILURE; } + set_current_user_info(session_info->unix_info->sanitized_username, + session_info->unix_info->unix_name, + session_info->info->domain_name); + + reload_services(smb2req->sconn, conn_snum_used, true); session->status = NT_STATUS_OK; + session->global->auth_session_info = 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->expiration_time = gensec_expire_time(session->gensec); + + status = smbXsrv_session_update(session); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("smb2: Failed to update session for vuid=%llu - %s\n", + (unsigned long long)session->compat->vuid, + nt_errstr(status))); + return NT_STATUS_LOGON_FAILURE; + } /* * we attach the session to the request @@ -490,300 +369,386 @@ static NTSTATUS smbd_smb2_common_ntlmssp_auth_return(struct smbd_smb2_session *s global_client_caps |= (CAP_LEVEL_II_OPLOCKS|CAP_STATUS32); - *out_session_id = session->vuid; + *out_session_id = session->global->session_wire_id; return NT_STATUS_OK; } -static NTSTATUS smbd_smb2_spnego_auth(struct smbd_smb2_session *session, +static NTSTATUS smbd_smb2_reauth_generic_return(struct smbXsrv_session *session, struct smbd_smb2_request *smb2req, - uint8_t in_security_mode, - DATA_BLOB in_security_buffer, + struct auth_session_info *session_info, uint16_t *out_session_flags, - DATA_BLOB *out_security_buffer, uint64_t *out_session_id) { - DATA_BLOB auth = data_blob_null; - DATA_BLOB auth_out = data_blob_null; NTSTATUS status; + struct smbXsrv_session *x = session; + struct smbXsrv_connection *conn = session->connection; - if (!spnego_parse_auth(talloc_tos(), in_security_buffer, &auth)) { - TALLOC_FREE(session); - return NT_STATUS_LOGON_FAILURE; + data_blob_clear_free(&session_info->session_key); + session_info->session_key = data_blob_dup_talloc(session_info, + x->global->application_key); + if (session_info->session_key.data == NULL) { + return NT_STATUS_NO_MEMORY; } - if (auth.data[0] == ASN1_APPLICATION(0)) { - /* Might be a second negTokenTarg packet */ - DATA_BLOB secblob_in = data_blob_null; - char *kerb_mech = NULL; + session->compat->session_info = session_info; + session->compat->vuid = session->global->session_wire_id; - status = parse_spnego_mechanisms(talloc_tos(), - in_security_buffer, - &secblob_in, &kerb_mech); - if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(session); - return status; - } + session->compat->homes_snum = + register_homes_share(session_info->unix_info->unix_name); -#ifdef HAVE_KRB5 - if (kerb_mech && ((lp_security()==SEC_ADS) || - USE_KERBEROS_KEYTAB) ) { - status = smbd_smb2_session_setup_krb5(session, - smb2req, - in_security_mode, - &secblob_in, - kerb_mech, - out_session_flags, - out_security_buffer, - out_session_id); - - data_blob_free(&secblob_in); - TALLOC_FREE(kerb_mech); - if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(session); - } - return status; - } -#endif - - /* Can't blunder into NTLMSSP auth if we have - * a krb5 ticket. */ - - if (kerb_mech) { - DEBUG(3,("smb2: network " - "misconfiguration, client sent us a " - "krb5 ticket and kerberos security " - "not enabled\n")); - TALLOC_FREE(session); - data_blob_free(&secblob_in); - TALLOC_FREE(kerb_mech); - return NT_STATUS_LOGON_FAILURE; - } + set_current_user_info(session_info->unix_info->sanitized_username, + session_info->unix_info->unix_name, + session_info->info->domain_name); - data_blob_free(&secblob_in); - } + reload_services(smb2req->sconn, conn_snum_used, true); - if (session->auth_ntlmssp_state == NULL) { - status = auth_ntlmssp_prepare(session->sconn->remote_address, - &session->auth_ntlmssp_state); - if (!NT_STATUS_IS_OK(status)) { - data_blob_free(&auth); - TALLOC_FREE(session); - return status; - } + session->status = NT_STATUS_OK; + TALLOC_FREE(session->global->auth_session_info); + session->global->auth_session_info = 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->expiration_time = gensec_expire_time(session->gensec); + + status = smbXsrv_session_update(session); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("smb2: Failed to update session for vuid=%llu - %s\n", + (unsigned long long)session->compat->vuid, + nt_errstr(status))); + return NT_STATUS_LOGON_FAILURE; + } - auth_ntlmssp_want_feature(session->auth_ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY); + conn_clear_vuid_caches(conn->sconn, session->compat->vuid); - status = auth_ntlmssp_start(session->auth_ntlmssp_state); - if (!NT_STATUS_IS_OK(status)) { - data_blob_free(&auth); - TALLOC_FREE(session); - return status; - } - } + *out_session_id = session->global->session_wire_id; - status = auth_ntlmssp_update(session->auth_ntlmssp_state, - talloc_tos(), auth, - &auth_out); - /* If status is NT_STATUS_OK then we need to get the token. - * Map to guest is now internal to auth_ntlmssp */ - if (NT_STATUS_IS_OK(status)) { - status = gensec_session_info(session->auth_ntlmssp_state->gensec_security, - session, - &session->session_info); - } + return NT_STATUS_OK; +} - if (!NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - TALLOC_FREE(session->auth_ntlmssp_state); - data_blob_free(&auth); - TALLOC_FREE(session); - return status; - } +struct smbd_smb2_session_setup_state { + struct tevent_context *ev; + struct smbd_smb2_request *smb2req; + uint64_t in_session_id; + uint8_t in_flags; + uint8_t in_security_mode; + uint64_t in_previous_session_id; + DATA_BLOB in_security_buffer; + struct smbXsrv_session *session; + struct auth_session_info *session_info; + uint16_t out_session_flags; + DATA_BLOB out_security_buffer; + uint64_t out_session_id; + /* The following pointer is owned by state->session. */ + struct smbd_smb2_session_setup_state **pp_self_ref; +}; - data_blob_free(&auth); +static int pp_self_ref_destructor(struct smbd_smb2_session_setup_state **pp_state) +{ + (*pp_state)->session = NULL; + /* + * To make things clearer, ensure the pp_self_ref + * pointer is nulled out. We're never going to + * access this again. + */ + (*pp_state)->pp_self_ref = NULL; + return 0; +} - *out_security_buffer = spnego_gen_auth_response(smb2req, - &auth_out, status, NULL); +static int smbd_smb2_session_setup_state_destructor(struct smbd_smb2_session_setup_state *state) +{ + /* + * if state->session is not NULL, + * we remove the session on failure + */ + TALLOC_FREE(state->session); + return 0; +} - if (out_security_buffer->data == NULL) { - TALLOC_FREE(session->auth_ntlmssp_state); - TALLOC_FREE(session); - return NT_STATUS_NO_MEMORY; - } +static void smbd_smb2_session_setup_gensec_done(struct tevent_req *subreq); +static void smbd_smb2_session_setup_previous_done(struct tevent_req *subreq); - *out_session_id = session->vuid; +/************************************************************************ + We have to tag the state->session pointer with memory talloc'ed + on it to ensure it gets NULL'ed out if the underlying struct smbXsrv_session + is deleted by shutdown whilst this request is in flight. +************************************************************************/ - if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - return NT_STATUS_MORE_PROCESSING_REQUIRED; +static NTSTATUS tag_state_session_ptr(struct smbd_smb2_session_setup_state *state) +{ + state->pp_self_ref = talloc_zero(state->session, + struct smbd_smb2_session_setup_state *); + if (state->pp_self_ref == NULL) { + return NT_STATUS_NO_MEMORY; } - - /* We're done - claim the session. */ - return smbd_smb2_common_ntlmssp_auth_return(session, - smb2req, - in_security_mode, - in_security_buffer, - out_session_flags, - out_session_id); + *state->pp_self_ref = state; + talloc_set_destructor(state->pp_self_ref, pp_self_ref_destructor); + return NT_STATUS_OK; } -static NTSTATUS smbd_smb2_raw_ntlmssp_auth(struct smbd_smb2_session *session, +static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, struct smbd_smb2_request *smb2req, + uint64_t in_session_id, + uint8_t in_flags, uint8_t in_security_mode, - DATA_BLOB in_security_buffer, - uint16_t *out_session_flags, - DATA_BLOB *out_security_buffer, - uint64_t *out_session_id) + uint64_t in_previous_session_id, + DATA_BLOB in_security_buffer) { + struct tevent_req *req; + struct smbd_smb2_session_setup_state *state; NTSTATUS status; + NTTIME now = timeval_to_nttime(&smb2req->request_time); + struct tevent_req *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct smbd_smb2_session_setup_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->smb2req = smb2req; + state->in_session_id = in_session_id; + state->in_flags = in_flags; + state->in_security_mode = in_security_mode; + state->in_previous_session_id = in_previous_session_id; + state->in_security_buffer = in_security_buffer; + + if (in_flags & SMB2_SESSION_FLAG_BINDING) { + if (smb2req->sconn->conn->protocol < PROTOCOL_SMB2_22) { + tevent_req_nterror(req, NT_STATUS_REQUEST_NOT_ACCEPTED); + return tevent_req_post(req, ev); + } - *out_security_buffer = data_blob_null; + /* + * We do not support multi channel. + */ + tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); + return tevent_req_post(req, ev); + } + + talloc_set_destructor(state, smbd_smb2_session_setup_state_destructor); - if (session->auth_ntlmssp_state == NULL) { - status = auth_ntlmssp_prepare(session->sconn->remote_address, - &session->auth_ntlmssp_state); - if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(session); - return status; + if (state->in_session_id == 0) { + /* create a new session */ + status = smbXsrv_session_create(state->smb2req->sconn->conn, + now, &state->session); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + } else { + status = smb2srv_session_lookup(state->smb2req->sconn->conn, + state->in_session_id, now, + &state->session); + if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) { + status = NT_STATUS_OK; + } + if (NT_STATUS_IS_OK(status)) { + state->session->status = NT_STATUS_MORE_PROCESSING_REQUIRED; + status = NT_STATUS_MORE_PROCESSING_REQUIRED; + TALLOC_FREE(state->session->gensec); + } + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); } + } - auth_ntlmssp_want_feature(session->auth_ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY); + status = tag_state_session_ptr(state); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } - if (session->sconn->use_gensec_hook) { - status = auth_generic_start(session->auth_ntlmssp_state, GENSEC_OID_SPNEGO); - } else { - status = auth_ntlmssp_start(session->auth_ntlmssp_state); + if (state->session->gensec == NULL) { + status = auth_generic_prepare(state->session, + state->session->connection->remote_address, + &state->session->gensec); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); } - if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(session); - return status; + + gensec_want_feature(state->session->gensec, GENSEC_FEATURE_SESSION_KEY); + gensec_want_feature(state->session->gensec, GENSEC_FEATURE_UNIX_TOKEN); + + status = gensec_start_mech_by_oid(state->session->gensec, + GENSEC_OID_SPNEGO); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); } } - /* RAW NTLMSSP */ - status = auth_ntlmssp_update(session->auth_ntlmssp_state, - smb2req, - in_security_buffer, - out_security_buffer); + become_root(); + subreq = gensec_update_send(state, state->ev, + state->session->gensec, + state->in_security_buffer); + unbecome_root(); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, smbd_smb2_session_setup_gensec_done, req); - if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - *out_session_id = session->vuid; - return status; + return req; +} + +static void smbd_smb2_session_setup_gensec_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct smbd_smb2_session_setup_state *state = + tevent_req_data(req, + struct smbd_smb2_session_setup_state); + NTSTATUS status; + + become_root(); + status = gensec_update_recv(subreq, state, + &state->out_security_buffer); + unbecome_root(); + TALLOC_FREE(subreq); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && + !NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; } - status = gensec_session_info(session->auth_ntlmssp_state->gensec_security, - session, - &session->session_info); + if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + state->out_session_id = state->session->global->session_wire_id; + /* we want to keep the session */ + TALLOC_FREE(state->pp_self_ref); + tevent_req_nterror(req, status); + return; + } + + status = gensec_session_info(state->session->gensec, + state->session->global, + &state->session_info); + if (tevent_req_nterror(req, status)) { + return; + } + + if ((state->in_previous_session_id != 0) && + (state->session->global->session_wire_id != + state->in_previous_session_id)) + { + subreq = smb2srv_session_close_previous_send(state, state->ev, + state->session->connection, + state->session_info, + state->in_previous_session_id, + state->session->global->session_wire_id); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + smbd_smb2_session_setup_previous_done, + req); + return; + } + + if (state->session->global->auth_session_info != NULL) { + status = smbd_smb2_reauth_generic_return(state->session, + state->smb2req, + state->session_info, + &state->out_session_flags, + &state->out_session_id); + if (tevent_req_nterror(req, status)) { + return; + } + /* we want to keep the session */ + TALLOC_FREE(state->pp_self_ref); + tevent_req_done(req); + return; + } + + status = smbd_smb2_auth_generic_return(state->session, + state->smb2req, + state->in_security_mode, + state->session_info, + &state->out_session_flags, + &state->out_session_id); + if (tevent_req_nterror(req, status)) { + return; + } + + /* we want to keep the session */ + TALLOC_FREE(state->pp_self_ref); + tevent_req_done(req); + return; +} + +static void smbd_smb2_session_setup_previous_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct smbd_smb2_session_setup_state *state = + tevent_req_data(req, + struct smbd_smb2_session_setup_state); + NTSTATUS status; - if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(session->auth_ntlmssp_state); - TALLOC_FREE(session); - return status; + status = smb2srv_session_close_previous_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; } - *out_session_id = session->vuid; - return smbd_smb2_common_ntlmssp_auth_return(session, - smb2req, - in_security_mode, - in_security_buffer, - out_session_flags, - out_session_id); + if (state->session->global->auth_session_info != NULL) { + status = smbd_smb2_reauth_generic_return(state->session, + state->smb2req, + state->session_info, + &state->out_session_flags, + &state->out_session_id); + if (tevent_req_nterror(req, status)) { + return; + } + /* we want to keep the session */ + TALLOC_FREE(state->pp_self_ref); + tevent_req_done(req); + return; + } + + status = smbd_smb2_auth_generic_return(state->session, + state->smb2req, + state->in_security_mode, + state->session_info, + &state->out_session_flags, + &state->out_session_id); + if (tevent_req_nterror(req, status)) { + return; + } + + /* we want to keep the session */ + TALLOC_FREE(state->pp_self_ref); + tevent_req_done(req); + return; } -static NTSTATUS smbd_smb2_session_setup(struct smbd_smb2_request *smb2req, - uint64_t in_session_id, - uint8_t in_security_mode, - DATA_BLOB in_security_buffer, +static NTSTATUS smbd_smb2_session_setup_recv(struct tevent_req *req, uint16_t *out_session_flags, + TALLOC_CTX *mem_ctx, DATA_BLOB *out_security_buffer, uint64_t *out_session_id) { - struct smbd_smb2_session *session; - - *out_session_flags = 0; - *out_session_id = 0; - - if (in_session_id == 0) { - int id; - - /* create a new session */ - session = talloc_zero(smb2req->sconn, struct smbd_smb2_session); - if (session == NULL) { - return NT_STATUS_NO_MEMORY; - } - session->status = NT_STATUS_MORE_PROCESSING_REQUIRED; - id = idr_get_new_random(smb2req->sconn->smb2.sessions.idtree, - session, - smb2req->sconn->smb2.sessions.limit); - if (id == -1) { - return NT_STATUS_INSUFFICIENT_RESOURCES; - } - session->vuid = id; + struct smbd_smb2_session_setup_state *state = + tevent_req_data(req, + struct smbd_smb2_session_setup_state); + NTSTATUS status; - session->tcons.idtree = idr_init(session); - if (session->tcons.idtree == NULL) { - return NT_STATUS_NO_MEMORY; + if (tevent_req_is_nterror(req, &status)) { + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + tevent_req_received(req); + return nt_status_squash(status); } - session->tcons.limit = 0x0000FFFE; - session->tcons.list = NULL; - - DLIST_ADD_END(smb2req->sconn->smb2.sessions.list, session, - struct smbd_smb2_session *); - session->sconn = smb2req->sconn; - talloc_set_destructor(session, smbd_smb2_session_destructor); } else { - void *p; + status = NT_STATUS_OK; + } - /* lookup an existing session */ - p = idr_find(smb2req->sconn->smb2.sessions.idtree, in_session_id); - if (p == NULL) { - return NT_STATUS_USER_SESSION_DELETED; - } - session = talloc_get_type_abort(p, struct smbd_smb2_session); - } - - if (NT_STATUS_IS_OK(session->status)) { - return NT_STATUS_REQUEST_NOT_ACCEPTED; - } - - /* Handle either raw NTLMSSP or hand off the whole blob to - * GENSEC. The processing at this layer is essentially - * identical regardless. In particular, both rely only on the - * status code (not the contents of the packet) and do not - * wrap the result */ - if (session->sconn->use_gensec_hook - || ntlmssp_blob_matches_magic(&in_security_buffer)) { - return smbd_smb2_raw_ntlmssp_auth(session, - smb2req, - in_security_mode, - in_security_buffer, - out_session_flags, - out_security_buffer, - out_session_id); - } else if (in_security_buffer.data[0] == ASN1_APPLICATION(0)) { - return smbd_smb2_spnego_negotiate(session, - smb2req, - in_security_mode, - in_security_buffer, - out_session_flags, - out_security_buffer, - out_session_id); - } else if (in_security_buffer.data[0] == ASN1_CONTEXT(1)) { - return smbd_smb2_spnego_auth(session, - smb2req, - in_security_mode, - in_security_buffer, - out_session_flags, - out_security_buffer, - out_session_id); - } - - /* Unknown packet type. */ - DEBUG(1,("Unknown packet type %u in smb2 sessionsetup\n", - (unsigned int)in_security_buffer.data[0] )); - TALLOC_FREE(session->auth_ntlmssp_state); - TALLOC_FREE(session); - return NT_STATUS_LOGON_FAILURE; + *out_session_flags = state->out_session_flags; + *out_security_buffer = state->out_security_buffer; + *out_session_id = state->out_session_id; + + talloc_steal(mem_ctx, out_security_buffer->data); + tevent_req_received(req); + return status; } NTSTATUS smbd_smb2_request_process_logoff(struct smbd_smb2_request *req) @@ -798,9 +763,19 @@ NTSTATUS smbd_smb2_request_process_logoff(struct smbd_smb2_request *req) /* * TODO: cancel all outstanding requests on the session - * and delete all tree connections. */ - smbd_smb2_session_destructor(req->session); + status = smbXsrv_session_logoff(req->session); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("smbd_smb2_request_process_logoff: " + "smbXsrv_session_logoff() failed: %s\n", + nt_errstr(status))); + /* + * If we hit this case, there is something completely + * wrong, so we better disconnect the transport connection. + */ + return status; + } + /* * we may need to sign the response, so we need to keep * the session until the response is sent to the wire.