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"
# 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 "libcli/composite/composite.h"
# include "auth/gensec/gensec.h"
2005-11-11 08:53:54 +03:00
/*
initialise a smb2_session structure
*/
struct smb2_session * smb2_session_init ( struct smb2_transport * transport ,
2007-10-07 02:28:14 +04:00
TALLOC_CTX * parent_ctx , bool primary )
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 ;
}
if ( primary ) {
session - > transport = talloc_steal ( session , transport ) ;
} else {
session - > transport = talloc_reference ( session , transport ) ;
}
2005-11-11 09:26:42 +03:00
/* prepare a gensec context for later use */
status = gensec_client_start ( session , & session - > gensec ,
session - > transport - > socket - > event . ctx ) ;
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 ;
}
2005-11-11 09:26:42 +03:00
/*
send a session setup request
*/
struct smb2_request * smb2_session_setup_send ( struct smb2_session * session ,
struct smb2_session_setup * io )
{
struct smb2_request * req ;
2005-11-11 10:23:45 +03:00
NTSTATUS status ;
2005-11-11 09:26:42 +03:00
req = smb2_request_init ( session - > transport , SMB2_OP_SESSSETUP ,
2007-10-07 02:28:14 +04:00
0x18 , true , io - > in . secblob . length ) ;
2005-11-11 09:26:42 +03:00
if ( req = = NULL ) return NULL ;
2005-11-11 10:23:45 +03:00
SBVAL ( req - > out . hdr , SMB2_HDR_UID , session - > uid ) ;
2006-06-30 15:07:47 +04:00
SSVAL ( req - > out . body , 0x02 , io - > in . _pad ) ; /* pad */
2005-11-11 09:26:42 +03:00
SIVAL ( req - > out . body , 0x04 , io - > in . unknown2 ) ;
SIVAL ( req - > out . body , 0x08 , io - > in . unknown3 ) ;
2005-11-16 14:01:15 +03:00
2005-11-12 04:08:43 +03:00
req - > session = session ;
2005-11-16 14:01:15 +03:00
2005-11-17 06:32:38 +03:00
status = smb2_push_o16s16_blob ( & req - > out , 0x0C , io - > in . secblob ) ;
2005-11-11 10:23:45 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( req ) ;
return NULL ;
}
2006-06-30 15:07:47 +04:00
SBVAL ( req - > out . body , 0x10 , io - > in . unknown4 ) ;
2005-11-11 09:26:42 +03:00
smb2_transport_send ( req ) ;
return req ;
}
/*
recv a session setup reply
*/
NTSTATUS smb2_session_setup_recv ( struct smb2_request * req , TALLOC_CTX * mem_ctx ,
struct smb2_session_setup * io )
{
2005-11-11 10:23:45 +03:00
NTSTATUS status ;
2005-11-11 09:26:42 +03:00
if ( ! smb2_request_receive ( req ) | |
2005-11-11 10:23:45 +03:00
( smb2_request_is_error ( req ) & &
! NT_STATUS_EQUAL ( req - > status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) ) {
2005-11-11 09:26:42 +03:00
return smb2_request_destroy ( req ) ;
}
2007-10-07 02:28:14 +04:00
SMB2_CHECK_PACKET_RECV ( req , 0x08 , true ) ;
2005-11-12 02:27:47 +03:00
io - > out . _pad = SVAL ( req - > in . body , 0x02 ) ;
2005-11-11 10:23:45 +03:00
io - > out . uid = BVAL ( req - > in . hdr , SMB2_HDR_UID ) ;
2005-11-16 14:01:15 +03:00
status = smb2_pull_o16s16_blob ( & req - > in , mem_ctx , req - > in . body + 0x04 , & io - > out . secblob ) ;
2005-11-11 10:23:45 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
smb2_request_destroy ( req ) ;
return status ;
}
2005-11-11 09:26:42 +03:00
return smb2_request_destroy ( req ) ;
}
/*
sync session setup request
*/
NTSTATUS smb2_session_setup ( struct smb2_session * session ,
TALLOC_CTX * mem_ctx , struct smb2_session_setup * io )
{
struct smb2_request * req = smb2_session_setup_send ( session , io ) ;
return smb2_session_setup_recv ( req , mem_ctx , io ) ;
}
2005-11-12 04:08:43 +03:00
struct smb2_session_state {
struct smb2_session_setup io ;
struct smb2_request * req ;
NTSTATUS gensec_status ;
} ;
/*
handle continuations of the spnego session setup
*/
static void session_request_handler ( struct smb2_request * req )
{
struct composite_context * c = talloc_get_type ( req - > async . private ,
struct composite_context ) ;
struct smb2_session_state * state = talloc_get_type ( c - > private_data ,
struct smb2_session_state ) ;
struct smb2_session * session = req - > session ;
c - > status = smb2_session_setup_recv ( req , c , & state - > io ) ;
if ( NT_STATUS_EQUAL ( c - > status , NT_STATUS_MORE_PROCESSING_REQUIRED ) | |
( NT_STATUS_IS_OK ( c - > status ) & &
NT_STATUS_EQUAL ( state - > gensec_status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) ) {
2005-11-25 08:23:55 +03:00
NTSTATUS session_key_err ;
DATA_BLOB session_key ;
2005-11-12 05:16:19 +03:00
c - > status = gensec_update ( session - > gensec , c ,
2005-11-12 04:08:43 +03:00
state - > io . out . secblob ,
& state - > io . in . secblob ) ;
state - > gensec_status = c - > status ;
2005-11-25 08:23:55 +03:00
session_key_err = gensec_session_key ( session - > gensec , & session_key ) ;
if ( NT_STATUS_IS_OK ( session_key_err ) ) {
session - > session_key = session_key ;
}
2005-11-12 04:08:43 +03:00
}
session - > uid = state - > io . out . uid ;
if ( NT_STATUS_EQUAL ( c - > status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
state - > req = smb2_session_setup_send ( session , & state - > io ) ;
if ( state - > req = = NULL ) {
composite_error ( c , NT_STATUS_NO_MEMORY ) ;
2006-03-15 08:53:15 +03:00
return ;
2005-11-12 04:08:43 +03:00
}
state - > req - > async . fn = session_request_handler ;
state - > req - > async . private = c ;
return ;
}
if ( ! NT_STATUS_IS_OK ( c - > status ) ) {
composite_error ( c , c - > status ) ;
return ;
}
composite_done ( c ) ;
}
/*
a composite function that does a full SPNEGO session setup
*/
struct composite_context * smb2_session_setup_spnego_send ( struct smb2_session * session ,
struct cli_credentials * credentials )
{
struct composite_context * c ;
struct smb2_session_state * state ;
2006-07-30 21:29:02 +04:00
c = composite_create ( session , session - > transport - > socket - > event . ctx ) ;
2005-11-12 04:08:43 +03:00
if ( c = = NULL ) return NULL ;
state = talloc ( c , struct smb2_session_state ) ;
2006-07-30 21:29:02 +04:00
if ( composite_nomem ( state , c ) ) return c ;
2005-11-12 04:08:43 +03:00
c - > private_data = state ;
ZERO_STRUCT ( state - > io ) ;
2006-06-30 15:07:47 +04:00
state - > io . in . _pad = 0x0000 ;
state - > io . in . unknown2 = 0x0000000F ;
state - > io . in . unknown3 = 0x00000000 ;
state - > io . in . unknown4 = 0 ; /* uint64_t */
2005-11-12 04:08:43 +03:00
c - > status = gensec_set_credentials ( session - > gensec , credentials ) ;
2006-07-30 21:29:02 +04:00
if ( ! composite_is_ok ( c ) ) return c ;
2005-11-12 04:08:43 +03:00
c - > status = gensec_set_target_hostname ( session - > gensec ,
session - > transport - > socket - > hostname ) ;
2006-07-30 21:29:02 +04:00
if ( ! composite_is_ok ( c ) ) return c ;
2005-11-12 04:08:43 +03:00
c - > status = gensec_set_target_service ( session - > gensec , " cifs " ) ;
2006-07-30 21:29:02 +04:00
if ( ! composite_is_ok ( c ) ) return c ;
2005-11-12 04:08:43 +03:00
c - > status = gensec_start_mech_by_oid ( session - > gensec , GENSEC_OID_SPNEGO ) ;
2006-07-30 21:29:02 +04:00
if ( ! composite_is_ok ( c ) ) return c ;
2005-11-12 04:08:43 +03:00
c - > status = gensec_update ( session - > gensec , c ,
session - > transport - > negotiate . secblob ,
& state - > io . in . secblob ) ;
if ( ! NT_STATUS_EQUAL ( c - > status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
2006-07-30 21:29:02 +04:00
composite_error ( c , c - > status ) ;
return c ;
2005-11-12 04:08:43 +03:00
}
state - > gensec_status = c - > status ;
state - > req = smb2_session_setup_send ( session , & state - > io ) ;
2006-07-30 21:29:02 +04:00
composite_continue_smb2 ( c , state - > req , session_request_handler , c ) ;
2005-11-12 04:08:43 +03:00
return c ;
}
/*
receive a composite session setup reply
*/
NTSTATUS smb2_session_setup_spnego_recv ( struct composite_context * c )
{
NTSTATUS status ;
status = composite_wait ( c ) ;
talloc_free ( c ) ;
return status ;
}
/*
sync version of smb2_session_setup_spnego
*/
NTSTATUS smb2_session_setup_spnego ( struct smb2_session * session ,
struct cli_credentials * credentials )
{
struct composite_context * c = smb2_session_setup_spnego_send ( session , credentials ) ;
return smb2_session_setup_spnego_recv ( c ) ;
}