2005-11-11 07:45:38 +03:00
/*
Unix SMB / CIFS implementation .
SMB2 client transport context management functions
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 07:45:38 +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 07:45:38 +03:00
*/
# include "includes.h"
2011-09-20 22:59:45 +04:00
# include "system/network.h"
2005-11-11 07:45:38 +03:00
# include "libcli/raw/libcliraw.h"
2008-04-02 06:53:27 +04:00
# include "libcli/raw/raw_proto.h"
2005-11-11 07:45:38 +03:00
# include "libcli/smb2/smb2.h"
2006-01-03 20:27:33 +03:00
# include "libcli/smb2/smb2_calls.h"
2005-11-11 07:45:38 +03:00
# include "lib/socket/socket.h"
# include "lib/events/events.h"
2008-10-11 23:31:42 +04:00
# include "../lib/util/dlinklist.h"
2011-09-20 22:59:45 +04:00
# include "../libcli/smb/smbXcli_base.h"
2012-02-22 18:29:26 +04:00
# include "librpc/ndr/libndr.h"
2005-11-11 07:45:38 +03:00
/*
destroy a transport
*/
2006-05-24 11:34:11 +04:00
static int transport_destructor ( struct smb2_transport * transport )
2005-11-11 07:45:38 +03:00
{
2006-07-13 21:37:45 +04:00
smb2_transport_dead ( transport , NT_STATUS_LOCAL_DISCONNECT ) ;
2005-11-11 07:45:38 +03:00
return 0 ;
}
/*
create a transport structure based on an established socket
*/
struct smb2_transport * smb2_transport_init ( struct smbcli_socket * sock ,
2008-05-30 11:03:54 +04:00
TALLOC_CTX * parent_ctx ,
struct smbcli_options * options )
2005-11-11 07:45:38 +03:00
{
struct smb2_transport * transport ;
transport = talloc_zero ( parent_ctx , struct smb2_transport ) ;
if ( ! transport ) return NULL ;
2011-09-20 22:59:45 +04:00
transport - > ev = sock - > event . ctx ;
2008-05-30 11:03:54 +04:00
transport - > options = * options ;
2005-11-11 07:45:38 +03:00
2014-11-20 16:35:38 +03:00
if ( transport - > options . max_protocol = = PROTOCOL_DEFAULT ) {
transport - > options . max_protocol = PROTOCOL_LATEST ;
}
if ( transport - > options . max_protocol < PROTOCOL_SMB2_02 ) {
transport - > options . max_protocol = PROTOCOL_LATEST ;
}
2011-09-20 22:59:45 +04:00
TALLOC_FREE ( sock - > event . fde ) ;
TALLOC_FREE ( sock - > event . te ) ;
transport - > conn = smbXcli_conn_create ( transport ,
sock - > sock - > fd ,
sock - > hostname ,
options - > signing ,
0 , /* smb1_capabilities */
2013-09-25 08:46:47 +04:00
& options - > client_guid ,
2013-09-25 08:57:23 +04:00
options - > smb2_capabilities ) ;
2011-09-20 22:59:45 +04:00
if ( transport - > conn = = NULL ) {
2005-11-11 07:45:38 +03:00
talloc_free ( transport ) ;
return NULL ;
}
2011-09-20 22:59:45 +04:00
sock - > sock - > fd = - 1 ;
TALLOC_FREE ( sock ) ;
2005-11-11 07:45:38 +03:00
talloc_set_destructor ( transport , transport_destructor ) ;
return transport ;
}
/*
mark the transport as dead
*/
2006-07-13 21:37:45 +04:00
void smb2_transport_dead ( struct smb2_transport * transport , NTSTATUS status )
2005-11-11 07:45:38 +03:00
{
2006-07-13 21:37:45 +04:00
if ( NT_STATUS_EQUAL ( NT_STATUS_UNSUCCESSFUL , status ) ) {
status = NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
}
2011-09-20 22:59:45 +04:00
if ( NT_STATUS_IS_OK ( status ) ) {
status = NT_STATUS_LOCAL_DISCONNECT ;
2005-11-11 07:45:38 +03:00
}
2011-09-20 22:59:45 +04:00
smbXcli_conn_disconnect ( transport - > conn , status ) ;
}
2008-04-19 00:27:24 +04:00
2011-09-20 22:59:45 +04:00
static void smb2_request_done ( struct tevent_req * subreq ) ;
static void smb2_transport_break_handler ( struct tevent_req * subreq ) ;
2008-04-19 00:27:24 +04:00
2011-09-20 22:59:45 +04:00
/*
put a request into the send queue
*/
void smb2_transport_send ( struct smb2_request * req )
{
NTSTATUS status ;
struct smb2_transport * transport = req - > transport ;
struct tevent_req * * reqs = transport - > compound . reqs ;
size_t num_reqs = talloc_array_length ( reqs ) ;
size_t i ;
uint16_t cmd = SVAL ( req - > out . hdr , SMB2_HDR_OPCODE ) ;
uint32_t additional_flags = IVAL ( req - > out . hdr , SMB2_HDR_FLAGS ) ;
uint32_t clear_flags = 0 ;
2012-07-25 12:36:27 +04:00
struct smbXcli_tcon * tcon = NULL ;
2011-09-20 22:59:45 +04:00
struct smbXcli_session * session = NULL ;
bool need_pending_break = false ;
size_t hdr_ofs ;
size_t pdu_len ;
DATA_BLOB body = data_blob_null ;
DATA_BLOB dyn = data_blob_null ;
uint32_t timeout_msec = transport - > options . request_timeout * 1000 ;
if ( transport - > oplock . handler ) {
need_pending_break = true ;
}
if ( transport - > lease . handler ) {
need_pending_break = true ;
}
if ( transport - > break_subreq ) {
need_pending_break = false ;
}
if ( need_pending_break ) {
struct tevent_req * subreq ;
subreq = smb2cli_req_create ( transport ,
transport - > ev ,
transport - > conn ,
SMB2_OP_BREAK ,
0 , /* additional_flags */
0 , /*clear_flags */
0 , /* timeout_msec */
2012-07-25 12:36:27 +04:00
NULL , /* tcon */
2011-09-20 22:59:45 +04:00
NULL , /* session */
NULL , /* body */
0 , /* body_fixed */
NULL , /* dyn */
2013-08-13 12:25:52 +04:00
0 , /* dyn_len */
0 ) ; /* max_dyn_len */
2011-09-20 22:59:45 +04:00
if ( subreq ! = NULL ) {
smbXcli_req_set_pending ( subreq ) ;
tevent_req_set_callback ( subreq ,
smb2_transport_break_handler ,
transport ) ;
transport - > break_subreq = subreq ;
}
2008-04-19 00:27:24 +04:00
}
2011-09-20 22:59:45 +04:00
if ( req - > session ) {
session = req - > session - > smbXcli ;
2009-03-31 02:57:57 +04:00
}
2008-04-19 00:27:24 +04:00
2012-07-25 12:36:27 +04:00
if ( req - > tree ) {
tcon = req - > tree - > smbXcli ;
}
2011-09-20 22:59:45 +04:00
if ( transport - > compound . related ) {
additional_flags | = SMB2_HDR_FLAG_CHAINED ;
2008-04-19 00:27:24 +04:00
}
2011-09-20 22:59:45 +04:00
hdr_ofs = PTR_DIFF ( req - > out . hdr , req - > out . buffer ) ;
pdu_len = req - > out . size - hdr_ofs ;
body . data = req - > out . body ;
body . length = req - > out . body_fixed ;
dyn . data = req - > out . body + req - > out . body_fixed ;
dyn . length = pdu_len - ( SMB2_HDR_BODY + req - > out . body_fixed ) ;
2009-03-31 02:57:57 +04:00
2011-09-20 22:59:45 +04:00
req - > subreq = smb2cli_req_create ( req ,
transport - > ev ,
transport - > conn ,
cmd ,
additional_flags ,
clear_flags ,
timeout_msec ,
2012-07-25 12:36:27 +04:00
tcon ,
2011-09-20 22:59:45 +04:00
session ,
body . data , body . length ,
2013-08-13 12:25:52 +04:00
dyn . data , dyn . length ,
0 ) ; /* max_dyn_len */
2011-09-20 22:59:45 +04:00
if ( req - > subreq = = NULL ) {
req - > state = SMB2_REQUEST_ERROR ;
req - > status = NT_STATUS_NO_MEMORY ;
return ;
2008-04-19 00:27:24 +04:00
}
2011-09-20 22:59:45 +04:00
if ( ! tevent_req_is_in_progress ( req - > subreq ) ) {
req - > state = SMB2_REQUEST_ERROR ;
req - > status = NT_STATUS_INTERNAL_ERROR ; /* TODO */
return ;
2009-06-08 18:24:27 +04:00
}
2011-09-20 22:59:45 +04:00
tevent_req_set_callback ( req - > subreq , smb2_request_done , req ) ;
2005-11-11 07:45:38 +03:00
2011-09-20 22:59:45 +04:00
smb2cli_req_set_notify_async ( req - > subreq ) ;
if ( req - > credit_charge ) {
smb2cli_req_set_credit_charge ( req - > subreq , req - > credit_charge ) ;
2005-11-11 07:45:38 +03:00
}
2011-09-20 22:59:45 +04:00
ZERO_STRUCT ( req - > out ) ;
req - > state = SMB2_REQUEST_RECV ;
2005-11-11 07:45:38 +03:00
2011-09-20 22:59:45 +04:00
if ( num_reqs > 0 ) {
for ( i = 0 ; i < num_reqs ; i + + ) {
if ( reqs [ i ] ! = NULL ) {
continue ;
}
2009-03-31 02:57:57 +04:00
2011-09-20 22:59:45 +04:00
reqs [ i ] = req - > subreq ;
i + + ;
break ;
}
2008-05-30 11:03:54 +04:00
2011-09-20 22:59:45 +04:00
if ( i < num_reqs ) {
return ;
}
} else {
reqs = & req - > subreq ;
num_reqs = 1 ;
2011-09-28 10:23:24 +04:00
}
2011-09-20 22:59:45 +04:00
status = smb2cli_req_compound_submit ( reqs , num_reqs ) ;
2011-09-28 10:23:24 +04:00
2011-09-20 22:59:45 +04:00
TALLOC_FREE ( transport - > compound . reqs ) ;
2012-08-17 10:25:08 +04:00
transport - > compound . related = false ;
2005-11-11 07:45:38 +03:00
2011-09-20 22:59:45 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2011-12-09 14:05:47 +04:00
req - > status = status ;
req - > state = SMB2_REQUEST_ERROR ;
2011-09-20 22:59:45 +04:00
smbXcli_conn_disconnect ( transport - > conn , status ) ;
2005-11-11 07:45:38 +03:00
}
2011-09-20 22:59:45 +04:00
}
2005-11-11 07:45:38 +03:00
2011-09-20 22:59:45 +04:00
static void smb2_request_done ( struct tevent_req * subreq )
{
struct smb2_request * req =
tevent_req_callback_data ( subreq ,
struct smb2_request ) ;
ssize_t len ;
size_t i ;
2005-11-11 07:45:38 +03:00
2011-09-20 22:59:45 +04:00
req - > recv_iov = NULL ;
2005-11-11 07:45:38 +03:00
2011-09-20 22:59:45 +04:00
req - > status = smb2cli_req_recv ( req - > subreq , req , & req - > recv_iov , NULL , 0 ) ;
if ( NT_STATUS_EQUAL ( req - > status , STATUS_PENDING ) ) {
2008-08-13 11:42:27 +04:00
req - > cancel . can_cancel = true ;
2011-09-20 22:59:45 +04:00
return ;
2008-06-07 09:10:30 +04:00
}
2011-09-20 22:59:45 +04:00
TALLOC_FREE ( req - > subreq ) ;
if ( ! NT_STATUS_IS_OK ( req - > status ) ) {
if ( req - > recv_iov = = NULL ) {
req - > state = SMB2_REQUEST_ERROR ;
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
return ;
2005-11-17 03:48:24 +03:00
}
}
2011-09-20 22:59:45 +04:00
len = req - > recv_iov [ 0 ] . iov_len ;
for ( i = 1 ; i < 3 ; i + + ) {
uint8_t * p = req - > recv_iov [ i - 1 ] . iov_base ;
uint8_t * c1 = req - > recv_iov [ i ] . iov_base ;
uint8_t * c2 = p + req - > recv_iov [ i - 1 ] . iov_len ;
2005-11-11 08:53:54 +03:00
2011-09-20 22:59:45 +04:00
len + = req - > recv_iov [ i ] . iov_len ;
2009-06-08 18:24:27 +04:00
2011-09-20 22:59:45 +04:00
if ( req - > recv_iov [ i ] . iov_len = = 0 ) {
continue ;
2009-06-08 18:24:27 +04:00
}
2011-09-20 22:59:45 +04:00
if ( c1 ! = c2 ) {
req - > status = NT_STATUS_INTERNAL_ERROR ;
req - > state = SMB2_REQUEST_ERROR ;
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
return ;
2009-06-08 18:24:27 +04:00
}
}
2011-09-20 22:59:45 +04:00
req - > in . buffer = req - > recv_iov [ 0 ] . iov_base ;
req - > in . size = len ;
req - > in . allocated = req - > in . size ;
2005-11-11 07:45:38 +03:00
2011-09-20 22:59:45 +04:00
req - > in . hdr = req - > recv_iov [ 0 ] . iov_base ;
req - > in . body = req - > recv_iov [ 1 ] . iov_base ;
req - > in . dynamic = req - > recv_iov [ 2 ] . iov_base ;
req - > in . body_fixed = req - > recv_iov [ 1 ] . iov_len ;
req - > in . body_size = req - > in . body_fixed ;
req - > in . body_size + = req - > recv_iov [ 2 ] . iov_len ;
2005-11-11 07:45:38 +03:00
2011-09-20 22:59:45 +04:00
smb2_setup_bufinfo ( req ) ;
2005-11-11 07:45:38 +03:00
2011-09-20 22:59:45 +04:00
req - > state = SMB2_REQUEST_DONE ;
2005-11-11 07:45:38 +03:00
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
}
2011-09-20 22:59:45 +04:00
static void smb2_transport_break_handler ( struct tevent_req * subreq )
2009-06-08 18:24:27 +04:00
{
2011-09-20 22:59:45 +04:00
struct smb2_transport * transport =
tevent_req_callback_data ( subreq ,
struct smb2_transport ) ;
2009-06-08 18:24:27 +04:00
NTSTATUS status ;
2011-09-20 22:59:45 +04:00
uint8_t * body ;
uint16_t len = 0 ;
bool lease ;
struct iovec * recv_iov = NULL ;
2009-06-08 18:24:27 +04:00
2011-09-20 22:59:45 +04:00
transport - > break_subreq = NULL ;
2009-06-08 18:24:27 +04:00
2011-09-20 22:59:45 +04:00
status = smb2cli_req_recv ( subreq , transport , & recv_iov , NULL , 0 ) ;
TALLOC_FREE ( subreq ) ;
2009-06-08 18:24:27 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2011-09-20 22:59:45 +04:00
TALLOC_FREE ( recv_iov ) ;
smb2_transport_dead ( transport , status ) ;
return ;
2009-06-08 18:24:27 +04:00
}
2011-09-20 22:59:45 +04:00
/*
* Setup the subreq to handle the
* next incoming SMB2 Break .
*/
subreq = smb2cli_req_create ( transport ,
transport - > ev ,
transport - > conn ,
SMB2_OP_BREAK ,
0 , /* additional_flags */
0 , /*clear_flags */
0 , /* timeout_msec */
2012-07-25 12:36:27 +04:00
NULL , /* tcon */
2011-09-20 22:59:45 +04:00
NULL , /* session */
NULL , /* body */
0 , /* body_fixed */
NULL , /* dyn */
2013-08-13 12:25:52 +04:00
0 , /* dyn_len */
0 ) ; /* max_dyn_len */
2011-09-20 22:59:45 +04:00
if ( subreq ! = NULL ) {
smbXcli_req_set_pending ( subreq ) ;
tevent_req_set_callback ( subreq ,
smb2_transport_break_handler ,
transport ) ;
transport - > break_subreq = subreq ;
}
body = recv_iov [ 1 ] . iov_base ;
len = recv_iov [ 1 ] . iov_len ;
if ( recv_iov [ 1 ] . iov_len > = 2 ) {
len = CVAL ( body , 0x00 ) ;
if ( len ! = recv_iov [ 1 ] . iov_len ) {
len = recv_iov [ 1 ] . iov_len ;
}
}
2005-11-11 10:23:45 +03:00
2011-09-20 22:59:45 +04:00
if ( len = = 24 ) {
lease = false ;
} else if ( len = = 44 ) {
lease = true ;
} else {
DEBUG ( 1 , ( " Discarding smb2 oplock reply of invalid size %u \n " ,
( unsigned ) len ) ) ;
TALLOC_FREE ( recv_iov ) ;
status = NT_STATUS_INVALID_NETWORK_RESPONSE ;
smb2_transport_dead ( transport , status ) ;
return ;
}
2009-06-08 18:24:27 +04:00
2011-09-20 22:59:45 +04:00
if ( ! lease & & transport - > oplock . handler ) {
struct smb2_handle h ;
uint8_t level ;
2009-06-08 18:24:27 +04:00
2011-09-20 22:59:45 +04:00
level = CVAL ( body , 0x02 ) ;
smb2_pull_handle ( body + 0x08 , & h ) ;
2009-06-08 18:24:27 +04:00
2011-09-20 22:59:45 +04:00
TALLOC_FREE ( recv_iov ) ;
2009-06-08 18:24:27 +04:00
2011-09-20 22:59:45 +04:00
transport - > oplock . handler ( transport , & h , level ,
transport - > oplock . private_data ) ;
} else if ( lease & & transport - > lease . handler ) {
struct smb2_lease_break lb ;
2009-06-08 18:24:27 +04:00
2011-09-20 22:59:45 +04:00
ZERO_STRUCT ( lb ) ;
2014-11-11 21:33:13 +03:00
lb . new_epoch = SVAL ( body , 0x2 ) ;
2011-09-20 22:59:45 +04:00
lb . break_flags = SVAL ( body , 0x4 ) ;
memcpy ( & lb . current_lease . lease_key , body + 0x8 ,
sizeof ( struct smb2_lease_key ) ) ;
lb . current_lease . lease_state = SVAL ( body , 0x18 ) ;
lb . new_lease_state = SVAL ( body , 0x1C ) ;
lb . break_reason = SVAL ( body , 0x20 ) ;
lb . access_mask_hint = SVAL ( body , 0x24 ) ;
lb . share_mask_hint = SVAL ( body , 0x28 ) ;
2005-11-11 07:45:38 +03:00
2011-09-20 22:59:45 +04:00
TALLOC_FREE ( recv_iov ) ;
2009-06-08 18:24:27 +04:00
2011-09-20 22:59:45 +04:00
transport - > lease . handler ( transport , & lb ,
transport - > lease . private_data ) ;
2009-06-08 18:24:27 +04:00
} else {
2011-09-20 22:59:45 +04:00
DEBUG ( 5 , ( " Got SMB2 %s break with no handler \n " ,
lease ? " lease " : " oplock " ) ) ;
2005-11-11 07:45:38 +03:00
}
2011-09-20 22:59:45 +04:00
TALLOC_FREE ( recv_iov ) ;
2005-11-11 07:45:38 +03:00
}
2006-07-17 11:45:23 +04:00
2009-06-08 18:24:27 +04:00
NTSTATUS smb2_transport_compound_start ( struct smb2_transport * transport ,
uint32_t num )
{
2011-09-20 22:59:45 +04:00
TALLOC_FREE ( transport - > compound . reqs ) ;
2009-06-08 18:24:27 +04:00
ZERO_STRUCT ( transport - > compound ) ;
2011-09-20 22:59:45 +04:00
transport - > compound . reqs = talloc_zero_array ( transport ,
struct tevent_req * ,
num ) ;
if ( transport - > compound . reqs = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2009-06-08 18:24:27 +04:00
return NT_STATUS_OK ;
}
void smb2_transport_compound_set_related ( struct smb2_transport * transport ,
bool related )
{
transport - > compound . related = related ;
}
2009-06-08 19:59:26 +04:00
void smb2_transport_credits_ask_num ( struct smb2_transport * transport ,
uint16_t ask_num )
{
2011-09-20 22:59:45 +04:00
smb2cli_conn_set_max_credits ( transport - > conn , ask_num ) ;
2009-07-13 14:25:40 +04:00
}
2008-12-29 22:24:57 +03:00
static void idle_handler ( struct tevent_context * ev ,
2009-02-02 10:26:08 +03:00
struct tevent_timer * te , struct timeval t , void * private_data )
2006-07-17 11:45:23 +04:00
{
2009-02-02 10:26:08 +03:00
struct smb2_transport * transport = talloc_get_type ( private_data ,
2006-07-17 11:45:23 +04:00
struct smb2_transport ) ;
2011-11-22 13:10:30 +04:00
struct timeval next ;
2009-02-02 11:56:47 +03:00
transport - > idle . func ( transport , transport - > idle . private_data ) ;
2011-11-22 13:10:30 +04:00
next = timeval_current_ofs_usec ( transport - > idle . period ) ;
transport - > idle . te = tevent_add_timer ( transport - > ev ,
transport ,
next ,
idle_handler ,
transport ) ;
2006-07-17 11:45:23 +04:00
}
/*
setup the idle handler for a transport
the period is in microseconds
*/
void smb2_transport_idle_handler ( struct smb2_transport * transport ,
void ( * idle_func ) ( struct smb2_transport * , void * ) ,
uint64_t period ,
2009-02-02 10:26:08 +03:00
void * private_data )
2006-07-17 11:45:23 +04:00
{
2011-11-22 13:10:30 +04:00
TALLOC_FREE ( transport - > idle . te ) ;
2006-07-17 11:45:23 +04:00
transport - > idle . func = idle_func ;
2009-02-02 11:56:47 +03:00
transport - > idle . private_data = private_data ;
2006-07-17 11:45:23 +04:00
transport - > idle . period = period ;
2011-11-22 13:10:30 +04:00
transport - > idle . te = tevent_add_timer ( transport - > ev ,
transport ,
timeval_current_ofs_usec ( period ) ,
idle_handler ,
transport ) ;
2006-07-17 11:45:23 +04:00
}