2005-11-11 08:53:54 +03:00
/*
Unix SMB / CIFS implementation .
SMB2 client session handling
Copyright ( C ) Andrew Tridgell 2005
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2005-11-11 08:53:54 +03:00
( 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
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2005-11-11 08:53:54 +03:00
*/
# include "includes.h"
2011-10-08 14:10:20 +04:00
# include "system/network.h"
2011-04-27 20:24:01 +04:00
# include <tevent.h>
# include "lib/util/tevent_ntstatus.h"
2005-11-11 08:53:54 +03:00
# include "libcli/raw/libcliraw.h"
# include "libcli/smb2/smb2.h"
2005-11-11 09:26:42 +03:00
# include "libcli/smb2/smb2_calls.h"
2005-11-12 04:08:43 +03:00
# include "auth/gensec/gensec.h"
2012-04-16 14:14:07 +04:00
# include "auth/credentials/credentials.h"
2011-09-20 22:59:45 +04:00
# include "../libcli/smb/smbXcli_base.h"
2005-11-11 08:53:54 +03:00
2007-12-03 19:41:50 +03:00
/**
2005-11-11 08:53:54 +03:00
initialise a smb2_session structure
*/
struct smb2_session * smb2_session_init ( struct smb2_transport * transport ,
2008-11-02 04:05:48 +03:00
struct gensec_settings * settings ,
2014-07-10 10:21:22 +04:00
TALLOC_CTX * parent_ctx )
2005-11-11 08:53:54 +03:00
{
struct smb2_session * session ;
2005-11-11 09:26:42 +03:00
NTSTATUS status ;
2005-11-11 08:53:54 +03:00
session = talloc_zero ( parent_ctx , struct smb2_session ) ;
if ( ! session ) {
return NULL ;
}
2014-07-10 10:21:22 +04:00
session - > transport = talloc_steal ( session , transport ) ;
2005-11-11 08:53:54 +03:00
2011-09-20 22:59:45 +04:00
session - > smbXcli = smbXcli_session_create ( session , transport - > conn ) ;
if ( session - > smbXcli = = NULL ) {
talloc_free ( session ) ;
return NULL ;
}
2005-11-11 09:26:42 +03:00
/* prepare a gensec context for later use */
2011-09-20 22:59:45 +04:00
status = gensec_client_start ( session , & session - > gensec ,
2008-11-02 04:05:48 +03:00
settings ) ;
2005-11-11 09:26:42 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( session ) ;
return NULL ;
}
2005-11-29 01:53:42 +03:00
gensec_want_feature ( session - > gensec , GENSEC_FEATURE_SESSION_KEY ) ;
2005-11-11 08:53:54 +03:00
return session ;
}
2014-06-24 02:22:30 +04:00
/*
* Note : that the caller needs to keep ' transport ' around as
* long as the returned session is active !
*/
struct smb2_session * smb2_session_channel ( struct smb2_transport * transport ,
struct gensec_settings * settings ,
TALLOC_CTX * parent_ctx ,
struct smb2_session * base_session )
{
struct smb2_session * session ;
NTSTATUS status ;
session = talloc_zero ( parent_ctx , struct smb2_session ) ;
if ( ! session ) {
return NULL ;
}
session - > transport = transport ;
status = smb2cli_session_create_channel ( session ,
base_session - > smbXcli ,
transport - > conn ,
& session - > smbXcli ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( session ) ;
return NULL ;
}
session - > needs_bind = true ;
/* prepare a gensec context for later use */
status = gensec_client_start ( session , & session - > gensec ,
settings ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( session ) ;
return NULL ;
}
gensec_want_feature ( session - > gensec , GENSEC_FEATURE_SESSION_KEY ) ;
return session ;
}
2011-04-27 20:24:01 +04:00
struct smb2_session_setup_spnego_state {
2011-09-20 22:59:45 +04:00
struct tevent_context * ev ;
struct smb2_session * session ;
struct cli_credentials * credentials ;
2012-02-26 01:32:03 +04:00
uint64_t previous_session_id ;
2014-06-24 02:22:30 +04:00
bool session_bind ;
2012-03-06 18:45:37 +04:00
bool reauth ;
2005-11-12 04:08:43 +03:00
NTSTATUS gensec_status ;
2017-05-16 00:37:22 +03:00
NTSTATUS remote_status ;
2011-09-20 22:59:45 +04:00
DATA_BLOB in_secblob ;
DATA_BLOB out_secblob ;
2017-05-16 00:37:22 +03:00
struct iovec * recv_iov ;
2005-11-12 04:08:43 +03:00
} ;
2017-05-16 00:37:22 +03:00
static void smb2_session_setup_spnego_gensec_next ( struct tevent_req * req ) ;
static void smb2_session_setup_spnego_gensec_done ( struct tevent_req * subreq ) ;
static void smb2_session_setup_spnego_smb2_next ( struct tevent_req * req ) ;
static void smb2_session_setup_spnego_smb2_done ( struct tevent_req * subreq ) ;
static void smb2_session_setup_spnego_both_ready ( struct tevent_req * req ) ;
2011-04-27 11:16:52 +04:00
/*
a composite function that does a full SPNEGO session setup
*/
2012-02-26 01:32:03 +04:00
struct tevent_req * smb2_session_setup_spnego_send (
TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct smb2_session * session ,
struct cli_credentials * credentials ,
uint64_t previous_session_id )
2011-04-27 11:16:52 +04:00
{
2017-05-16 00:37:22 +03:00
struct smb2_transport * transport = session - > transport ;
2011-04-27 20:24:01 +04:00
struct tevent_req * req ;
struct smb2_session_setup_spnego_state * state ;
2012-03-06 18:45:37 +04:00
uint64_t current_session_id ;
2011-04-27 11:16:52 +04:00
const char * chosen_oid ;
2011-04-27 20:24:01 +04:00
NTSTATUS status ;
2011-09-20 22:59:45 +04:00
const DATA_BLOB * server_gss_blob ;
2017-05-16 00:37:22 +03:00
struct timeval endtime ;
bool ok ;
2011-04-27 11:16:52 +04:00
2011-04-27 20:24:01 +04:00
req = tevent_req_create ( mem_ctx , & state ,
struct smb2_session_setup_spnego_state ) ;
if ( req = = NULL ) {
return NULL ;
}
2011-09-20 22:59:45 +04:00
state - > ev = ev ;
state - > session = session ;
state - > credentials = credentials ;
2012-02-26 01:32:03 +04:00
state - > previous_session_id = previous_session_id ;
2017-05-16 00:37:22 +03:00
state - > gensec_status = NT_STATUS_MORE_PROCESSING_REQUIRED ;
state - > remote_status = NT_STATUS_MORE_PROCESSING_REQUIRED ;
endtime = timeval_current_ofs ( transport - > options . request_timeout , 0 ) ;
ok = tevent_req_set_endtime ( req , ev , endtime ) ;
if ( ! ok ) {
return tevent_req_post ( req , ev ) ;
}
2011-04-27 11:16:52 +04:00
2012-03-06 18:45:37 +04:00
current_session_id = smb2cli_session_current_id ( state - > session - > smbXcli ) ;
2014-06-24 02:22:30 +04:00
if ( state - > session - > needs_bind ) {
state - > session_bind = true ;
} else if ( current_session_id ! = 0 ) {
2012-03-06 18:45:37 +04:00
state - > reauth = true ;
}
2011-09-20 22:59:45 +04:00
server_gss_blob = smbXcli_conn_server_gss_blob ( session - > transport - > conn ) ;
if ( server_gss_blob ) {
2017-05-16 00:37:22 +03:00
state - > out_secblob = * server_gss_blob ;
2011-04-27 11:16:52 +04:00
}
2011-04-27 20:24:01 +04:00
status = gensec_set_credentials ( session - > gensec , credentials ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
2011-04-27 11:16:52 +04:00
2011-04-27 20:24:01 +04:00
status = gensec_set_target_hostname ( session - > gensec ,
2011-09-20 22:59:45 +04:00
smbXcli_conn_remote_name ( session - > transport - > conn ) ) ;
2011-04-27 20:24:01 +04:00
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
2011-04-27 11:16:52 +04:00
2011-04-27 20:24:01 +04:00
status = gensec_set_target_service ( session - > gensec , " cifs " ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
2011-04-27 11:16:52 +04:00
2017-05-16 00:37:22 +03:00
if ( state - > out_secblob . length > 0 ) {
2011-04-27 11:16:52 +04:00
chosen_oid = GENSEC_OID_SPNEGO ;
2018-07-20 00:04:33 +03:00
status = gensec_start_mech_by_oid ( session - > gensec , chosen_oid ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " Failed to start set GENSEC client mechanism %s: %s \n " ,
gensec_get_name_by_oid ( session - > gensec ,
chosen_oid ) ,
nt_errstr ( status ) ) ) ;
state - > out_secblob = data_blob_null ;
chosen_oid = GENSEC_OID_NTLMSSP ;
status = gensec_start_mech_by_oid ( session - > gensec ,
chosen_oid ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " Failed to start set (fallback) GENSEC client mechanism %s: %s \n " ,
gensec_get_name_by_oid ( session - > gensec ,
chosen_oid ) ,
nt_errstr ( status ) ) ) ;
}
}
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
2011-04-27 11:16:52 +04:00
} else {
chosen_oid = GENSEC_OID_NTLMSSP ;
2018-07-20 00:04:33 +03:00
status = gensec_start_mech_by_oid ( session - > gensec , chosen_oid ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " Failed to start set GENSEC client mechanism %s: %s \n " ,
gensec_get_name_by_oid ( session - > gensec ,
chosen_oid ) ,
nt_errstr ( status ) ) ) ;
}
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
2011-04-27 20:24:01 +04:00
}
2017-05-16 00:37:22 +03:00
smb2_session_setup_spnego_gensec_next ( req ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
2011-04-27 20:24:01 +04:00
return tevent_req_post ( req , ev ) ;
}
2017-05-16 00:37:22 +03:00
return req ;
}
static void smb2_session_setup_spnego_gensec_next ( struct tevent_req * req )
{
struct smb2_session_setup_spnego_state * state =
tevent_req_data ( req ,
struct smb2_session_setup_spnego_state ) ;
struct smb2_session * session = state - > session ;
struct tevent_req * subreq = NULL ;
if ( NT_STATUS_IS_OK ( state - > gensec_status ) ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
subreq = gensec_update_send ( state , state - > ev ,
session - > gensec ,
state - > out_secblob ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq ,
smb2_session_setup_spnego_gensec_done ,
req ) ;
}
static void smb2_session_setup_spnego_gensec_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct smb2_session_setup_spnego_state * state =
tevent_req_data ( req ,
struct smb2_session_setup_spnego_state ) ;
NTSTATUS status ;
status = gensec_update_recv ( subreq , state ,
& state - > in_secblob ) ;
2011-04-27 20:24:01 +04:00
state - > gensec_status = status ;
2017-05-16 00:37:22 +03:00
state - > out_secblob = data_blob_null ;
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
tevent_req_nterror ( req , status ) ;
return ;
}
if ( NT_STATUS_IS_OK ( state - > remote_status ) & &
NT_STATUS_IS_OK ( state - > gensec_status ) ) {
smb2_session_setup_spnego_both_ready ( req ) ;
return ;
}
smb2_session_setup_spnego_smb2_next ( req ) ;
}
static void smb2_session_setup_spnego_smb2_next ( struct tevent_req * req )
{
struct smb2_session_setup_spnego_state * state =
tevent_req_data ( req ,
struct smb2_session_setup_spnego_state ) ;
struct smb2_session * session = state - > session ;
uint32_t timeout_msec ;
uint8_t in_flags = 0 ;
struct tevent_req * subreq = NULL ;
if ( NT_STATUS_IS_OK ( state - > remote_status ) ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
timeout_msec = session - > transport - > options . request_timeout * 1000 ;
2011-04-27 11:16:52 +04:00
2014-06-24 02:22:30 +04:00
if ( state - > session_bind ) {
in_flags | = SMB2_SESSION_FLAG_BINDING ;
}
2011-09-20 22:59:45 +04:00
subreq = smb2cli_session_setup_send ( state , state - > ev ,
session - > transport - > conn ,
timeout_msec ,
session - > smbXcli ,
2014-06-24 02:22:30 +04:00
in_flags ,
2011-09-20 22:59:45 +04:00
0 , /* in_capabilities */
0 , /* in_channel */
2012-02-26 01:32:03 +04:00
state - > previous_session_id ,
2011-09-20 22:59:45 +04:00
& state - > in_secblob ) ;
2011-04-27 20:24:01 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
2017-05-16 00:37:22 +03:00
return ;
2011-04-27 11:16:52 +04:00
}
2017-05-16 00:37:22 +03:00
tevent_req_set_callback ( subreq ,
smb2_session_setup_spnego_smb2_done ,
req ) ;
2011-04-27 11:16:52 +04:00
}
2005-11-12 04:08:43 +03:00
/*
handle continuations of the spnego session setup
*/
2017-05-16 00:37:22 +03:00
static void smb2_session_setup_spnego_smb2_done ( struct tevent_req * subreq )
2005-11-12 04:08:43 +03:00
{
2011-04-27 20:24:01 +04:00
struct tevent_req * req =
2011-09-20 22:59:45 +04:00
tevent_req_callback_data ( subreq ,
2011-04-27 20:24:01 +04:00
struct tevent_req ) ;
struct smb2_session_setup_spnego_state * state =
tevent_req_data ( req ,
struct smb2_session_setup_spnego_state ) ;
NTSTATUS status ;
2005-11-12 04:08:43 +03:00
2011-09-20 22:59:45 +04:00
status = smb2cli_session_setup_recv ( subreq , state ,
2017-05-16 00:37:22 +03:00
& state - > recv_iov ,
2011-09-20 22:59:45 +04:00
& state - > out_secblob ) ;
2017-05-16 00:37:22 +03:00
state - > remote_status = status ;
state - > in_secblob = data_blob_null ;
2011-09-20 22:59:45 +04:00
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
tevent_req_nterror ( req , status ) ;
return ;
}
2005-11-12 04:08:43 +03:00
2017-05-16 00:37:22 +03:00
if ( NT_STATUS_IS_OK ( state - > remote_status ) & &
NT_STATUS_IS_OK ( state - > gensec_status ) ) {
smb2_session_setup_spnego_both_ready ( req ) ;
2009-05-20 21:57:37 +04:00
return ;
}
2005-11-12 04:08:43 +03:00
2017-05-16 00:37:22 +03:00
smb2_session_setup_spnego_gensec_next ( req ) ;
}
2012-02-27 12:20:20 +04:00
2017-05-16 00:37:22 +03:00
static void smb2_session_setup_spnego_both_ready ( struct tevent_req * req )
{
struct smb2_session_setup_spnego_state * state =
tevent_req_data ( req ,
struct smb2_session_setup_spnego_state ) ;
struct smb2_session * session = state - > session ;
NTSTATUS status ;
DATA_BLOB session_key ;
2012-03-06 18:45:37 +04:00
2017-05-16 00:37:22 +03:00
if ( state - > out_secblob . length ! = 0 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
2012-04-16 14:14:07 +04:00
2017-05-16 00:37:22 +03:00
if ( state - > in_secblob . length ! = 0 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
2005-11-12 04:08:43 +03:00
2017-05-16 00:37:22 +03:00
if ( state - > reauth ) {
2011-09-20 22:59:45 +04:00
tevent_req_done ( req ) ;
return ;
2008-05-30 11:03:54 +04:00
}
2017-05-16 00:37:22 +03:00
if ( cli_credentials_is_anonymous ( state - > credentials ) ) {
/*
* Windows server does not set the
* SMB2_SESSION_FLAG_IS_GUEST nor
* SMB2_SESSION_FLAG_IS_NULL flag .
*
* This fix makes sure we do not try
* to verify a signature on the final
* session setup response .
*/
tevent_req_done ( req ) ;
return ;
2014-06-24 02:22:30 +04:00
}
2017-05-16 00:37:22 +03:00
status = gensec_session_key ( session - > gensec , state ,
& session_key ) ;
if ( tevent_req_nterror ( req , status ) ) {
2011-09-20 22:59:45 +04:00
return ;
}
2017-05-16 00:37:22 +03:00
if ( state - > session_bind ) {
status = smb2cli_session_set_channel_key ( session - > smbXcli ,
session_key ,
state - > recv_iov ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
session - > needs_bind = false ;
} else {
status = smb2cli_session_set_session_key ( session - > smbXcli ,
session_key ,
state - > recv_iov ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
}
tevent_req_done ( req ) ;
return ;
2005-11-12 04:08:43 +03:00
}
/*
receive a composite session setup reply
*/
2011-04-27 20:24:01 +04:00
NTSTATUS smb2_session_setup_spnego_recv ( struct tevent_req * req )
2005-11-12 04:08:43 +03:00
{
2011-04-27 20:24:01 +04:00
return tevent_req_simple_recv_ntstatus ( req ) ;
2005-11-12 04:08:43 +03:00
}
/*
sync version of smb2_session_setup_spnego
*/
NTSTATUS smb2_session_setup_spnego ( struct smb2_session * session ,
2012-02-26 01:32:03 +04:00
struct cli_credentials * credentials ,
uint64_t previous_session_id )
2005-11-12 04:08:43 +03:00
{
2011-04-27 20:24:01 +04:00
struct tevent_req * subreq ;
NTSTATUS status ;
bool ok ;
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2011-09-20 22:59:45 +04:00
struct tevent_context * ev = session - > transport - > ev ;
2011-04-27 20:24:01 +04:00
if ( frame = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
subreq = smb2_session_setup_spnego_send ( frame , ev ,
2012-02-26 01:32:03 +04:00
session , credentials ,
previous_session_id ) ;
2011-04-27 20:24:01 +04:00
if ( subreq = = NULL ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_NO_MEMORY ;
}
ok = tevent_req_poll ( subreq , ev ) ;
if ( ! ok ) {
2011-06-20 08:55:32 +04:00
status = map_nt_error_from_unix_common ( errno ) ;
2011-04-27 20:24:01 +04:00
TALLOC_FREE ( frame ) ;
return status ;
}
status = smb2_session_setup_spnego_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( frame ) ;
return status ;
}
TALLOC_FREE ( frame ) ;
return NT_STATUS_OK ;
2005-11-12 04:08:43 +03:00
}