2009-05-15 13:20:34 +04:00
/*
Unix SMB / CIFS implementation .
Core SMB2 server
Copyright ( C ) Stefan Metzmacher 2009
2010-05-18 00:05:22 +04:00
Copyright ( C ) Jeremy Allison 2010
2009-05-15 13:20:34 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2011-03-22 18:57:01 +03:00
# include "smbd/smbd.h"
2009-05-15 13:20:34 +04:00
# include "smbd/globals.h"
2009-08-12 19:52:55 +04:00
# include "../libcli/smb/smb_common.h"
2011-07-26 09:11:47 +04:00
# include "../auth/gensec/gensec.h"
2011-03-24 15:46:20 +03:00
# include "auth.h"
2011-06-16 18:03:03 +04:00
# include "../lib/tsocket/tsocket.h"
2011-07-19 05:57:05 +04:00
# include "../libcli/security/security.h"
2012-05-12 12:16:41 +04:00
# include "../lib/util/tevent_ntstatus.h"
2009-05-15 13:20:34 +04:00
2012-05-12 12:16:41 +04:00
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 ,
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 ) ;
2012-05-12 12:20:55 +04:00
static void smbd_smb2_request_sesssetup_done ( struct tevent_req * subreq ) ;
2010-05-18 00:05:22 +04:00
NTSTATUS smbd_smb2_request_process_sesssetup ( struct smbd_smb2_request * smb2req )
2009-05-15 13:20:34 +04:00
{
const uint8_t * inhdr ;
const uint8_t * inbody ;
uint64_t in_session_id ;
2012-05-12 11:48:19 +04:00
uint8_t in_flags ;
2009-05-23 00:58:39 +04:00
uint8_t in_security_mode ;
2012-04-18 17:00:06 +04:00
uint64_t in_previous_session_id ;
2009-05-15 13:20:34 +04:00
uint16_t in_security_offset ;
uint16_t in_security_length ;
DATA_BLOB in_security_buffer ;
NTSTATUS status ;
2012-05-12 12:20:55 +04:00
struct tevent_req * subreq ;
2009-05-15 13:20:34 +04:00
2011-09-06 16:01:43 +04:00
status = smbd_smb2_request_verify_sizes ( smb2req , 0x19 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return smbd_smb2_request_error ( smb2req , status ) ;
2009-05-15 13:20:34 +04:00
}
2012-08-05 17:00:23 +04:00
inhdr = SMBD_SMB2_IN_HDR_PTR ( smb2req ) ;
inbody = SMBD_SMB2_IN_BODY_PTR ( smb2req ) ;
2009-05-15 13:20:34 +04:00
2012-04-18 17:00:06 +04:00
in_session_id = BVAL ( inhdr , SMB2_HDR_SESSION_ID ) ;
2012-05-12 11:48:19 +04:00
in_flags = CVAL ( inbody , 0x02 ) ;
2012-04-18 17:00:06 +04:00
in_security_mode = CVAL ( inbody , 0x03 ) ;
/* Capabilities = IVAL(inbody, 0x04) */
/* Channel = IVAL(inbody, 0x08) */
2009-05-15 13:20:34 +04:00
in_security_offset = SVAL ( inbody , 0x0C ) ;
in_security_length = SVAL ( inbody , 0x0E ) ;
2012-04-18 17:00:06 +04:00
in_previous_session_id = BVAL ( inbody , 0x10 ) ;
2009-05-15 13:20:34 +04:00
2012-08-05 17:00:23 +04:00
if ( in_security_offset ! = ( SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN ( smb2req ) ) ) {
2010-05-18 00:05:22 +04:00
return smbd_smb2_request_error ( smb2req , NT_STATUS_INVALID_PARAMETER ) ;
2009-05-15 13:20:34 +04:00
}
2012-08-05 17:00:23 +04:00
if ( in_security_length > SMBD_SMB2_IN_DYN_LEN ( smb2req ) ) {
2010-05-18 00:05:22 +04:00
return smbd_smb2_request_error ( smb2req , NT_STATUS_INVALID_PARAMETER ) ;
2009-05-15 13:20:34 +04:00
}
2012-08-05 17:00:23 +04:00
in_security_buffer . data = SMBD_SMB2_IN_DYN_PTR ( smb2req ) ;
2009-05-15 13:20:34 +04:00
in_security_buffer . length = in_security_length ;
2012-05-12 12:20:55 +04:00
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 ;
2013-12-10 20:49:44 +04:00
uint16_t out_session_flags = 0 ;
uint64_t out_session_id = 0 ;
2012-05-12 12:20:55 +04:00
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 ) ;
2009-05-15 13:20:34 +04:00
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
status = nt_status_squash ( status ) ;
2012-05-12 12:20:55 +04:00
error = smbd_smb2_request_error ( smb2req , status ) ;
if ( ! NT_STATUS_IS_OK ( error ) ) {
smbd_server_connection_terminate ( smb2req - > sconn ,
nt_errstr ( error ) ) ;
return ;
}
return ;
2009-05-15 13:20:34 +04:00
}
out_security_offset = SMB2_HDR_BODY + 0x08 ;
2012-08-05 17:00:23 +04:00
outhdr = SMBD_SMB2_OUT_HDR_PTR ( smb2req ) ;
2009-05-15 13:20:34 +04:00
2013-12-04 17:59:07 +04:00
outbody = smbd_smb2_generate_outbody ( smb2req , 0x08 ) ;
2009-05-15 13:20:34 +04:00
if ( outbody . data = = NULL ) {
2012-05-12 12:20:55 +04:00
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 ;
2009-05-15 13:20:34 +04:00
}
SBVAL ( outhdr , SMB2_HDR_SESSION_ID , out_session_id ) ;
SSVAL ( outbody . data , 0x00 , 0x08 + 1 ) ; /* struct size */
2009-05-23 00:58:39 +04:00
SSVAL ( outbody . data , 0x02 ,
out_session_flags ) ; /* session flags */
2009-05-15 13:20:34 +04:00
SSVAL ( outbody . data , 0x04 ,
out_security_offset ) ; /* security buffer offset */
SSVAL ( outbody . data , 0x06 ,
out_security_buffer . length ) ; /* security buffer length */
outdyn = out_security_buffer ;
2012-05-12 12:20:55 +04:00
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 ;
}
2009-05-15 13:20:34 +04:00
}
2012-03-27 13:09:05 +04:00
static NTSTATUS smbd_smb2_auth_generic_return ( struct smbXsrv_session * session ,
2010-05-18 00:05:22 +04:00
struct smbd_smb2_request * smb2req ,
uint8_t in_security_mode ,
2012-05-17 02:30:43 +04:00
struct auth_session_info * session_info ,
2010-05-18 05:22:19 +04:00
uint16_t * out_session_flags ,
uint64_t * out_session_id )
2010-05-18 00:05:22 +04:00
{
2012-03-16 18:01:27 +04:00
NTSTATUS status ;
2011-09-21 05:56:30 +04:00
bool guest = false ;
2012-03-16 18:01:27 +04:00
uint8_t session_key [ 16 ] ;
2012-03-27 13:09:05 +04:00
struct smbXsrv_session * x = session ;
struct smbXsrv_connection * conn = session - > connection ;
2011-09-21 05:56:30 +04:00
2009-05-23 00:58:39 +04:00
if ( ( in_security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED ) | |
2011-11-02 22:07:45 +04:00
lp_server_signing ( ) = = SMB_SIGNING_REQUIRED ) {
2012-03-16 18:01:27 +04:00
x - > global - > signing_required = true ;
2009-05-23 00:58:39 +04:00
}
2012-08-08 08:25:10 +04:00
if ( lp_smb_encrypt ( - 1 ) = = SMB_SIGNING_REQUIRED ) {
x - > global - > encryption_required = true ;
}
2012-03-27 13:09:05 +04:00
if ( security_session_user_level ( session_info , NULL ) < SECURITY_USER ) {
2009-05-23 00:58:39 +04:00
/* 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 */
2012-03-16 18:01:27 +04:00
x - > global - > signing_required = false ;
2011-09-21 05:56:30 +04:00
guest = true ;
2009-05-23 00:58:39 +04:00
}
2012-08-08 08:25:10 +04:00
if ( guest & & x - > global - > encryption_required ) {
DEBUG ( 1 , ( " reject guest session as encryption is required \n " ) ) ;
return NT_STATUS_ACCESS_DENIED ;
}
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 ( x - > global - > encryption_required ) {
* out_session_flags | = SMB2_SESSION_FLAG_ENCRYPT_DATA ;
}
2012-03-16 18:01:27 +04:00
ZERO_STRUCT ( session_key ) ;
2012-03-27 13:09:05 +04:00
memcpy ( session_key , session_info - > session_key . data ,
MIN ( session_info - > session_key . length , sizeof ( session_key ) ) ) ;
2012-03-16 18:01:27 +04:00
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 ;
}
2012-03-16 18:01:27 +04:00
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 " ) ;
smb2_key_derivation ( session_key , sizeof ( session_key ) ,
label . data , label . length ,
context . data , context . length ,
x - > global - > signing_key . data ) ;
}
2012-08-08 11:32:22 +04:00
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 " ) ;
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 ;
}
smb2_key_derivation ( session_key , sizeof ( session_key ) ,
label . data , label . length ,
context . data , context . length ,
x - > global - > decryption_key . data ) ;
}
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 " ) ;
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 ;
}
smb2_key_derivation ( session_key , sizeof ( session_key ) ,
label . data , label . length ,
context . data , context . length ,
x - > global - > encryption_key . data ) ;
2012-08-22 12:30:52 +04:00
generate_random_buffer ( ( uint8_t * ) & x - > nonce_high , sizeof ( x - > nonce_high ) ) ;
x - > nonce_low = 1 ;
2012-08-08 11:32:22 +04:00
}
2012-03-16 18:01:27 +04:00
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 ;
}
2012-03-16 18:01:27 +04:00
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 " ) ;
smb2_key_derivation ( session_key , sizeof ( session_key ) ,
label . data , label . length ,
context . data , context . length ,
x - > global - > application_key . data ) ;
}
2012-03-16 18:01:27 +04:00
ZERO_STRUCT ( session_key ) ;
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 ;
}
2009-05-23 00:58:39 +04:00
2012-03-16 18:01:27 +04:00
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 ;
}
2012-03-27 13:09:05 +04:00
session - > compat = talloc_zero ( session , struct user_struct ) ;
if ( session - > compat = = NULL ) {
2009-05-27 20:33:45 +04:00
return NT_STATUS_NO_MEMORY ;
}
2012-03-27 13:09:05 +04:00
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 + + ;
2009-05-27 20:33:45 +04:00
2012-03-27 13:09:05 +04:00
if ( security_session_user_level ( session_info , NULL ) > = SECURITY_USER ) {
session - > compat - > homes_snum =
register_homes_share ( session_info - > unix_info - > unix_name ) ;
2010-05-18 05:22:19 +04:00
}
2012-03-27 13:09:05 +04:00
set_current_user_info ( session_info - > unix_info - > sanitized_username ,
session_info - > unix_info - > unix_name ,
session_info - > info - > domain_name ) ;
2012-01-25 08:22:38 +04:00
reload_services ( smb2req - > sconn , conn_snum_used , true ) ;
2010-05-18 05:22:19 +04:00
2012-03-27 13:09:05 +04:00
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 ;
2012-05-14 16:24:08 +04:00
session - > global - > expiration_time = gensec_expire_time ( session - > gensec ) ;
2012-03-16 18:01:27 +04:00
2012-08-27 13:03:25 +04:00
if ( ! session_claim ( session ) ) {
DEBUG ( 1 , ( " smb2: Failed to claim session "
" for vuid=%llu \n " ,
( unsigned long long ) session - > compat - > vuid ) ) ;
return NT_STATUS_LOGON_FAILURE ;
}
2012-03-27 13:09:05 +04:00
status = smbXsrv_session_update ( session ) ;
2012-03-16 18:01:27 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " smb2: Failed to update session for vuid=%llu - %s \n " ,
2012-03-27 13:09:05 +04:00
( unsigned long long ) session - > compat - > vuid ,
2012-03-16 18:01:27 +04:00
nt_errstr ( status ) ) ) ;
return NT_STATUS_LOGON_FAILURE ;
}
2009-05-23 00:58:39 +04:00
/*
* we attach the session to the request
* so that the response can be signed
*/
2010-05-18 00:05:22 +04:00
smb2req - > session = session ;
2011-09-21 05:56:30 +04:00
if ( ! guest ) {
2010-05-18 00:05:22 +04:00
smb2req - > do_signing = true ;
}
global_client_caps | = ( CAP_LEVEL_II_OPLOCKS | CAP_STATUS32 ) ;
2010-05-18 05:22:19 +04:00
2012-03-27 13:09:05 +04:00
* out_session_id = session - > global - > session_wire_id ;
2010-05-18 05:22:19 +04:00
2010-05-18 00:05:22 +04:00
return NT_STATUS_OK ;
}
2012-05-14 16:24:08 +04:00
static NTSTATUS smbd_smb2_reauth_generic_return ( struct smbXsrv_session * session ,
struct smbd_smb2_request * smb2req ,
2012-05-17 02:30:43 +04:00
struct auth_session_info * session_info ,
2012-05-14 16:24:08 +04:00
uint16_t * out_session_flags ,
uint64_t * out_session_id )
{
NTSTATUS status ;
struct smbXsrv_session * x = session ;
struct smbXsrv_connection * conn = session - > connection ;
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 - > compat - > session_info = session_info ;
session - > compat - > vuid = session - > global - > session_wire_id ;
session - > compat - > homes_snum =
register_homes_share ( session_info - > unix_info - > unix_name ) ;
set_current_user_info ( session_info - > unix_info - > sanitized_username ,
session_info - > unix_info - > unix_name ,
session_info - > info - > domain_name ) ;
reload_services ( smb2req - > sconn , conn_snum_used , true ) ;
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 ;
}
conn_clear_vuid_caches ( conn - > sconn , session - > compat - > vuid ) ;
* out_session_id = session - > global - > session_wire_id ;
return NT_STATUS_OK ;
}
2012-05-12 12:16:41 +04:00
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 ;
2012-05-16 17:56:15 +04:00
struct smbXsrv_session * session ;
2012-05-17 02:30:43 +04:00
struct auth_session_info * session_info ;
2012-05-12 12:16:41 +04:00
uint16_t out_session_flags ;
DATA_BLOB out_security_buffer ;
uint64_t out_session_id ;
2012-09-07 04:14:52 +04:00
/* The following pointer is owned by state->session. */
struct smbd_smb2_session_setup_state * * pp_self_ref ;
2012-05-12 12:16:41 +04:00
} ;
2012-09-07 04:14:52 +04:00
static int pp_self_ref_destructor ( struct smbd_smb2_session_setup_state * * pp_state )
{
( * pp_state ) - > session = NULL ;
2012-09-12 03:16:37 +04:00
/*
* 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 ;
2012-09-07 04:14:52 +04:00
return 0 ;
}
2012-05-16 17:56:15 +04:00
static int smbd_smb2_session_setup_state_destructor ( struct smbd_smb2_session_setup_state * state )
{
2014-06-10 15:24:50 +04:00
struct smbXsrv_connection * xconn ;
2013-09-24 01:10:27 +04:00
struct smbd_smb2_request * preq ;
2012-05-16 17:56:15 +04:00
/*
2013-09-20 01:41:51 +04:00
* If state - > session is not NULL ,
* we move the session from the session table to the request on failure
* so that the error response can be correctly signed , but the session
* is then really deleted when the request is done .
2012-05-16 17:56:15 +04:00
*/
2013-09-20 01:41:51 +04:00
if ( state - > session = = NULL ) {
return 0 ;
}
state - > session - > status = NT_STATUS_USER_SESSION_DELETED ;
state - > smb2req - > session = talloc_move ( state - > smb2req , & state - > session ) ;
2013-09-24 01:10:27 +04:00
/*
* We ' ve made this session owned by the current request .
* Ensure that any outstanding requests don ' t also refer
* to it .
*/
2014-06-10 15:24:50 +04:00
xconn = state - > smb2req - > sconn - > conn ;
2013-09-24 01:10:27 +04:00
2014-06-10 15:24:50 +04:00
for ( preq = xconn - > smb2 . requests ; preq ! = NULL ; preq = preq - > next ) {
2013-09-24 01:10:27 +04:00
if ( preq = = state - > smb2req ) {
continue ;
}
if ( preq - > session = = state - > smb2req - > session ) {
preq - > session = NULL ;
/*
* If we no longer have a session we can ' t
* sign or encrypt replies .
*/
preq - > do_signing = false ;
preq - > do_encryption = false ;
}
}
2012-05-16 17:56:15 +04:00
return 0 ;
}
2012-05-17 02:30:43 +04:00
static void smbd_smb2_session_setup_gensec_done ( struct tevent_req * subreq ) ;
2012-05-17 02:32:40 +04:00
static void smbd_smb2_session_setup_previous_done ( struct tevent_req * subreq ) ;
2012-05-16 18:05:48 +04:00
2012-09-07 04:14:52 +04:00
/************************************************************************
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 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
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 ;
}
* state - > pp_self_ref = state ;
talloc_set_destructor ( state - > pp_self_ref , pp_self_ref_destructor ) ;
return NT_STATUS_OK ;
}
2012-05-12 12:16:41 +04:00
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 ,
uint64_t in_previous_session_id ,
DATA_BLOB in_security_buffer )
{
struct tevent_req * req ;
struct smbd_smb2_session_setup_state * state ;
NTSTATUS status ;
2012-05-16 17:56:15 +04:00
NTTIME now = timeval_to_nttime ( & smb2req - > request_time ) ;
2012-05-16 18:05:48 +04:00
struct tevent_req * subreq ;
2012-05-12 12:16:41 +04:00
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 ;
2012-07-26 02:11:13 +04:00
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 ) ;
}
/*
* We do not support multi channel .
*/
tevent_req_nterror ( req , NT_STATUS_NOT_SUPPORTED ) ;
return tevent_req_post ( req , ev ) ;
}
2012-05-16 17:56:15 +04:00
talloc_set_destructor ( state , smbd_smb2_session_setup_state_destructor ) ;
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 ) ;
}
}
2012-09-07 04:14:52 +04:00
status = tag_state_session_ptr ( state ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
2012-05-16 17:56:15 +04:00
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 ) ;
}
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 ) ;
}
}
become_root ( ) ;
2012-05-16 18:05:48 +04:00
subreq = gensec_update_send ( state , state - > ev ,
state - > session - > gensec ,
state - > in_security_buffer ) ;
2012-05-16 17:56:15 +04:00
unbecome_root ( ) ;
2012-05-16 18:05:48 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2012-05-17 02:30:43 +04:00
tevent_req_set_callback ( subreq , smbd_smb2_session_setup_gensec_done , req ) ;
2012-05-16 18:05:48 +04:00
return req ;
}
2012-05-17 02:30:43 +04:00
static void smbd_smb2_session_setup_gensec_done ( struct tevent_req * subreq )
2012-05-16 18:05:48 +04:00
{
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 ) ;
2012-05-16 17:56:15 +04:00
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) & &
! NT_STATUS_IS_OK ( status ) ) {
tevent_req_nterror ( req , status ) ;
2012-05-16 18:05:48 +04:00
return ;
2012-05-16 17:56:15 +04:00
}
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 */
2013-09-20 01:41:51 +04:00
state - > session = NULL ;
2012-09-07 04:14:52 +04:00
TALLOC_FREE ( state - > pp_self_ref ) ;
2012-05-16 17:56:15 +04:00
tevent_req_nterror ( req , status ) ;
2012-05-16 18:05:48 +04:00
return ;
2012-05-16 17:56:15 +04:00
}
2012-05-17 02:30:43 +04:00
status = gensec_session_info ( state - > session - > gensec ,
state - > session - > global ,
& state - > session_info ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2012-05-17 02:32:40 +04:00
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 */
2013-09-20 01:41:51 +04:00
state - > session = NULL ;
2012-09-07 04:14:52 +04:00
TALLOC_FREE ( state - > pp_self_ref ) ;
2012-05-17 02:32:40 +04:00
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 */
2013-09-20 01:41:51 +04:00
state - > session = NULL ;
2012-09-07 04:14:52 +04:00
TALLOC_FREE ( state - > pp_self_ref ) ;
2012-05-17 02:32:40 +04:00
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 ;
status = smb2srv_session_close_previous_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2012-05-16 17:56:15 +04:00
if ( state - > session - > global - > auth_session_info ! = NULL ) {
status = smbd_smb2_reauth_generic_return ( state - > session ,
state - > smb2req ,
2012-05-17 02:30:43 +04:00
state - > session_info ,
2012-05-16 17:56:15 +04:00
& state - > out_session_flags ,
& state - > out_session_id ) ;
if ( tevent_req_nterror ( req , status ) ) {
2012-05-16 18:05:48 +04:00
return ;
2012-05-16 17:56:15 +04:00
}
/* we want to keep the session */
2013-09-20 01:41:51 +04:00
state - > session = NULL ;
2012-09-07 04:14:52 +04:00
TALLOC_FREE ( state - > pp_self_ref ) ;
2012-05-16 17:56:15 +04:00
tevent_req_done ( req ) ;
2012-05-16 18:05:48 +04:00
return ;
2012-05-12 12:16:41 +04:00
}
2012-05-16 17:56:15 +04:00
status = smbd_smb2_auth_generic_return ( state - > session ,
state - > smb2req ,
state - > in_security_mode ,
2012-05-17 02:30:43 +04:00
state - > session_info ,
2012-05-16 17:56:15 +04:00
& state - > out_session_flags ,
& state - > out_session_id ) ;
2012-05-12 12:16:41 +04:00
if ( tevent_req_nterror ( req , status ) ) {
2012-05-16 18:05:48 +04:00
return ;
2012-05-12 12:16:41 +04:00
}
2012-05-16 17:56:15 +04:00
/* we want to keep the session */
2013-09-20 01:41:51 +04:00
state - > session = NULL ;
2012-09-07 04:14:52 +04:00
TALLOC_FREE ( state - > pp_self_ref ) ;
2012-05-12 12:16:41 +04:00
tevent_req_done ( req ) ;
2012-05-16 18:05:48 +04:00
return ;
2012-05-12 12:16:41 +04:00
}
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_setup_state * state =
tevent_req_data ( req ,
struct smbd_smb2_session_setup_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
tevent_req_received ( req ) ;
2012-05-16 17:32:08 +04:00
return nt_status_squash ( status ) ;
2012-05-12 12:16:41 +04:00
}
} else {
status = NT_STATUS_OK ;
}
* 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 ;
}
2014-03-10 12:53:18 +04:00
static struct tevent_req * smbd_smb2_logoff_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct smbd_smb2_request * smb2req ) ;
static NTSTATUS smbd_smb2_logoff_recv ( struct tevent_req * req ) ;
static void smbd_smb2_request_logoff_done ( struct tevent_req * subreq ) ;
2009-05-15 13:40:19 +04:00
NTSTATUS smbd_smb2_request_process_logoff ( struct smbd_smb2_request * req )
{
2011-09-06 16:01:43 +04:00
NTSTATUS status ;
2014-03-10 12:53:18 +04:00
struct tevent_req * subreq = NULL ;
2009-05-15 13:40:19 +04:00
2011-09-06 16:01:43 +04:00
status = smbd_smb2_request_verify_sizes ( req , 0x04 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return smbd_smb2_request_error ( req , status ) ;
2009-05-15 13:40:19 +04:00
}
2014-03-10 12:53:18 +04:00
subreq = smbd_smb2_logoff_send ( req , req - > sconn - > ev_ctx , req ) ;
if ( subreq = = NULL ) {
return smbd_smb2_request_error ( req , NT_STATUS_NO_MEMORY ) ;
}
tevent_req_set_callback ( subreq , smbd_smb2_request_logoff_done , req ) ;
2009-05-15 13:40:19 +04:00
/*
2014-03-10 12:53:18 +04:00
* Wait a long time before going async on this to allow
* requests we ' re waiting on to finish . Set timeout to 10 secs .
2009-05-15 13:40:19 +04:00
*/
2014-03-10 12:53:18 +04:00
return smbd_smb2_request_pending_queue ( req , subreq , 10000000 ) ;
}
static void smbd_smb2_request_logoff_done ( struct tevent_req * subreq )
{
struct smbd_smb2_request * smb2req =
tevent_req_callback_data ( subreq ,
struct smbd_smb2_request ) ;
DATA_BLOB outbody ;
NTSTATUS status ;
NTSTATUS error ;
status = smbd_smb2_logoff_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
2012-03-27 13:09:05 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2014-03-10 12:53:18 +04:00
error = smbd_smb2_request_error ( smb2req , status ) ;
if ( ! NT_STATUS_IS_OK ( error ) ) {
smbd_server_connection_terminate ( smb2req - > sconn ,
nt_errstr ( error ) ) ;
return ;
}
return ;
}
outbody = smbd_smb2_generate_outbody ( smb2req , 0x04 ) ;
if ( outbody . data = = NULL ) {
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 ;
}
SSVAL ( outbody . data , 0x00 , 0x04 ) ; /* struct size */
SSVAL ( outbody . data , 0x02 , 0 ) ; /* reserved */
error = smbd_smb2_request_done ( smb2req , outbody , NULL ) ;
if ( ! NT_STATUS_IS_OK ( error ) ) {
smbd_server_connection_terminate ( smb2req - > sconn ,
nt_errstr ( error ) ) ;
return ;
}
}
struct smbd_smb2_logout_state {
struct smbd_smb2_request * smb2req ;
2014-03-10 12:53:18 +04:00
struct tevent_queue * wait_queue ;
2014-03-10 12:53:18 +04:00
} ;
2014-03-10 12:53:18 +04:00
static void smbd_smb2_logoff_wait_done ( struct tevent_req * subreq ) ;
2014-03-10 12:53:18 +04:00
static struct tevent_req * smbd_smb2_logoff_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct smbd_smb2_request * smb2req )
{
struct tevent_req * req ;
struct smbd_smb2_logout_state * state ;
2014-03-10 12:53:18 +04:00
struct tevent_req * subreq ;
struct smbd_smb2_request * preq ;
2014-06-10 15:24:50 +04:00
struct smbXsrv_connection * xconn = smb2req - > sconn - > conn ;
2014-03-10 12:53:18 +04:00
req = tevent_req_create ( mem_ctx , & state ,
struct smbd_smb2_logout_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > smb2req = smb2req ;
2014-03-10 12:53:18 +04:00
state - > wait_queue = tevent_queue_create ( state , " logoff_wait_queue " ) ;
if ( tevent_req_nomem ( state - > wait_queue , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2014-03-10 12:53:18 +04:00
/*
2014-03-10 12:53:18 +04:00
* Make sure that no new request will be able to use this session .
2014-03-10 12:53:18 +04:00
*/
2014-03-10 12:53:18 +04:00
smb2req - > session - > status = NT_STATUS_USER_SESSION_DELETED ;
2014-06-10 15:24:50 +04:00
for ( preq = xconn - > smb2 . requests ; preq ! = NULL ; preq = preq - > next ) {
2014-03-10 12:53:18 +04:00
if ( preq = = smb2req ) {
/* Can't cancel current request. */
continue ;
}
if ( preq - > session ! = smb2req - > session ) {
/* Request on different session. */
continue ;
}
/*
* Never cancel anything in a compound
* request . Way too hard to deal with
* the result .
*/
if ( ! preq - > compound_related & & preq - > subreq ! = NULL ) {
tevent_req_cancel ( preq - > subreq ) ;
}
/*
* Now wait until the request is finished .
*
* We don ' t set a callback , as we just want to block the
* wait queue and the talloc_free ( ) of the request will
* remove the item from the wait queue .
*/
subreq = tevent_queue_wait_send ( preq , ev , state - > wait_queue ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
}
/*
* Now we add our own waiter to the end of the queue ,
* this way we get notified when all pending requests are finished
* and send to the socket .
*/
subreq = tevent_queue_wait_send ( state , ev , state - > wait_queue ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , smbd_smb2_logoff_wait_done , req ) ;
return req ;
}
static void smbd_smb2_logoff_wait_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct smbd_smb2_logout_state * state = tevent_req_data (
req , struct smbd_smb2_logout_state ) ;
NTSTATUS status ;
tevent_queue_wait_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
/*
* As we ' ve been awoken , we may have changed
* uid in the meantime . Ensure we ' re still
* root ( SMB2_OP_LOGOFF has . as_root = true ) .
*/
change_to_root_user ( ) ;
2014-03-10 12:53:18 +04:00
status = smbXsrv_session_logoff ( state - > smb2req - > session ) ;
if ( tevent_req_nterror ( req , status ) ) {
2014-03-10 12:53:18 +04:00
return ;
2012-03-27 13:09:05 +04:00
}
2009-05-15 13:40:19 +04:00
/*
* we may need to sign the response , so we need to keep
* the session until the response is sent to the wire .
*/
2014-03-10 12:53:18 +04:00
talloc_steal ( state - > smb2req , state - > smb2req - > session ) ;
2009-05-15 13:40:19 +04:00
2014-03-10 12:53:18 +04:00
tevent_req_done ( req ) ;
}
2009-05-15 13:40:19 +04:00
2014-03-10 12:53:18 +04:00
static NTSTATUS smbd_smb2_logoff_recv ( struct tevent_req * req )
{
return tevent_req_simple_recv_ntstatus ( req ) ;
2009-05-15 13:40:19 +04:00
}