2005-11-18 17:13:49 +03:00
/*
Unix SMB2 implementation .
Copyright ( C ) Andrew Bartlett 2001 - 2005
Copyright ( C ) Stefan Metzmacher 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-18 17:13:49 +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-18 17:13:49 +03:00
*/
# include "includes.h"
2009-12-22 18:24:44 +03:00
# include <tevent.h>
2006-11-07 03:48:36 +03:00
# include "auth/gensec/gensec.h"
2005-11-18 17:13:49 +03:00
# include "auth/auth.h"
# include "libcli/smb2/smb2.h"
# include "libcli/smb2/smb2_calls.h"
# include "smb_server/smb_server.h"
# include "smb_server/smb2/smb2_server.h"
2020-11-20 17:27:17 +03:00
# include "samba/service_stream.h"
2010-09-12 16:24:46 +04:00
# include "lib/stream/packet.h"
2005-11-18 17:13:49 +03:00
2006-07-27 14:03:54 +04:00
static void smb2srv_sesssetup_send ( struct smb2srv_request * req , union smb_sesssetup * io )
{
if ( NT_STATUS_IS_OK ( req - > status ) ) {
2011-10-31 16:43:01 +04:00
/* nothing */
2006-07-27 14:03:54 +04:00
} else if ( NT_STATUS_EQUAL ( req - > status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
2011-10-31 16:43:01 +04:00
/* nothing */
2006-07-27 14:03:54 +04:00
} else {
smb2srv_send_error ( req , req - > status ) ;
return ;
}
2007-10-07 02:10:49 +04:00
SMB2SRV_CHECK ( smb2srv_setup_reply ( req , 0x08 , true , io - > smb2 . out . secblob . length ) ) ;
2006-07-27 14:03:54 +04:00
2008-02-12 09:00:35 +03:00
SBVAL ( req - > out . hdr , SMB2_HDR_SESSION_ID , io - > smb2 . out . uid ) ;
2006-07-27 14:03:54 +04:00
2008-02-12 08:43:38 +03:00
SSVAL ( req - > out . body , 0x02 , io - > smb2 . out . session_flags ) ;
2006-07-27 14:03:54 +04:00
SMB2SRV_CHECK ( smb2_push_o16s16_blob ( & req - > out , 0x04 , io - > smb2 . out . secblob ) ) ;
smb2srv_send_reply ( req ) ;
}
struct smb2srv_sesssetup_callback_ctx {
struct smb2srv_request * req ;
union smb_sesssetup * io ;
struct smbsrv_session * smb_sess ;
} ;
2009-12-22 18:24:44 +03:00
static void smb2srv_sesssetup_callback ( struct tevent_req * subreq )
2006-07-27 14:03:54 +04:00
{
2009-12-22 18:24:44 +03:00
struct smb2srv_sesssetup_callback_ctx * ctx = tevent_req_callback_data ( subreq ,
2006-07-27 14:03:54 +04:00
struct smb2srv_sesssetup_callback_ctx ) ;
struct smb2srv_request * req = ctx - > req ;
union smb_sesssetup * io = ctx - > io ;
2006-07-29 03:35:11 +04:00
struct smbsrv_session * smb_sess = ctx - > smb_sess ;
2006-07-27 14:03:54 +04:00
struct auth_session_info * session_info = NULL ;
2011-09-29 12:09:41 +04:00
enum security_user_level user_level ;
2006-07-27 14:03:54 +04:00
NTSTATUS status ;
2010-09-12 16:24:46 +04:00
packet_recv_enable ( req - > smb_conn - > packet ) ;
2009-12-22 18:24:44 +03:00
status = gensec_update_recv ( subreq , req , & io - > smb2 . out . secblob ) ;
TALLOC_FREE ( subreq ) ;
2006-07-27 14:03:54 +04:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
goto done ;
} else if ( ! NT_STATUS_IS_OK ( status ) ) {
goto failed ;
}
2011-08-01 09:39:01 +04:00
status = gensec_session_info ( smb_sess - > gensec_ctx , smb_sess , & session_info ) ;
2006-07-27 14:03:54 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto failed ;
}
/* Ensure this is marked as a 'real' vuid, not one
* simply valid for the session setup leg */
status = smbsrv_session_sesssetup_finished ( smb_sess , session_info ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto failed ;
}
req - > session = smb_sess ;
2011-09-29 12:09:41 +04:00
user_level = security_session_user_level ( smb_sess - > session_info , NULL ) ;
if ( user_level > = SECURITY_USER ) {
if ( smb_sess - > smb2_signing . required ) {
/* activate smb2 signing on the session */
smb_sess - > smb2_signing . active = true ;
}
/* we need to sign the session setup response */
req - > is_signed = true ;
2008-06-09 23:45:19 +04:00
}
2011-09-29 12:09:41 +04:00
2006-07-27 14:03:54 +04:00
done :
io - > smb2 . out . uid = smb_sess - > vuid ;
failed :
2011-03-03 03:05:33 +03:00
req - > status = nt_status_squash ( status ) ;
2006-07-27 14:03:54 +04:00
smb2srv_sesssetup_send ( req , io ) ;
2007-05-20 13:44:03 +04:00
if ( ! NT_STATUS_IS_OK ( status ) & & !
NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
talloc_free ( smb_sess ) ;
}
2006-07-27 14:03:54 +04:00
}
static void smb2srv_sesssetup_backend ( struct smb2srv_request * req , union smb_sesssetup * io )
2005-11-18 17:13:49 +03:00
{
2005-12-07 10:14:13 +03:00
NTSTATUS status ;
2006-07-27 14:03:54 +04:00
struct smb2srv_sesssetup_callback_ctx * callback_ctx ;
2005-11-18 17:13:49 +03:00
struct smbsrv_session * smb_sess = NULL ;
uint64_t vuid ;
2009-12-22 18:24:44 +03:00
struct tevent_req * subreq ;
2005-11-18 17:13:49 +03:00
2008-02-12 08:43:38 +03:00
io - > smb2 . out . session_flags = 0 ;
2006-05-20 14:59:26 +04:00
io - > smb2 . out . uid = 0 ;
io - > smb2 . out . secblob = data_blob ( NULL , 0 ) ;
2005-11-18 17:13:49 +03:00
2008-02-12 09:00:35 +03:00
vuid = BVAL ( req - > in . hdr , SMB2_HDR_SESSION_ID ) ;
2005-11-18 17:13:49 +03:00
2005-12-07 10:14:13 +03:00
/*
* only when we got ' 0 ' we should allocate a new session
2005-11-18 17:13:49 +03:00
*/
if ( vuid = = 0 ) {
struct gensec_security * gensec_ctx ;
2017-02-23 04:31:52 +03:00
struct tsocket_address * remote_address , * local_address ;
2005-11-18 17:13:49 +03:00
2009-02-13 02:24:16 +03:00
status = samba_server_gensec_start ( req ,
req - > smb_conn - > connection - > event . ctx ,
req - > smb_conn - > connection - > msg_ctx ,
req - > smb_conn - > lp_ctx ,
req - > smb_conn - > negotiate . server_credentials ,
" cifs " ,
& gensec_ctx ) ;
2005-11-18 17:13:49 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " Failed to start GENSEC server code: %s \n " , nt_errstr ( status ) ) ) ;
2006-07-27 14:03:54 +04:00
goto failed ;
2005-11-18 17:13:49 +03:00
}
gensec_want_feature ( gensec_ctx , GENSEC_FEATURE_SESSION_KEY ) ;
2017-03-06 04:10:17 +03:00
gensec_want_feature ( gensec_ctx , GENSEC_FEATURE_SMB_TRANSPORT ) ;
2005-11-18 17:13:49 +03:00
2017-02-23 04:31:52 +03:00
remote_address = socket_get_remote_addr ( req - > smb_conn - > connection - > socket ,
req ) ;
if ( ! remote_address ) {
status = NT_STATUS_INTERNAL_ERROR ;
2023-08-07 07:54:43 +03:00
DBG_ERR ( " Failed to obtain remote address \n " ) ;
2017-02-23 04:31:52 +03:00
goto failed ;
}
status = gensec_set_remote_address ( gensec_ctx ,
remote_address ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2023-08-07 07:54:43 +03:00
DBG_ERR ( " Failed to set remote address \n " ) ;
2017-02-23 04:31:52 +03:00
goto failed ;
}
local_address = socket_get_local_addr ( req - > smb_conn - > connection - > socket ,
req ) ;
if ( ! local_address ) {
status = NT_STATUS_INTERNAL_ERROR ;
2023-08-07 07:54:43 +03:00
DBG_ERR ( " Failed to obtain local address \n " ) ;
2017-02-23 04:31:52 +03:00
goto failed ;
}
status = gensec_set_local_address ( gensec_ctx ,
local_address ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2023-08-07 07:54:43 +03:00
DBG_ERR ( " Failed to set local address \n " ) ;
2017-02-23 04:31:52 +03:00
goto failed ;
}
status = gensec_set_target_service_description ( gensec_ctx ,
" SMB2 " ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2023-08-07 07:54:43 +03:00
DBG_ERR ( " Failed to set service description \n " ) ;
2017-02-23 04:31:52 +03:00
goto failed ;
}
2005-11-18 17:13:49 +03:00
status = gensec_start_mech_by_oid ( gensec_ctx , GENSEC_OID_SPNEGO ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " Failed to start GENSEC SPNEGO server code: %s \n " , nt_errstr ( status ) ) ) ;
2006-07-27 14:03:54 +04:00
goto failed ;
2005-11-18 17:13:49 +03:00
}
/* allocate a new session */
2007-05-20 13:44:03 +04:00
smb_sess = smbsrv_session_new ( req - > smb_conn , req - > smb_conn , gensec_ctx ) ;
2006-07-27 14:03:54 +04:00
if ( ! smb_sess ) {
status = NT_STATUS_INSUFFICIENT_RESOURCES ;
goto failed ;
}
2005-12-06 20:59:20 +03:00
status = smbsrv_smb2_init_tcons ( smb_sess ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-12-07 10:14:13 +03:00
goto failed ;
2005-12-06 20:59:20 +03:00
}
2005-11-18 17:13:49 +03:00
} else {
/* lookup an existing session */
smb_sess = smbsrv_session_find_sesssetup ( req - > smb_conn , vuid ) ;
}
if ( ! smb_sess ) {
2012-05-06 23:09:47 +04:00
status = NT_STATUS_USER_SESSION_DELETED ;
goto failed ;
}
if ( smb_sess - > session_info ) {
2008-05-29 13:32:04 +04:00
/* see WSPP test suite - test 11 */
status = NT_STATUS_REQUEST_NOT_ACCEPTED ;
2006-07-27 14:03:54 +04:00
goto failed ;
2005-11-18 17:13:49 +03:00
}
if ( ! smb_sess - > gensec_ctx ) {
status = NT_STATUS_INTERNAL_ERROR ;
DEBUG ( 1 , ( " Internal ERROR: no gensec_ctx on session: %s \n " , nt_errstr ( status ) ) ) ;
goto failed ;
}
2006-07-27 14:03:54 +04:00
callback_ctx = talloc ( req , struct smb2srv_sesssetup_callback_ctx ) ;
if ( ! callback_ctx ) goto nomem ;
callback_ctx - > req = req ;
callback_ctx - > io = io ;
callback_ctx - > smb_sess = smb_sess ;
2009-12-22 18:24:44 +03:00
subreq = gensec_update_send ( callback_ctx ,
req - > smb_conn - > connection - > event . ctx ,
smb_sess - > gensec_ctx ,
io - > smb2 . in . secblob ) ;
if ( ! subreq ) goto nomem ;
tevent_req_set_callback ( subreq , smb2srv_sesssetup_callback , callback_ctx ) ;
2008-06-07 09:10:30 +04:00
/* note that we ignore SMB2_NEGOTIATE_SIGNING_ENABLED from the client.
This is deliberate as windows does not set it even when it does
set SMB2_NEGOTIATE_SIGNING_REQUIRED */
2008-06-07 19:14:25 +04:00
if ( io - > smb2 . in . security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED ) {
2008-06-09 23:45:19 +04:00
smb_sess - > smb2_signing . required = true ;
2008-06-07 09:10:30 +04:00
}
2010-09-12 16:24:46 +04:00
/* disable receipt of more packets on this socket until we've
finished with the session setup . This avoids a problem with
crashes if we get EOF on the socket while processing a session
setup */
packet_recv_disable ( req - > smb_conn - > packet ) ;
2006-07-27 14:03:54 +04:00
return ;
nomem :
status = NT_STATUS_NO_MEMORY ;
2005-11-18 17:13:49 +03:00
failed :
talloc_free ( smb_sess ) ;
2011-03-03 03:05:33 +03:00
req - > status = nt_status_squash ( status ) ;
2006-07-27 14:03:54 +04:00
smb2srv_sesssetup_send ( req , io ) ;
2005-11-18 17:13:49 +03:00
}
void smb2srv_sesssetup_recv ( struct smb2srv_request * req )
{
2006-05-20 14:59:26 +04:00
union smb_sesssetup * io ;
2005-11-18 17:13:49 +03:00
2007-10-07 02:10:49 +04:00
SMB2SRV_CHECK_BODY_SIZE ( req , 0x18 , true ) ;
2006-06-30 15:07:47 +04:00
SMB2SRV_TALLOC_IO_PTR ( io , union smb_sesssetup ) ;
2005-11-18 17:13:49 +03:00
2008-02-12 08:43:38 +03:00
io - > smb2 . level = RAW_SESSSETUP_SMB2 ;
io - > smb2 . in . vc_number = CVAL ( req - > in . body , 0x02 ) ;
io - > smb2 . in . security_mode = CVAL ( req - > in . body , 0x03 ) ;
io - > smb2 . in . capabilities = IVAL ( req - > in . body , 0x04 ) ;
io - > smb2 . in . channel = IVAL ( req - > in . body , 0x08 ) ;
io - > smb2 . in . previous_sessionid = BVAL ( req - > in . body , 0x10 ) ;
2006-06-30 15:07:47 +04:00
SMB2SRV_CHECK ( smb2_pull_o16s16_blob ( & req - > in , io , req - > in . body + 0x0C , & io - > smb2 . in . secblob ) ) ;
2005-11-18 17:13:49 +03:00
2006-07-27 14:03:54 +04:00
smb2srv_sesssetup_backend ( req , io ) ;
2005-11-18 17:13:49 +03:00
}
2005-12-06 16:26:24 +03:00
2008-06-09 23:57:05 +04:00
static int smb2srv_cleanup_session_destructor ( struct smbsrv_session * * session )
2005-12-06 16:26:24 +03:00
{
/* TODO: call ntvfs backends to close file of this session */
2008-06-09 23:57:05 +04:00
DEBUG ( 0 , ( " free session[%p] \n " , * session ) ) ;
talloc_free ( * session ) ;
return 0 ;
}
static NTSTATUS smb2srv_logoff_backend ( struct smb2srv_request * req )
{
struct smbsrv_session * * session_ptr ;
/* we need to destroy the session after sending the reply */
session_ptr = talloc ( req , struct smbsrv_session * ) ;
NT_STATUS_HAVE_NO_MEMORY ( session_ptr ) ;
* session_ptr = req - > session ;
talloc_set_destructor ( session_ptr , smb2srv_cleanup_session_destructor ) ;
2005-12-06 16:26:24 +03:00
return NT_STATUS_OK ;
}
static void smb2srv_logoff_send ( struct smb2srv_request * req )
{
if ( NT_STATUS_IS_ERR ( req - > status ) ) {
smb2srv_send_error ( req , req - > status ) ;
return ;
}
2007-10-07 02:10:49 +04:00
SMB2SRV_CHECK ( smb2srv_setup_reply ( req , 0x04 , false , 0 ) ) ;
2005-12-06 16:26:24 +03:00
SSVAL ( req - > out . body , 0x02 , 0 ) ;
smb2srv_send_reply ( req ) ;
}
void smb2srv_logoff_recv ( struct smb2srv_request * req )
{
2007-10-07 02:10:49 +04:00
SMB2SRV_CHECK_BODY_SIZE ( req , 0x04 , false ) ;
2005-12-06 16:26:24 +03:00
req - > status = smb2srv_logoff_backend ( req ) ;
if ( req - > control_flags & SMB2SRV_REQ_CTRL_FLAG_NOT_REPLY ) {
talloc_free ( req ) ;
return ;
}
smb2srv_logoff_send ( req ) ;
}