2011-07-18 16:35:03 +04:00
/*
Unix SMB / CIFS implementation .
Infrastructure for async SMB client requests
Copyright ( C ) Volker Lendecke 2008
2011-08-11 15:29:59 +04:00
Copyright ( C ) Stefan Metzmacher 2011
2011-07-18 16:35:03 +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-08-11 15:29:59 +04:00
# include "system/network.h"
2011-07-18 16:35:03 +04:00
# include "../lib/async_req/async_sock.h"
# include "../lib/util/tevent_ntstatus.h"
# include "../lib/util/tevent_unix.h"
2011-08-11 15:29:59 +04:00
# include "lib/util/util_net.h"
2011-09-03 12:18:17 +04:00
# include "lib/util/dlinklist.h"
2011-08-11 15:29:59 +04:00
# include "../libcli/smb/smb_common.h"
2011-07-18 16:35:03 +04:00
# include "../libcli/smb/smb_seal.h"
2011-08-11 15:29:59 +04:00
# include "../libcli/smb/smb_signing.h"
2011-07-18 16:35:03 +04:00
# include "../libcli/smb/read_smb.h"
2011-08-11 15:29:59 +04:00
# include "smbXcli_base.h"
# include "librpc/ndr/libndr.h"
2011-07-18 16:35:03 +04:00
2011-09-03 12:18:17 +04:00
struct smbXcli_conn ;
struct smbXcli_req ;
struct smbXcli_session ;
2012-06-02 20:03:20 +04:00
struct smbXcli_tcon ;
2011-09-03 12:18:17 +04:00
2011-08-11 15:29:59 +04:00
struct smbXcli_conn {
2011-09-25 05:15:54 +04:00
int read_fd ;
int write_fd ;
2011-08-11 15:29:59 +04:00
struct sockaddr_storage local_ss ;
struct sockaddr_storage remote_ss ;
const char * remote_name ;
struct tevent_queue * outgoing ;
struct tevent_req * * pending ;
struct tevent_req * read_smb_req ;
enum protocol_types protocol ;
bool allow_signing ;
bool desire_signing ;
bool mandatory_signing ;
/*
* The incoming dispatch function should return :
* - NT_STATUS_RETRY , if more incoming PDUs are expected .
* - NT_STATUS_OK , if no more processing is desired , e . g .
* the dispatch function called
* tevent_req_done ( ) .
* - All other return values disconnect the connection .
*/
NTSTATUS ( * dispatch_incoming ) ( struct smbXcli_conn * conn ,
TALLOC_CTX * tmp_mem ,
uint8_t * inbuf ) ;
struct {
struct {
uint32_t capabilities ;
uint32_t max_xmit ;
} client ;
struct {
uint32_t capabilities ;
uint32_t max_xmit ;
uint16_t max_mux ;
uint16_t security_mode ;
bool readbraw ;
bool writebraw ;
bool lockread ;
bool writeunlock ;
uint32_t session_key ;
struct GUID guid ;
DATA_BLOB gss_blob ;
uint8_t challenge [ 8 ] ;
const char * workgroup ;
2011-09-15 16:02:49 +04:00
const char * name ;
2011-08-11 15:29:59 +04:00
int time_zone ;
NTTIME system_time ;
} server ;
uint32_t capabilities ;
uint32_t max_xmit ;
uint16_t mid ;
struct smb_signing_state * signing ;
struct smb_trans_enc_state * trans_enc ;
2011-11-25 17:49:24 +04:00
struct tevent_req * read_braw_req ;
2011-08-11 15:29:59 +04:00
} smb1 ;
2011-09-15 14:23:05 +04:00
struct {
struct {
2012-02-29 05:02:29 +04:00
uint32_t capabilities ;
2011-09-15 14:23:05 +04:00
uint16_t security_mode ;
2011-09-15 15:21:42 +04:00
struct GUID guid ;
2011-09-15 14:23:05 +04:00
} client ;
struct {
uint32_t capabilities ;
uint16_t security_mode ;
struct GUID guid ;
uint32_t max_trans_size ;
uint32_t max_read_size ;
uint32_t max_write_size ;
NTTIME system_time ;
NTTIME start_time ;
DATA_BLOB gss_blob ;
} server ;
uint64_t mid ;
2011-09-06 19:52:02 +04:00
uint16_t cur_credits ;
uint16_t max_credits ;
2011-09-15 14:23:05 +04:00
} smb2 ;
2011-09-03 12:18:17 +04:00
struct smbXcli_session * sessions ;
} ;
2012-07-26 04:36:37 +04:00
struct smb2cli_session {
uint64_t session_id ;
uint16_t session_flags ;
DATA_BLOB application_key ;
DATA_BLOB signing_key ;
bool should_sign ;
bool should_encrypt ;
DATA_BLOB encryption_key ;
DATA_BLOB decryption_key ;
uint64_t nonce_high ;
uint64_t nonce_low ;
2012-07-26 03:54:33 +04:00
uint16_t channel_sequence ;
2012-07-26 04:36:37 +04:00
} ;
2011-09-03 12:18:17 +04:00
struct smbXcli_session {
struct smbXcli_session * prev , * next ;
struct smbXcli_conn * conn ;
2012-06-02 14:49:22 +04:00
struct {
uint16_t session_id ;
2012-08-01 10:53:30 +04:00
DATA_BLOB application_key ;
2012-08-01 11:01:25 +04:00
bool protected_key ;
2012-06-02 14:49:22 +04:00
} smb1 ;
2012-07-26 04:36:37 +04:00
struct smb2cli_session * smb2 ;
2011-09-03 12:18:17 +04:00
struct {
DATA_BLOB signing_key ;
2012-07-26 04:36:37 +04:00
} smb2_channel ;
2012-10-22 16:18:20 +04:00
/*
* this should be a short term hack
* until the upper layers have implemented
* re - authentication .
*/
bool disconnect_expired ;
2011-08-11 15:29:59 +04:00
} ;
2012-06-02 20:03:20 +04:00
struct smbXcli_tcon {
struct {
uint16_t tcon_id ;
uint16_t optional_support ;
uint32_t maximal_access ;
uint32_t guest_maximal_access ;
char * service ;
char * fs_type ;
} smb1 ;
struct {
uint32_t tcon_id ;
uint8_t type ;
uint32_t flags ;
uint32_t capabilities ;
uint32_t maximal_access ;
2012-07-25 14:34:20 +04:00
bool should_encrypt ;
2012-06-02 20:03:20 +04:00
} smb2 ;
} ;
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state {
struct tevent_context * ev ;
struct smbXcli_conn * conn ;
2011-09-03 12:18:32 +04:00
struct smbXcli_session * session ; /* maybe NULL */
2012-07-23 23:34:58 +04:00
struct smbXcli_tcon * tcon ; /* maybe NULL */
2011-08-11 15:29:59 +04:00
uint8_t length_hdr [ 4 ] ;
bool one_way ;
uint8_t * inbuf ;
struct {
/* Space for the header including the wct */
uint8_t hdr [ HDR_VWV ] ;
/*
* For normal requests , smb1cli_req_send chooses a mid .
* SecondaryV trans requests need to use the mid of the primary
* request , so we need a place to store it .
* Assume it is set if ! = 0.
*/
uint16_t mid ;
uint16_t * vwv ;
uint8_t bytecount_buf [ 2 ] ;
2011-11-28 18:28:31 +04:00
# define MAX_SMB_IOV 10
2011-08-11 15:29:59 +04:00
/* length_hdr, hdr, words, byte_count, buffers */
struct iovec iov [ 1 + 3 + MAX_SMB_IOV ] ;
int iov_count ;
2011-11-18 12:24:11 +04:00
bool one_way_seqnum ;
2011-08-11 15:29:59 +04:00
uint32_t seqnum ;
struct tevent_req * * chained_requests ;
2011-11-16 14:11:18 +04:00
uint8_t recv_cmd ;
NTSTATUS recv_status ;
/* always an array of 3 talloc elements */
struct iovec * recv_iov ;
2011-08-11 15:29:59 +04:00
} smb1 ;
2011-09-15 14:23:05 +04:00
struct {
const uint8_t * fixed ;
uint16_t fixed_len ;
const uint8_t * dyn ;
uint32_t dyn_len ;
2012-07-23 12:14:53 +04:00
uint8_t transform [ SMB2_TF_HDR_SIZE ] ;
2012-07-23 11:44:06 +04:00
uint8_t hdr [ SMB2_HDR_BODY ] ;
2011-09-15 14:23:05 +04:00
uint8_t pad [ 7 ] ; /* padding space for compounding */
2012-07-23 10:11:59 +04:00
/*
* always an array of 3 talloc elements
* ( without a SMB2_TRANSFORM header ! )
*
* HDR , BODY , DYN
*/
2011-09-15 14:23:05 +04:00
struct iovec * recv_iov ;
2011-09-06 19:52:02 +04:00
2013-08-13 11:50:29 +04:00
/*
* the expected max for the response dyn_len
*/
uint32_t max_dyn_len ;
2011-09-06 19:52:02 +04:00
uint16_t credit_charge ;
2011-09-03 12:18:32 +04:00
2012-07-23 12:00:50 +04:00
bool should_sign ;
2012-07-23 12:14:53 +04:00
bool should_encrypt ;
2012-08-14 11:30:43 +04:00
uint64_t encryption_session_id ;
2012-07-23 12:00:50 +04:00
2011-09-03 12:18:32 +04:00
bool signing_skipped ;
2011-10-27 19:20:27 +04:00
bool notify_async ;
bool got_async ;
2011-09-15 14:23:05 +04:00
} smb2 ;
2011-08-11 15:29:59 +04:00
} ;
static int smbXcli_conn_destructor ( struct smbXcli_conn * conn )
{
/*
* NT_STATUS_OK , means we do not notify the callers
*/
smbXcli_conn_disconnect ( conn , NT_STATUS_OK ) ;
2011-09-03 12:18:17 +04:00
while ( conn - > sessions ) {
conn - > sessions - > conn = NULL ;
DLIST_REMOVE ( conn - > sessions , conn - > sessions ) ;
}
2011-08-11 15:29:59 +04:00
if ( conn - > smb1 . trans_enc ) {
2012-01-14 08:30:34 +04:00
TALLOC_FREE ( conn - > smb1 . trans_enc ) ;
2011-08-11 15:29:59 +04:00
}
return 0 ;
}
struct smbXcli_conn * smbXcli_conn_create ( TALLOC_CTX * mem_ctx ,
int fd ,
const char * remote_name ,
enum smb_signing_setting signing_state ,
2011-09-15 15:21:42 +04:00
uint32_t smb1_capabilities ,
2012-02-29 05:02:29 +04:00
struct GUID * client_guid ,
uint32_t smb2_capabilities )
2011-08-11 15:29:59 +04:00
{
struct smbXcli_conn * conn = NULL ;
void * ss = NULL ;
struct sockaddr * sa = NULL ;
socklen_t sa_length ;
int ret ;
conn = talloc_zero ( mem_ctx , struct smbXcli_conn ) ;
if ( ! conn ) {
return NULL ;
}
2011-09-25 05:15:54 +04:00
conn - > read_fd = fd ;
conn - > write_fd = dup ( fd ) ;
if ( conn - > write_fd = = - 1 ) {
goto error ;
}
2011-08-11 15:29:59 +04:00
conn - > remote_name = talloc_strdup ( conn , remote_name ) ;
if ( conn - > remote_name = = NULL ) {
goto error ;
}
ss = ( void * ) & conn - > local_ss ;
sa = ( struct sockaddr * ) ss ;
sa_length = sizeof ( conn - > local_ss ) ;
ret = getsockname ( fd , sa , & sa_length ) ;
if ( ret = = - 1 ) {
goto error ;
}
ss = ( void * ) & conn - > remote_ss ;
sa = ( struct sockaddr * ) ss ;
sa_length = sizeof ( conn - > remote_ss ) ;
ret = getpeername ( fd , sa , & sa_length ) ;
if ( ret = = - 1 ) {
goto error ;
}
conn - > outgoing = tevent_queue_create ( conn , " smbXcli_outgoing " ) ;
if ( conn - > outgoing = = NULL ) {
goto error ;
}
conn - > pending = NULL ;
conn - > protocol = PROTOCOL_NONE ;
switch ( signing_state ) {
case SMB_SIGNING_OFF :
/* never */
conn - > allow_signing = false ;
conn - > desire_signing = false ;
conn - > mandatory_signing = false ;
break ;
case SMB_SIGNING_DEFAULT :
case SMB_SIGNING_IF_REQUIRED :
/* if the server requires it */
conn - > allow_signing = true ;
conn - > desire_signing = false ;
conn - > mandatory_signing = false ;
break ;
case SMB_SIGNING_REQUIRED :
/* always */
conn - > allow_signing = true ;
conn - > desire_signing = true ;
conn - > mandatory_signing = true ;
break ;
}
conn - > smb1 . client . capabilities = smb1_capabilities ;
conn - > smb1 . client . max_xmit = UINT16_MAX ;
conn - > smb1 . capabilities = conn - > smb1 . client . capabilities ;
conn - > smb1 . max_xmit = 1024 ;
conn - > smb1 . mid = 1 ;
/* initialise signing */
conn - > smb1 . signing = smb_signing_init ( conn ,
conn - > allow_signing ,
conn - > desire_signing ,
conn - > mandatory_signing ) ;
if ( ! conn - > smb1 . signing ) {
goto error ;
}
2011-09-15 14:23:05 +04:00
conn - > smb2 . client . security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED ;
if ( conn - > mandatory_signing ) {
conn - > smb2 . client . security_mode | = SMB2_NEGOTIATE_SIGNING_REQUIRED ;
}
2011-09-15 15:21:42 +04:00
if ( client_guid ) {
conn - > smb2 . client . guid = * client_guid ;
}
2012-02-29 05:02:29 +04:00
conn - > smb2 . client . capabilities = smb2_capabilities ;
2011-09-15 14:23:05 +04:00
2011-09-06 19:52:02 +04:00
conn - > smb2 . cur_credits = 1 ;
conn - > smb2 . max_credits = 0 ;
2011-08-11 15:29:59 +04:00
talloc_set_destructor ( conn , smbXcli_conn_destructor ) ;
return conn ;
error :
2011-09-25 05:15:54 +04:00
if ( conn - > write_fd ! = - 1 ) {
close ( conn - > write_fd ) ;
}
2011-08-11 15:29:59 +04:00
TALLOC_FREE ( conn ) ;
return NULL ;
}
bool smbXcli_conn_is_connected ( struct smbXcli_conn * conn )
{
if ( conn = = NULL ) {
return false ;
}
2011-09-25 05:15:54 +04:00
if ( conn - > read_fd = = - 1 ) {
2011-08-11 15:29:59 +04:00
return false ;
}
return true ;
}
enum protocol_types smbXcli_conn_protocol ( struct smbXcli_conn * conn )
{
return conn - > protocol ;
}
bool smbXcli_conn_use_unicode ( struct smbXcli_conn * conn )
{
if ( conn - > protocol > = PROTOCOL_SMB2_02 ) {
return true ;
}
if ( conn - > smb1 . capabilities & CAP_UNICODE ) {
return true ;
}
return false ;
}
void smbXcli_conn_set_sockopt ( struct smbXcli_conn * conn , const char * options )
{
2011-09-25 05:15:54 +04:00
set_socket_options ( conn - > read_fd , options ) ;
2011-08-11 15:29:59 +04:00
}
const struct sockaddr_storage * smbXcli_conn_local_sockaddr ( struct smbXcli_conn * conn )
{
return & conn - > local_ss ;
}
const struct sockaddr_storage * smbXcli_conn_remote_sockaddr ( struct smbXcli_conn * conn )
{
return & conn - > remote_ss ;
}
const char * smbXcli_conn_remote_name ( struct smbXcli_conn * conn )
{
return conn - > remote_name ;
}
2011-09-15 16:02:49 +04:00
uint16_t smbXcli_conn_max_requests ( struct smbXcli_conn * conn )
{
if ( conn - > protocol > = PROTOCOL_SMB2_02 ) {
/*
* TODO . . .
*/
return 1 ;
}
return conn - > smb1 . server . max_mux ;
}
NTTIME smbXcli_conn_server_system_time ( struct smbXcli_conn * conn )
{
if ( conn - > protocol > = PROTOCOL_SMB2_02 ) {
return conn - > smb2 . server . system_time ;
}
return conn - > smb1 . server . system_time ;
}
const DATA_BLOB * smbXcli_conn_server_gss_blob ( struct smbXcli_conn * conn )
{
if ( conn - > protocol > = PROTOCOL_SMB2_02 ) {
return & conn - > smb2 . server . gss_blob ;
}
return & conn - > smb1 . server . gss_blob ;
}
const struct GUID * smbXcli_conn_server_guid ( struct smbXcli_conn * conn )
{
if ( conn - > protocol > = PROTOCOL_SMB2_02 ) {
return & conn - > smb2 . server . guid ;
}
return & conn - > smb1 . server . guid ;
}
2012-01-04 16:28:07 +04:00
struct smbXcli_conn_samba_suicide_state {
struct smbXcli_conn * conn ;
struct iovec iov ;
uint8_t buf [ 9 ] ;
} ;
static void smbXcli_conn_samba_suicide_done ( struct tevent_req * subreq ) ;
struct tevent_req * smbXcli_conn_samba_suicide_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct smbXcli_conn * conn ,
uint8_t exitcode )
{
struct tevent_req * req , * subreq ;
struct smbXcli_conn_samba_suicide_state * state ;
req = tevent_req_create ( mem_ctx , & state ,
struct smbXcli_conn_samba_suicide_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > conn = conn ;
SIVAL ( state - > buf , 4 , 0x74697865 ) ;
SCVAL ( state - > buf , 8 , exitcode ) ;
_smb_setlen_nbt ( state - > buf , sizeof ( state - > buf ) - 4 ) ;
state - > iov . iov_base = state - > buf ;
state - > iov . iov_len = sizeof ( state - > buf ) ;
subreq = writev_send ( state , ev , conn - > outgoing , conn - > write_fd ,
false , & state - > iov , 1 ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , smbXcli_conn_samba_suicide_done , req ) ;
return req ;
}
static void smbXcli_conn_samba_suicide_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct smbXcli_conn_samba_suicide_state * state = tevent_req_data (
req , struct smbXcli_conn_samba_suicide_state ) ;
ssize_t nwritten ;
int err ;
nwritten = writev_recv ( subreq , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( nwritten = = - 1 ) {
NTSTATUS status = map_nt_error_from_unix_common ( err ) ;
smbXcli_conn_disconnect ( state - > conn , status ) ;
return ;
}
tevent_req_done ( req ) ;
}
NTSTATUS smbXcli_conn_samba_suicide_recv ( struct tevent_req * req )
{
return tevent_req_simple_recv_ntstatus ( req ) ;
}
NTSTATUS smbXcli_conn_samba_suicide ( struct smbXcli_conn * conn ,
uint8_t exitcode )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
bool ok ;
if ( smbXcli_conn_has_async_calls ( conn ) ) {
/*
* Can ' t use sync call while an async call is in flight
*/
status = NT_STATUS_INVALID_PARAMETER_MIX ;
goto fail ;
}
2013-02-18 12:07:11 +04:00
ev = samba_tevent_context_init ( frame ) ;
2012-01-04 16:28:07 +04:00
if ( ev = = NULL ) {
goto fail ;
}
req = smbXcli_conn_samba_suicide_send ( frame , ev , conn , exitcode ) ;
if ( req = = NULL ) {
goto fail ;
}
ok = tevent_req_poll ( req , ev ) ;
if ( ! ok ) {
status = map_nt_error_from_unix_common ( errno ) ;
goto fail ;
}
status = smbXcli_conn_samba_suicide_recv ( req ) ;
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
2011-09-15 16:02:49 +04:00
uint32_t smb1cli_conn_capabilities ( struct smbXcli_conn * conn )
{
return conn - > smb1 . capabilities ;
}
uint32_t smb1cli_conn_max_xmit ( struct smbXcli_conn * conn )
{
return conn - > smb1 . max_xmit ;
}
2013-08-13 16:05:05 +04:00
bool smb1cli_conn_req_possible ( struct smbXcli_conn * conn )
{
size_t pending ;
uint16_t possible = conn - > smb1 . server . max_mux ;
pending = tevent_queue_length ( conn - > outgoing ) ;
if ( pending > = possible ) {
return false ;
}
pending + = talloc_array_length ( conn - > pending ) ;
if ( pending > = possible ) {
return false ;
}
return true ;
}
2011-09-15 16:02:49 +04:00
uint32_t smb1cli_conn_server_session_key ( struct smbXcli_conn * conn )
{
return conn - > smb1 . server . session_key ;
}
const uint8_t * smb1cli_conn_server_challenge ( struct smbXcli_conn * conn )
{
return conn - > smb1 . server . challenge ;
}
uint16_t smb1cli_conn_server_security_mode ( struct smbXcli_conn * conn )
{
return conn - > smb1 . server . security_mode ;
}
2011-11-25 18:13:38 +04:00
bool smb1cli_conn_server_readbraw ( struct smbXcli_conn * conn )
{
return conn - > smb1 . server . readbraw ;
}
bool smb1cli_conn_server_writebraw ( struct smbXcli_conn * conn )
{
return conn - > smb1 . server . writebraw ;
}
bool smb1cli_conn_server_lockread ( struct smbXcli_conn * conn )
{
return conn - > smb1 . server . lockread ;
}
bool smb1cli_conn_server_writeunlock ( struct smbXcli_conn * conn )
{
return conn - > smb1 . server . writeunlock ;
}
2011-09-15 16:02:49 +04:00
int smb1cli_conn_server_time_zone ( struct smbXcli_conn * conn )
{
return conn - > smb1 . server . time_zone ;
}
2011-08-11 15:29:59 +04:00
bool smb1cli_conn_activate_signing ( struct smbXcli_conn * conn ,
const DATA_BLOB user_session_key ,
const DATA_BLOB response )
{
return smb_signing_activate ( conn - > smb1 . signing ,
user_session_key ,
response ) ;
}
bool smb1cli_conn_check_signing ( struct smbXcli_conn * conn ,
const uint8_t * buf , uint32_t seqnum )
{
2012-08-03 11:58:29 +04:00
const uint8_t * hdr = buf + NBT_HDR_SIZE ;
size_t len = smb_len_nbt ( buf ) ;
return smb_signing_check_pdu ( conn - > smb1 . signing , hdr , len , seqnum ) ;
2011-08-11 15:29:59 +04:00
}
bool smb1cli_conn_signing_is_active ( struct smbXcli_conn * conn )
{
return smb_signing_is_active ( conn - > smb1 . signing ) ;
}
void smb1cli_conn_set_encryption ( struct smbXcli_conn * conn ,
struct smb_trans_enc_state * es )
{
/* Replace the old state, if any. */
if ( conn - > smb1 . trans_enc ) {
2012-01-14 08:30:34 +04:00
TALLOC_FREE ( conn - > smb1 . trans_enc ) ;
2011-08-11 15:29:59 +04:00
}
conn - > smb1 . trans_enc = es ;
}
bool smb1cli_conn_encryption_on ( struct smbXcli_conn * conn )
{
return common_encryption_on ( conn - > smb1 . trans_enc ) ;
}
2011-11-16 14:14:16 +04:00
static NTSTATUS smb1cli_pull_raw_error ( const uint8_t * hdr )
2011-07-18 16:35:03 +04:00
{
uint32_t flags2 = SVAL ( hdr , HDR_FLG2 ) ;
NTSTATUS status = NT_STATUS ( IVAL ( hdr , HDR_RCLS ) ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
return NT_STATUS_OK ;
}
if ( flags2 & FLAGS2_32_BIT_ERROR_CODES ) {
return status ;
}
return NT_STATUS_DOS ( CVAL ( hdr , HDR_RCLS ) , SVAL ( hdr , HDR_ERR ) ) ;
}
2011-08-11 15:29:59 +04:00
/**
* Is the SMB command able to hold an AND_X successor
* @ param [ in ] cmd The SMB command in question
* @ retval Can we add a chained request after " cmd " ?
*/
bool smb1cli_is_andx_req ( uint8_t cmd )
{
switch ( cmd ) {
case SMBtconX :
case SMBlockingX :
case SMBopenX :
case SMBreadX :
case SMBwriteX :
case SMBsesssetupX :
case SMBulogoffX :
case SMBntcreateX :
return true ;
break ;
default :
break ;
}
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
return false ;
}
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
static uint16_t smb1cli_alloc_mid ( struct smbXcli_conn * conn )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
size_t num_pending = talloc_array_length ( conn - > pending ) ;
2011-07-18 16:35:03 +04:00
uint16_t result ;
while ( true ) {
2011-08-11 15:29:59 +04:00
size_t i ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
result = conn - > smb1 . mid + + ;
2011-07-18 16:35:03 +04:00
if ( ( result = = 0 ) | | ( result = = 0xffff ) ) {
continue ;
}
for ( i = 0 ; i < num_pending ; i + + ) {
2011-08-11 15:29:59 +04:00
if ( result = = smb1cli_req_mid ( conn - > pending [ i ] ) ) {
2011-07-18 16:35:03 +04:00
break ;
}
}
if ( i = = num_pending ) {
return result ;
}
}
}
2011-08-11 15:29:59 +04:00
void smbXcli_req_unset_pending ( struct tevent_req * req )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
struct smbXcli_conn * conn = state - > conn ;
size_t num_pending = talloc_array_length ( conn - > pending ) ;
size_t i ;
if ( state - > smb1 . mid ! = 0 ) {
2011-07-18 16:35:03 +04:00
/*
* This is a [ nt ] trans [ 2 ] request which waits
* for more than one reply .
*/
return ;
}
talloc_set_destructor ( req , NULL ) ;
if ( num_pending = = 1 ) {
/*
* The pending read_smb tevent_req is a child of
2011-08-11 15:29:59 +04:00
* conn - > pending . So if nothing is pending anymore , we need to
2011-07-18 16:35:03 +04:00
* delete the socket read fde .
*/
2011-08-11 15:29:59 +04:00
TALLOC_FREE ( conn - > pending ) ;
conn - > read_smb_req = NULL ;
2011-07-18 16:35:03 +04:00
return ;
}
for ( i = 0 ; i < num_pending ; i + + ) {
2011-08-11 15:29:59 +04:00
if ( req = = conn - > pending [ i ] ) {
2011-07-18 16:35:03 +04:00
break ;
}
}
if ( i = = num_pending ) {
/*
* Something ' s seriously broken . Just returning here is the
* right thing nevertheless , the point of this routine is to
2011-08-11 15:29:59 +04:00
* remove ourselves from conn - > pending .
2011-07-18 16:35:03 +04:00
*/
return ;
}
/*
2011-08-11 15:29:59 +04:00
* Remove ourselves from the conn - > pending array
2011-07-18 16:35:03 +04:00
*/
for ( ; i < ( num_pending - 1 ) ; i + + ) {
2011-08-11 15:29:59 +04:00
conn - > pending [ i ] = conn - > pending [ i + 1 ] ;
2011-07-18 16:35:03 +04:00
}
/*
* No NULL check here , we ' re shrinking by sizeof ( void * ) , and
* talloc_realloc just adjusts the size for this .
*/
2011-08-11 15:29:59 +04:00
conn - > pending = talloc_realloc ( NULL , conn - > pending , struct tevent_req * ,
num_pending - 1 ) ;
2011-07-18 16:35:03 +04:00
return ;
}
2011-08-11 15:29:59 +04:00
static int smbXcli_req_destructor ( struct tevent_req * req )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
2011-07-18 16:35:03 +04:00
/*
* Make sure we really remove it from
* the pending array on destruction .
*/
2011-08-11 15:29:59 +04:00
state - > smb1 . mid = 0 ;
smbXcli_req_unset_pending ( req ) ;
2011-07-18 16:35:03 +04:00
return 0 ;
}
2011-09-24 08:06:46 +04:00
static bool smb1cli_req_cancel ( struct tevent_req * req ) ;
static bool smb2cli_req_cancel ( struct tevent_req * req ) ;
static bool smbXcli_req_cancel ( struct tevent_req * req )
{
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
if ( ! smbXcli_conn_is_connected ( state - > conn ) ) {
return false ;
}
if ( state - > conn - > protocol = = PROTOCOL_NONE ) {
return false ;
}
if ( state - > conn - > protocol > = PROTOCOL_SMB2_02 ) {
return smb2cli_req_cancel ( req ) ;
}
return smb1cli_req_cancel ( req ) ;
}
2011-08-11 15:29:59 +04:00
static bool smbXcli_conn_receive_next ( struct smbXcli_conn * conn ) ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
bool smbXcli_req_set_pending ( struct tevent_req * req )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
struct smbXcli_conn * conn ;
2011-07-18 16:35:03 +04:00
struct tevent_req * * pending ;
2011-08-11 15:29:59 +04:00
size_t num_pending ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
conn = state - > conn ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
if ( ! smbXcli_conn_is_connected ( conn ) ) {
return false ;
}
num_pending = talloc_array_length ( conn - > pending ) ;
pending = talloc_realloc ( conn , conn - > pending , struct tevent_req * ,
2011-07-18 16:35:03 +04:00
num_pending + 1 ) ;
if ( pending = = NULL ) {
return false ;
}
pending [ num_pending ] = req ;
2011-08-11 15:29:59 +04:00
conn - > pending = pending ;
talloc_set_destructor ( req , smbXcli_req_destructor ) ;
2011-09-24 08:06:46 +04:00
tevent_req_set_cancel_fn ( req , smbXcli_req_cancel ) ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
if ( ! smbXcli_conn_receive_next ( conn ) ) {
2011-07-18 16:35:03 +04:00
/*
* the caller should notify the current request
*
* And all other pending requests get notified
2011-08-11 15:29:59 +04:00
* by smbXcli_conn_disconnect ( ) .
2011-07-18 16:35:03 +04:00
*/
2011-08-11 15:29:59 +04:00
smbXcli_req_unset_pending ( req ) ;
smbXcli_conn_disconnect ( conn , NT_STATUS_NO_MEMORY ) ;
2011-07-18 16:35:03 +04:00
return false ;
}
return true ;
}
2011-08-11 15:29:59 +04:00
static void smbXcli_conn_received ( struct tevent_req * subreq ) ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
static bool smbXcli_conn_receive_next ( struct smbXcli_conn * conn )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
size_t num_pending = talloc_array_length ( conn - > pending ) ;
2011-07-18 16:35:03 +04:00
struct tevent_req * req ;
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * state ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
if ( conn - > read_smb_req ! = NULL ) {
2011-07-18 16:35:03 +04:00
return true ;
}
if ( num_pending = = 0 ) {
2011-09-15 14:23:05 +04:00
if ( conn - > smb2 . mid < UINT64_MAX ) {
/* no more pending requests, so we are done for now */
return true ;
}
/*
* If there are no more SMB2 requests possible ,
* because we are out of message ids ,
* we need to disconnect .
*/
smbXcli_conn_disconnect ( conn , NT_STATUS_CONNECTION_ABORTED ) ;
2011-07-18 16:35:03 +04:00
return true ;
}
2011-08-11 15:29:59 +04:00
req = conn - > pending [ 0 ] ;
state = tevent_req_data ( req , struct smbXcli_req_state ) ;
2011-07-18 16:35:03 +04:00
/*
* We ' re the first ones , add the read_smb request that waits for the
* answer from the server
*/
2011-09-25 05:15:54 +04:00
conn - > read_smb_req = read_smb_send ( conn - > pending ,
state - > ev ,
conn - > read_fd ) ;
2011-08-11 15:29:59 +04:00
if ( conn - > read_smb_req = = NULL ) {
2011-07-18 16:35:03 +04:00
return false ;
}
2011-08-11 15:29:59 +04:00
tevent_req_set_callback ( conn - > read_smb_req , smbXcli_conn_received , conn ) ;
2011-07-18 16:35:03 +04:00
return true ;
}
2011-08-11 15:29:59 +04:00
void smbXcli_conn_disconnect ( struct smbXcli_conn * conn , NTSTATUS status )
2011-07-18 16:35:03 +04:00
{
2012-07-26 03:54:33 +04:00
struct smbXcli_session * session ;
2011-11-28 13:23:23 +04:00
tevent_queue_stop ( conn - > outgoing ) ;
2011-09-25 05:15:54 +04:00
if ( conn - > read_fd ! = - 1 ) {
close ( conn - > read_fd ) ;
}
if ( conn - > write_fd ! = - 1 ) {
close ( conn - > write_fd ) ;
2011-08-11 15:29:59 +04:00
}
2011-09-25 05:15:54 +04:00
conn - > read_fd = - 1 ;
conn - > write_fd = - 1 ;
2011-07-18 16:35:03 +04:00
2012-07-26 03:54:33 +04:00
session = conn - > sessions ;
if ( talloc_array_length ( conn - > pending ) = = 0 ) {
/*
* if we do not have pending requests
* there is no need to update the channel_sequence
*/
session = NULL ;
}
for ( ; session ; session = session - > next ) {
smb2cli_session_increment_channel_sequence ( session ) ;
}
2011-07-18 16:35:03 +04:00
/*
* Cancel all pending requests . We do not do a for - loop walking
2011-08-11 15:29:59 +04:00
* conn - > pending because that array changes in
* smbXcli_req_unset_pending .
2011-07-18 16:35:03 +04:00
*/
2011-08-11 15:29:59 +04:00
while ( talloc_array_length ( conn - > pending ) > 0 ) {
2011-07-18 16:35:03 +04:00
struct tevent_req * req ;
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * state ;
2011-11-18 13:30:33 +04:00
struct tevent_req * * chain ;
size_t num_chained ;
size_t i ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
req = conn - > pending [ 0 ] ;
state = tevent_req_data ( req , struct smbXcli_req_state ) ;
2011-07-18 16:35:03 +04:00
2011-11-18 13:30:33 +04:00
if ( state - > smb1 . chained_requests = = NULL ) {
/*
* We ' re dead . No point waiting for trans2
* replies .
*/
state - > smb1 . mid = 0 ;
2011-08-11 15:29:59 +04:00
2011-11-18 13:30:33 +04:00
smbXcli_req_unset_pending ( req ) ;
2011-07-18 16:35:03 +04:00
2011-11-18 13:30:33 +04:00
if ( NT_STATUS_IS_OK ( status ) ) {
/* do not notify the callers */
continue ;
}
/*
* we need to defer the callback , because we may notify
* more then one caller .
*/
tevent_req_defer_callback ( req , state - > ev ) ;
tevent_req_nterror ( req , status ) ;
2011-08-11 15:29:59 +04:00
continue ;
}
2011-07-18 16:35:03 +04:00
2011-11-18 13:30:33 +04:00
chain = talloc_move ( conn , & state - > smb1 . chained_requests ) ;
num_chained = talloc_array_length ( chain ) ;
for ( i = 0 ; i < num_chained ; i + + ) {
req = chain [ i ] ;
state = tevent_req_data ( req , struct smbXcli_req_state ) ;
/*
* We ' re dead . No point waiting for trans2
* replies .
*/
state - > smb1 . mid = 0 ;
smbXcli_req_unset_pending ( req ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
/* do not notify the callers */
continue ;
}
/*
* we need to defer the callback , because we may notify
2011-11-21 15:12:53 +04:00
* more than one caller .
2011-11-18 13:30:33 +04:00
*/
tevent_req_defer_callback ( req , state - > ev ) ;
tevent_req_nterror ( req , status ) ;
}
TALLOC_FREE ( chain ) ;
2011-07-18 16:35:03 +04:00
}
}
/*
* Fetch a smb request ' s mid . Only valid after the request has been sent by
2011-08-11 15:29:59 +04:00
* smb1cli_req_send ( ) .
2011-07-18 16:35:03 +04:00
*/
2011-08-11 15:29:59 +04:00
uint16_t smb1cli_req_mid ( struct tevent_req * req )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
if ( state - > smb1 . mid ! = 0 ) {
return state - > smb1 . mid ;
2011-07-18 16:35:03 +04:00
}
2011-08-11 15:29:59 +04:00
return SVAL ( state - > smb1 . hdr , HDR_MID ) ;
2011-07-18 16:35:03 +04:00
}
2011-08-11 15:29:59 +04:00
void smb1cli_req_set_mid ( struct tevent_req * req , uint16_t mid )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
state - > smb1 . mid = mid ;
2011-07-18 16:35:03 +04:00
}
2011-08-11 15:29:59 +04:00
uint32_t smb1cli_req_seqnum ( struct tevent_req * req )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
return state - > smb1 . seqnum ;
2011-07-18 16:35:03 +04:00
}
2011-08-11 15:29:59 +04:00
void smb1cli_req_set_seqnum ( struct tevent_req * req , uint32_t seqnum )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
state - > smb1 . seqnum = seqnum ;
2011-07-18 16:35:03 +04:00
}
2011-08-11 15:29:59 +04:00
static size_t smbXcli_iov_len ( const struct iovec * iov , int count )
2011-07-18 16:35:03 +04:00
{
size_t result = 0 ;
int i ;
for ( i = 0 ; i < count ; i + + ) {
result + = iov [ i ] . iov_len ;
}
return result ;
}
2011-08-11 15:29:59 +04:00
static uint8_t * smbXcli_iov_concat ( TALLOC_CTX * mem_ctx ,
const struct iovec * iov ,
int count )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
size_t len = smbXcli_iov_len ( iov , count ) ;
2011-07-18 16:35:03 +04:00
size_t copied ;
uint8_t * buf ;
int i ;
buf = talloc_array ( mem_ctx , uint8_t , len ) ;
if ( buf = = NULL ) {
return NULL ;
}
copied = 0 ;
for ( i = 0 ; i < count ; i + + ) {
memcpy ( buf + copied , iov [ i ] . iov_base , iov [ i ] . iov_len ) ;
copied + = iov [ i ] . iov_len ;
}
return buf ;
}
2011-09-07 21:07:51 +04:00
static void smb1cli_req_flags ( enum protocol_types protocol ,
uint32_t smb1_capabilities ,
uint8_t smb_command ,
uint8_t additional_flags ,
uint8_t clear_flags ,
uint8_t * _flags ,
uint16_t additional_flags2 ,
uint16_t clear_flags2 ,
uint16_t * _flags2 )
{
uint8_t flags = 0 ;
uint16_t flags2 = 0 ;
if ( protocol > = PROTOCOL_LANMAN1 ) {
flags | = FLAG_CASELESS_PATHNAMES ;
flags | = FLAG_CANONICAL_PATHNAMES ;
}
if ( protocol > = PROTOCOL_LANMAN2 ) {
flags2 | = FLAGS2_LONG_PATH_COMPONENTS ;
flags2 | = FLAGS2_EXTENDED_ATTRIBUTES ;
}
if ( protocol > = PROTOCOL_NT1 ) {
flags2 | = FLAGS2_IS_LONG_NAME ;
if ( smb1_capabilities & CAP_UNICODE ) {
flags2 | = FLAGS2_UNICODE_STRINGS ;
}
if ( smb1_capabilities & CAP_STATUS32 ) {
flags2 | = FLAGS2_32_BIT_ERROR_CODES ;
}
if ( smb1_capabilities & CAP_EXTENDED_SECURITY ) {
flags2 | = FLAGS2_EXTENDED_SECURITY ;
}
}
flags | = additional_flags ;
flags & = ~ clear_flags ;
flags2 | = additional_flags2 ;
flags2 & = ~ clear_flags2 ;
* _flags = flags ;
* _flags2 = flags2 ;
}
2011-09-24 08:06:46 +04:00
static void smb1cli_req_cancel_done ( struct tevent_req * subreq ) ;
static bool smb1cli_req_cancel ( struct tevent_req * req )
{
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
uint8_t flags ;
uint16_t flags2 ;
uint32_t pid ;
uint16_t mid ;
struct tevent_req * subreq ;
NTSTATUS status ;
flags = CVAL ( state - > smb1 . hdr , HDR_FLG ) ;
flags2 = SVAL ( state - > smb1 . hdr , HDR_FLG2 ) ;
pid = SVAL ( state - > smb1 . hdr , HDR_PID ) ;
pid | = SVAL ( state - > smb1 . hdr , HDR_PIDHIGH ) < < 16 ;
mid = SVAL ( state - > smb1 . hdr , HDR_MID ) ;
subreq = smb1cli_req_create ( state , state - > ev ,
state - > conn ,
SMBntcancel ,
flags , 0 ,
flags2 , 0 ,
0 , /* timeout */
2012-07-23 23:34:58 +04:00
pid ,
state - > tcon ,
2012-07-23 22:15:21 +04:00
state - > session ,
2011-09-24 08:06:46 +04:00
0 , NULL , /* vwv */
0 , NULL ) ; /* bytes */
if ( subreq = = NULL ) {
return false ;
}
smb1cli_req_set_mid ( subreq , mid ) ;
status = smb1cli_req_chain_submit ( & subreq , 1 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( subreq ) ;
return false ;
}
smb1cli_req_set_mid ( subreq , 0 ) ;
tevent_req_set_callback ( subreq , smb1cli_req_cancel_done , NULL ) ;
return true ;
}
static void smb1cli_req_cancel_done ( struct tevent_req * subreq )
{
/* we do not care about the result */
TALLOC_FREE ( subreq ) ;
}
2011-08-11 15:29:59 +04:00
struct tevent_req * smb1cli_req_create ( TALLOC_CTX * mem_ctx ,
2011-07-18 16:35:03 +04:00
struct tevent_context * ev ,
2011-08-11 15:29:59 +04:00
struct smbXcli_conn * conn ,
2011-07-18 16:35:03 +04:00
uint8_t smb_command ,
uint8_t additional_flags ,
2011-08-11 15:29:59 +04:00
uint8_t clear_flags ,
uint16_t additional_flags2 ,
uint16_t clear_flags2 ,
uint32_t timeout_msec ,
uint32_t pid ,
2012-07-23 23:34:58 +04:00
struct smbXcli_tcon * tcon ,
2012-07-23 22:15:21 +04:00
struct smbXcli_session * session ,
2011-07-18 16:35:03 +04:00
uint8_t wct , uint16_t * vwv ,
int iov_count ,
struct iovec * bytes_iov )
{
2011-08-11 15:29:59 +04:00
struct tevent_req * req ;
struct smbXcli_req_state * state ;
uint8_t flags = 0 ;
uint16_t flags2 = 0 ;
2012-07-23 22:15:21 +04:00
uint16_t uid = 0 ;
2012-07-23 23:34:58 +04:00
uint16_t tid = 0 ;
2011-07-18 16:35:03 +04:00
if ( iov_count > MAX_SMB_IOV ) {
/*
* Should not happen : - )
*/
return NULL ;
}
2011-08-11 15:29:59 +04:00
req = tevent_req_create ( mem_ctx , & state ,
struct smbXcli_req_state ) ;
if ( req = = NULL ) {
2011-07-18 16:35:03 +04:00
return NULL ;
}
state - > ev = ev ;
2011-08-11 15:29:59 +04:00
state - > conn = conn ;
2012-07-23 22:15:21 +04:00
state - > session = session ;
2012-07-23 23:34:58 +04:00
state - > tcon = tcon ;
2012-07-23 22:15:21 +04:00
if ( session ) {
uid = session - > smb1 . session_id ;
}
2011-07-18 16:35:03 +04:00
2012-07-23 23:34:58 +04:00
if ( tcon ) {
tid = tcon - > smb1 . tcon_id ;
}
2011-11-16 14:11:18 +04:00
state - > smb1 . recv_cmd = 0xFF ;
state - > smb1 . recv_status = NT_STATUS_INTERNAL_ERROR ;
state - > smb1 . recv_iov = talloc_zero_array ( state , struct iovec , 3 ) ;
if ( state - > smb1 . recv_iov = = NULL ) {
TALLOC_FREE ( req ) ;
return NULL ;
}
2011-09-07 21:07:51 +04:00
smb1cli_req_flags ( conn - > protocol ,
conn - > smb1 . capabilities ,
smb_command ,
additional_flags ,
clear_flags ,
& flags ,
additional_flags2 ,
clear_flags2 ,
& flags2 ) ;
2011-08-11 15:29:59 +04:00
SIVAL ( state - > smb1 . hdr , 0 , SMB_MAGIC ) ;
SCVAL ( state - > smb1 . hdr , HDR_COM , smb_command ) ;
SIVAL ( state - > smb1 . hdr , HDR_RCLS , NT_STATUS_V ( NT_STATUS_OK ) ) ;
SCVAL ( state - > smb1 . hdr , HDR_FLG , flags ) ;
SSVAL ( state - > smb1 . hdr , HDR_FLG2 , flags2 ) ;
SSVAL ( state - > smb1 . hdr , HDR_PIDHIGH , pid > > 16 ) ;
SSVAL ( state - > smb1 . hdr , HDR_TID , tid ) ;
SSVAL ( state - > smb1 . hdr , HDR_PID , pid ) ;
SSVAL ( state - > smb1 . hdr , HDR_UID , uid ) ;
SSVAL ( state - > smb1 . hdr , HDR_MID , 0 ) ; /* this comes later */
2011-11-28 17:50:03 +04:00
SCVAL ( state - > smb1 . hdr , HDR_WCT , wct ) ;
2011-08-11 15:29:59 +04:00
state - > smb1 . vwv = vwv ;
SSVAL ( state - > smb1 . bytecount_buf , 0 , smbXcli_iov_len ( bytes_iov , iov_count ) ) ;
state - > smb1 . iov [ 0 ] . iov_base = ( void * ) state - > length_hdr ;
state - > smb1 . iov [ 0 ] . iov_len = sizeof ( state - > length_hdr ) ;
state - > smb1 . iov [ 1 ] . iov_base = ( void * ) state - > smb1 . hdr ;
state - > smb1 . iov [ 1 ] . iov_len = sizeof ( state - > smb1 . hdr ) ;
state - > smb1 . iov [ 2 ] . iov_base = ( void * ) state - > smb1 . vwv ;
state - > smb1 . iov [ 2 ] . iov_len = wct * sizeof ( uint16_t ) ;
state - > smb1 . iov [ 3 ] . iov_base = ( void * ) state - > smb1 . bytecount_buf ;
state - > smb1 . iov [ 3 ] . iov_len = sizeof ( uint16_t ) ;
2011-07-18 16:35:03 +04:00
if ( iov_count ! = 0 ) {
2011-08-11 15:29:59 +04:00
memcpy ( & state - > smb1 . iov [ 4 ] , bytes_iov ,
2011-07-18 16:35:03 +04:00
iov_count * sizeof ( * bytes_iov ) ) ;
}
2011-08-11 15:29:59 +04:00
state - > smb1 . iov_count = iov_count + 4 ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
if ( timeout_msec > 0 ) {
struct timeval endtime ;
endtime = timeval_current_ofs_msec ( timeout_msec ) ;
if ( ! tevent_req_set_endtime ( req , ev , endtime ) ) {
return req ;
2011-07-18 16:35:03 +04:00
}
}
switch ( smb_command ) {
case SMBtranss :
case SMBtranss2 :
case SMBnttranss :
2011-11-18 12:24:11 +04:00
state - > one_way = true ;
break ;
2011-07-18 16:35:03 +04:00
case SMBntcancel :
state - > one_way = true ;
2011-11-18 12:24:11 +04:00
state - > smb1 . one_way_seqnum = true ;
2011-07-18 16:35:03 +04:00
break ;
case SMBlockingX :
if ( ( wct = = 8 ) & &
( CVAL ( vwv + 3 , 0 ) = = LOCKING_ANDX_OPLOCK_RELEASE ) ) {
state - > one_way = true ;
}
break ;
}
2011-08-11 15:29:59 +04:00
return req ;
2011-07-18 16:35:03 +04:00
}
2011-08-11 15:29:59 +04:00
static NTSTATUS smb1cli_conn_signv ( struct smbXcli_conn * conn ,
struct iovec * iov , int iov_count ,
2011-11-18 12:24:11 +04:00
uint32_t * seqnum ,
bool one_way_seqnum )
2011-07-18 16:35:03 +04:00
{
2011-11-25 16:12:35 +04:00
TALLOC_CTX * frame = NULL ;
2011-07-18 16:35:03 +04:00
uint8_t * buf ;
/*
* Obvious optimization : Make cli_calculate_sign_mac work with struct
* iovec directly . MD5Update would do that just fine .
*/
2011-08-11 15:29:59 +04:00
if ( iov_count < 4 ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
if ( iov [ 0 ] . iov_len ! = NBT_HDR_SIZE ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
if ( iov [ 1 ] . iov_len ! = ( MIN_SMB_SIZE - sizeof ( uint16_t ) ) ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
if ( iov [ 2 ] . iov_len > ( 0xFF * sizeof ( uint16_t ) ) ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
if ( iov [ 3 ] . iov_len ! = sizeof ( uint16_t ) ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
2011-07-18 16:35:03 +04:00
}
2011-11-25 16:12:35 +04:00
frame = talloc_stackframe ( ) ;
2012-08-03 11:58:29 +04:00
buf = smbXcli_iov_concat ( frame , & iov [ 1 ] , iov_count - 1 ) ;
2011-07-18 16:35:03 +04:00
if ( buf = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2011-11-18 12:24:11 +04:00
* seqnum = smb_signing_next_seqnum ( conn - > smb1 . signing ,
one_way_seqnum ) ;
2012-08-03 11:58:29 +04:00
smb_signing_sign_pdu ( conn - > smb1 . signing ,
buf , talloc_get_size ( buf ) ,
* seqnum ) ;
memcpy ( iov [ 1 ] . iov_base , buf , iov [ 1 ] . iov_len ) ;
2011-07-18 16:35:03 +04:00
2011-11-25 16:12:35 +04:00
TALLOC_FREE ( frame ) ;
2011-07-18 16:35:03 +04:00
return NT_STATUS_OK ;
}
2011-08-11 15:29:59 +04:00
static void smb1cli_req_writev_done ( struct tevent_req * subreq ) ;
static NTSTATUS smb1cli_conn_dispatch_incoming ( struct smbXcli_conn * conn ,
TALLOC_CTX * tmp_mem ,
uint8_t * inbuf ) ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
static NTSTATUS smb1cli_req_writev_submit ( struct tevent_req * req ,
struct smbXcli_req_state * state ,
struct iovec * iov , int iov_count )
2011-07-18 16:35:03 +04:00
{
struct tevent_req * subreq ;
NTSTATUS status ;
2011-11-25 17:49:24 +04:00
uint8_t cmd ;
2011-08-11 15:29:59 +04:00
uint16_t mid ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
if ( ! smbXcli_conn_is_connected ( state - > conn ) ) {
2011-07-18 16:35:03 +04:00
return NT_STATUS_CONNECTION_DISCONNECTED ;
}
2011-08-11 15:29:59 +04:00
if ( state - > conn - > protocol > PROTOCOL_NT1 ) {
return NT_STATUS_REVISION_MISMATCH ;
}
if ( iov_count < 4 ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
if ( iov [ 0 ] . iov_len ! = NBT_HDR_SIZE ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
if ( iov [ 1 ] . iov_len ! = ( MIN_SMB_SIZE - sizeof ( uint16_t ) ) ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
if ( iov [ 2 ] . iov_len > ( 0xFF * sizeof ( uint16_t ) ) ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
if ( iov [ 3 ] . iov_len ! = sizeof ( uint16_t ) ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
2011-07-18 16:35:03 +04:00
}
2011-11-25 17:49:24 +04:00
cmd = CVAL ( iov [ 1 ] . iov_base , HDR_COM ) ;
if ( cmd = = SMBreadBraw ) {
if ( smbXcli_conn_has_async_calls ( state - > conn ) ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
state - > conn - > smb1 . read_braw_req = req ;
}
2011-08-11 15:29:59 +04:00
if ( state - > smb1 . mid ! = 0 ) {
mid = state - > smb1 . mid ;
2011-07-18 16:35:03 +04:00
} else {
2011-08-11 15:29:59 +04:00
mid = smb1cli_alloc_mid ( state - > conn ) ;
2011-07-18 16:35:03 +04:00
}
2011-08-11 15:29:59 +04:00
SSVAL ( iov [ 1 ] . iov_base , HDR_MID , mid ) ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
_smb_setlen_nbt ( iov [ 0 ] . iov_base , smbXcli_iov_len ( & iov [ 1 ] , iov_count - 1 ) ) ;
2011-07-18 16:35:03 +04:00
2011-08-11 15:29:59 +04:00
status = smb1cli_conn_signv ( state - > conn , iov , iov_count ,
2011-11-18 12:24:11 +04:00
& state - > smb1 . seqnum ,
state - > smb1 . one_way_seqnum ) ;
2011-07-18 16:35:03 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2011-08-11 15:29:59 +04:00
/*
* If we supported multiple encrytion contexts
* here we ' d look up based on tid .
*/
if ( common_encryption_on ( state - > conn - > smb1 . trans_enc ) ) {
2011-07-18 16:35:03 +04:00
char * buf , * enc_buf ;
2011-08-11 15:29:59 +04:00
buf = ( char * ) smbXcli_iov_concat ( talloc_tos ( ) , iov , iov_count ) ;
2011-07-18 16:35:03 +04:00
if ( buf = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2011-08-11 15:29:59 +04:00
status = common_encrypt_buffer ( state - > conn - > smb1 . trans_enc ,
2011-07-18 16:35:03 +04:00
( char * ) buf , & enc_buf ) ;
TALLOC_FREE ( buf ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " Error in encrypting client message: %s \n " ,
nt_errstr ( status ) ) ) ;
return status ;
}
buf = ( char * ) talloc_memdup ( state , enc_buf ,
smb_len_nbt ( enc_buf ) + 4 ) ;
SAFE_FREE ( enc_buf ) ;
if ( buf = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
iov [ 0 ] . iov_base = ( void * ) buf ;
iov [ 0 ] . iov_len = talloc_get_size ( buf ) ;
iov_count = 1 ;
}
2011-08-11 15:29:59 +04:00
if ( state - > conn - > dispatch_incoming = = NULL ) {
state - > conn - > dispatch_incoming = smb1cli_conn_dispatch_incoming ;
}
2011-09-24 08:06:46 +04:00
tevent_req_set_cancel_fn ( req , smbXcli_req_cancel ) ;
2011-08-11 15:29:59 +04:00
subreq = writev_send ( state , state - > ev , state - > conn - > outgoing ,
2011-09-25 05:15:54 +04:00
state - > conn - > write_fd , false , iov , iov_count ) ;
2011-07-18 16:35:03 +04:00
if ( subreq = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2011-08-11 15:29:59 +04:00
tevent_req_set_callback ( subreq , smb1cli_req_writev_done , req ) ;
2011-07-18 16:35:03 +04:00
return NT_STATUS_OK ;
}
2011-08-11 15:29:59 +04:00
struct tevent_req * smb1cli_req_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct smbXcli_conn * conn ,
uint8_t smb_command ,
uint8_t additional_flags ,
uint8_t clear_flags ,
uint16_t additional_flags2 ,
uint16_t clear_flags2 ,
uint32_t timeout_msec ,
uint32_t pid ,
2012-07-23 23:34:58 +04:00
struct smbXcli_tcon * tcon ,
2012-07-23 22:15:21 +04:00
struct smbXcli_session * session ,
2011-08-11 15:29:59 +04:00
uint8_t wct , uint16_t * vwv ,
uint32_t num_bytes ,
const uint8_t * bytes )
2011-07-18 16:35:03 +04:00
{
struct tevent_req * req ;
struct iovec iov ;
NTSTATUS status ;
iov . iov_base = discard_const_p ( void , bytes ) ;
iov . iov_len = num_bytes ;
2011-08-11 15:29:59 +04:00
req = smb1cli_req_create ( mem_ctx , ev , conn , smb_command ,
additional_flags , clear_flags ,
additional_flags2 , clear_flags2 ,
timeout_msec ,
2012-07-23 23:34:58 +04:00
pid , tcon , session ,
2011-08-11 15:29:59 +04:00
wct , vwv , 1 , & iov ) ;
2011-07-18 16:35:03 +04:00
if ( req = = NULL ) {
return NULL ;
}
if ( ! tevent_req_is_in_progress ( req ) ) {
return tevent_req_post ( req , ev ) ;
}
2011-08-11 15:29:59 +04:00
status = smb1cli_req_chain_submit ( & req , 1 ) ;
if ( tevent_req_nterror ( req , status ) ) {
2011-07-18 16:35:03 +04:00
return tevent_req_post ( req , ev ) ;
}
return req ;
}
2011-08-11 15:29:59 +04:00
static void smb1cli_req_writev_done ( struct tevent_req * subreq )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
2011-07-18 16:35:03 +04:00
ssize_t nwritten ;
int err ;
nwritten = writev_recv ( subreq , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( nwritten = = - 1 ) {
NTSTATUS status = map_nt_error_from_unix_common ( err ) ;
2011-08-11 15:29:59 +04:00
smbXcli_conn_disconnect ( state - > conn , status ) ;
2011-07-18 16:35:03 +04:00
return ;
}
if ( state - > one_way ) {
state - > inbuf = NULL ;
tevent_req_done ( req ) ;
return ;
}
2011-08-11 15:29:59 +04:00
if ( ! smbXcli_req_set_pending ( req ) ) {
2011-07-18 16:35:03 +04:00
tevent_req_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
}
2011-08-11 15:29:59 +04:00
static void smbXcli_conn_received ( struct tevent_req * subreq )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
struct smbXcli_conn * conn =
tevent_req_callback_data ( subreq ,
struct smbXcli_conn ) ;
2011-07-18 16:35:03 +04:00
TALLOC_CTX * frame = talloc_stackframe ( ) ;
NTSTATUS status ;
uint8_t * inbuf ;
ssize_t received ;
int err ;
2011-08-11 15:29:59 +04:00
if ( subreq ! = conn - > read_smb_req ) {
2011-07-18 16:35:03 +04:00
DEBUG ( 1 , ( " Internal error: cli_smb_received called with "
" unexpected subreq \n " ) ) ;
2013-06-09 20:02:27 +04:00
smbXcli_conn_disconnect ( conn , NT_STATUS_INTERNAL_ERROR ) ;
2011-07-18 16:35:03 +04:00
TALLOC_FREE ( frame ) ;
return ;
}
2011-08-11 15:29:59 +04:00
conn - > read_smb_req = NULL ;
2011-07-18 16:35:03 +04:00
received = read_smb_recv ( subreq , frame , & inbuf , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( received = = - 1 ) {
status = map_nt_error_from_unix_common ( err ) ;
2011-08-11 15:29:59 +04:00
smbXcli_conn_disconnect ( conn , status ) ;
2011-07-18 16:35:03 +04:00
TALLOC_FREE ( frame ) ;
return ;
}
2011-08-11 15:29:59 +04:00
status = conn - > dispatch_incoming ( conn , frame , inbuf ) ;
2011-07-18 16:35:03 +04:00
TALLOC_FREE ( frame ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
/*
* We should not do any more processing
* as the dispatch function called
* tevent_req_done ( ) .
*/
return ;
2013-06-09 13:41:32 +04:00
}
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_RETRY ) ) {
2011-07-18 16:35:03 +04:00
/*
* We got an error , so notify all pending requests
*/
2011-08-11 15:29:59 +04:00
smbXcli_conn_disconnect ( conn , status ) ;
2011-07-18 16:35:03 +04:00
return ;
}
/*
* We got NT_STATUS_RETRY , so we may ask for a
* next incoming pdu .
*/
2011-08-11 15:29:59 +04:00
if ( ! smbXcli_conn_receive_next ( conn ) ) {
smbXcli_conn_disconnect ( conn , NT_STATUS_NO_MEMORY ) ;
2011-07-18 16:35:03 +04:00
}
}
2011-11-16 14:20:15 +04:00
static NTSTATUS smb1cli_inbuf_parse_chain ( uint8_t * buf , TALLOC_CTX * mem_ctx ,
struct iovec * * piov , int * pnum_iov )
{
struct iovec * iov ;
int num_iov ;
size_t buflen ;
size_t taken ;
size_t remaining ;
uint8_t * hdr ;
uint8_t cmd ;
uint32_t wct_ofs ;
2012-08-16 08:55:36 +04:00
NTSTATUS status ;
size_t min_size = MIN_SMB_SIZE ;
2011-11-16 14:20:15 +04:00
2013-03-14 02:23:52 +04:00
buflen = smb_len_tcp ( buf ) ;
2011-11-16 14:20:15 +04:00
taken = 0 ;
hdr = buf + NBT_HDR_SIZE ;
2012-08-16 08:55:36 +04:00
status = smb1cli_pull_raw_error ( hdr ) ;
if ( NT_STATUS_IS_ERR ( status ) ) {
/*
* This is an ugly hack to support OS / 2
* which skips the byte_count in the DATA block
* on some error responses .
*
* See bug # 9096
*/
min_size - = sizeof ( uint16_t ) ;
}
if ( buflen < min_size ) {
2011-11-16 14:20:15 +04:00
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
/*
* This returns iovec elements in the following order :
*
* - SMB header
*
* - Parameter Block
* - Data Block
*
* - Parameter Block
* - Data Block
*
* - Parameter Block
* - Data Block
*/
num_iov = 1 ;
iov = talloc_array ( mem_ctx , struct iovec , num_iov ) ;
if ( iov = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
iov [ 0 ] . iov_base = hdr ;
iov [ 0 ] . iov_len = HDR_WCT ;
taken + = HDR_WCT ;
cmd = CVAL ( hdr , HDR_COM ) ;
wct_ofs = HDR_WCT ;
while ( true ) {
size_t len = buflen - taken ;
struct iovec * cur ;
struct iovec * iov_tmp ;
uint8_t wct ;
uint32_t bcc_ofs ;
uint16_t bcc ;
size_t needed ;
/*
2012-08-16 08:55:36 +04:00
* we need at least WCT
2011-11-16 14:20:15 +04:00
*/
2012-08-16 08:55:36 +04:00
needed = sizeof ( uint8_t ) ;
2011-11-16 14:20:15 +04:00
if ( len < needed ) {
DEBUG ( 10 , ( " %s: %d bytes left, expected at least %d \n " ,
__location__ , ( int ) len , ( int ) needed ) ) ;
goto inval ;
}
/*
* Now we check if the specified words are there
*/
wct = CVAL ( hdr , wct_ofs ) ;
needed + = wct * sizeof ( uint16_t ) ;
if ( len < needed ) {
DEBUG ( 10 , ( " %s: %d bytes left, expected at least %d \n " ,
__location__ , ( int ) len , ( int ) needed ) ) ;
goto inval ;
}
2012-08-16 08:55:36 +04:00
if ( ( num_iov = = 1 ) & &
( len = = needed ) & &
NT_STATUS_IS_ERR ( status ) )
{
/*
* This is an ugly hack to support OS / 2
* which skips the byte_count in the DATA block
* on some error responses .
*
* See bug # 9096
*/
iov_tmp = talloc_realloc ( mem_ctx , iov , struct iovec ,
num_iov + 2 ) ;
if ( iov_tmp = = NULL ) {
TALLOC_FREE ( iov ) ;
return NT_STATUS_NO_MEMORY ;
}
iov = iov_tmp ;
cur = & iov [ num_iov ] ;
num_iov + = 2 ;
cur [ 0 ] . iov_len = 0 ;
cur [ 0 ] . iov_base = hdr + ( wct_ofs + sizeof ( uint8_t ) ) ;
cur [ 1 ] . iov_len = 0 ;
cur [ 1 ] . iov_base = cur [ 0 ] . iov_base ;
taken + = needed ;
break ;
}
/*
* we need at least BCC
*/
needed + = sizeof ( uint16_t ) ;
if ( len < needed ) {
DEBUG ( 10 , ( " %s: %d bytes left, expected at least %d \n " ,
__location__ , ( int ) len , ( int ) needed ) ) ;
goto inval ;
}
2011-11-16 14:20:15 +04:00
/*
* Now we check if the specified bytes are there
*/
bcc_ofs = wct_ofs + sizeof ( uint8_t ) + wct * sizeof ( uint16_t ) ;
bcc = SVAL ( hdr , bcc_ofs ) ;
needed + = bcc * sizeof ( uint8_t ) ;
if ( len < needed ) {
DEBUG ( 10 , ( " %s: %d bytes left, expected at least %d \n " ,
__location__ , ( int ) len , ( int ) needed ) ) ;
goto inval ;
}
/*
* we allocate 2 iovec structures for words and bytes
*/
iov_tmp = talloc_realloc ( mem_ctx , iov , struct iovec ,
num_iov + 2 ) ;
if ( iov_tmp = = NULL ) {
TALLOC_FREE ( iov ) ;
return NT_STATUS_NO_MEMORY ;
}
iov = iov_tmp ;
cur = & iov [ num_iov ] ;
num_iov + = 2 ;
cur [ 0 ] . iov_len = wct * sizeof ( uint16_t ) ;
cur [ 0 ] . iov_base = hdr + ( wct_ofs + sizeof ( uint8_t ) ) ;
cur [ 1 ] . iov_len = bcc * sizeof ( uint8_t ) ;
cur [ 1 ] . iov_base = hdr + ( bcc_ofs + sizeof ( uint16_t ) ) ;
taken + = needed ;
if ( ! smb1cli_is_andx_req ( cmd ) ) {
/*
* If the current command does not have AndX chanining
* we are done .
*/
break ;
}
if ( wct = = 0 & & bcc = = 0 ) {
/*
* An empty response also ends the chain ,
* most likely with an error .
*/
break ;
}
if ( wct < 2 ) {
DEBUG ( 10 , ( " %s: wct[%d] < 2 for cmd[0x%02X] \n " ,
__location__ , ( int ) wct , ( int ) cmd ) ) ;
goto inval ;
}
cmd = CVAL ( cur [ 0 ] . iov_base , 0 ) ;
if ( cmd = = 0xFF ) {
/*
* If it is the end of the chain we are also done .
*/
break ;
}
wct_ofs = SVAL ( cur [ 0 ] . iov_base , 2 ) ;
if ( wct_ofs < taken ) {
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
if ( wct_ofs > buflen ) {
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
/*
* we consumed everything up to the start of the next
* parameter block .
*/
taken = wct_ofs ;
}
remaining = buflen - taken ;
if ( remaining > 0 & & num_iov > = 3 ) {
/*
* The last DATA block gets the remaining
* bytes , this is needed to support
* CAP_LARGE_WRITEX and CAP_LARGE_READX .
*/
iov [ num_iov - 1 ] . iov_len + = remaining ;
}
* piov = iov ;
* pnum_iov = num_iov ;
return NT_STATUS_OK ;
inval :
TALLOC_FREE ( iov ) ;
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
2011-08-11 15:29:59 +04:00
static NTSTATUS smb1cli_conn_dispatch_incoming ( struct smbXcli_conn * conn ,
TALLOC_CTX * tmp_mem ,
uint8_t * inbuf )
2011-07-18 16:35:03 +04:00
{
struct tevent_req * req ;
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * state ;
2011-07-18 16:35:03 +04:00
NTSTATUS status ;
2011-08-11 15:29:59 +04:00
size_t num_pending ;
size_t i ;
2011-11-16 14:35:50 +04:00
uint8_t cmd ;
2011-07-18 16:35:03 +04:00
uint16_t mid ;
bool oplock_break ;
2012-08-03 11:58:29 +04:00
uint8_t * inhdr = inbuf + NBT_HDR_SIZE ;
2013-03-14 02:23:52 +04:00
size_t len = smb_len_tcp ( inbuf ) ;
2011-11-16 14:35:50 +04:00
struct iovec * iov = NULL ;
int num_iov = 0 ;
2011-11-21 16:08:58 +04:00
struct tevent_req * * chain = NULL ;
size_t num_chained = 0 ;
size_t num_responses = 0 ;
2011-07-18 16:35:03 +04:00
2011-11-25 17:49:24 +04:00
if ( conn - > smb1 . read_braw_req ! = NULL ) {
req = conn - > smb1 . read_braw_req ;
conn - > smb1 . read_braw_req = NULL ;
state = tevent_req_data ( req , struct smbXcli_req_state ) ;
smbXcli_req_unset_pending ( req ) ;
if ( state - > smb1 . recv_iov = = NULL ) {
/*
* For requests with more than
* one response , we have to readd the
* recv_iov array .
*/
state - > smb1 . recv_iov = talloc_zero_array ( state ,
struct iovec ,
3 ) ;
if ( tevent_req_nomem ( state - > smb1 . recv_iov , req ) ) {
return NT_STATUS_OK ;
}
}
2012-08-03 11:58:29 +04:00
state - > smb1 . recv_iov [ 0 ] . iov_base = ( void * ) ( inhdr ) ;
state - > smb1 . recv_iov [ 0 ] . iov_len = len ;
2011-11-25 17:49:24 +04:00
ZERO_STRUCT ( state - > smb1 . recv_iov [ 1 ] ) ;
ZERO_STRUCT ( state - > smb1 . recv_iov [ 2 ] ) ;
state - > smb1 . recv_cmd = SMBreadBraw ;
state - > smb1 . recv_status = NT_STATUS_OK ;
state - > inbuf = talloc_move ( state - > smb1 . recv_iov , & inbuf ) ;
tevent_req_done ( req ) ;
return NT_STATUS_OK ;
}
2011-07-18 16:35:03 +04:00
if ( ( IVAL ( inhdr , 0 ) ! = SMB_MAGIC ) /* 0xFF"SMB" */
& & ( SVAL ( inhdr , 0 ) ! = 0x45ff ) ) /* 0xFF"E" */ {
DEBUG ( 10 , ( " Got non-SMB PDU \n " ) ) ;
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
2011-08-11 15:29:59 +04:00
/*
* If we supported multiple encrytion contexts
* here we ' d look up based on tid .
*/
if ( common_encryption_on ( conn - > smb1 . trans_enc )
& & ( CVAL ( inbuf , 0 ) = = 0 ) ) {
2011-07-18 16:35:03 +04:00
uint16_t enc_ctx_num ;
status = get_enc_ctx_num ( inbuf , & enc_ctx_num ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " get_enc_ctx_num returned %s \n " ,
nt_errstr ( status ) ) ) ;
return status ;
}
2011-08-11 15:29:59 +04:00
if ( enc_ctx_num ! = conn - > smb1 . trans_enc - > enc_ctx_num ) {
2011-07-18 16:35:03 +04:00
DEBUG ( 10 , ( " wrong enc_ctx %d, expected %d \n " ,
enc_ctx_num ,
2011-08-11 15:29:59 +04:00
conn - > smb1 . trans_enc - > enc_ctx_num ) ) ;
2011-07-18 16:35:03 +04:00
return NT_STATUS_INVALID_HANDLE ;
}
2011-08-11 15:29:59 +04:00
status = common_decrypt_buffer ( conn - > smb1 . trans_enc ,
2011-07-18 16:35:03 +04:00
( char * ) inbuf ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " common_decrypt_buffer returned %s \n " ,
nt_errstr ( status ) ) ) ;
return status ;
}
2012-08-03 11:58:29 +04:00
inhdr = inbuf + NBT_HDR_SIZE ;
len = smb_len_nbt ( inbuf ) ;
2011-07-18 16:35:03 +04:00
}
mid = SVAL ( inhdr , HDR_MID ) ;
2011-08-11 15:29:59 +04:00
num_pending = talloc_array_length ( conn - > pending ) ;
2011-07-18 16:35:03 +04:00
for ( i = 0 ; i < num_pending ; i + + ) {
2011-08-11 15:29:59 +04:00
if ( mid = = smb1cli_req_mid ( conn - > pending [ i ] ) ) {
2011-07-18 16:35:03 +04:00
break ;
}
}
if ( i = = num_pending ) {
/* Dump unexpected reply */
return NT_STATUS_RETRY ;
}
oplock_break = false ;
if ( mid = = 0xffff ) {
/*
* Paranoia checks that this is really an oplock break request .
*/
2012-08-03 11:58:29 +04:00
oplock_break = ( len = = 51 ) ; /* hdr + 8 words */
2011-07-18 16:35:03 +04:00
oplock_break & = ( ( CVAL ( inhdr , HDR_FLG ) & FLAG_REPLY ) = = 0 ) ;
oplock_break & = ( CVAL ( inhdr , HDR_COM ) = = SMBlockingX ) ;
oplock_break & = ( SVAL ( inhdr , HDR_VWV + VWV ( 6 ) ) = = 0 ) ;
oplock_break & = ( SVAL ( inhdr , HDR_VWV + VWV ( 7 ) ) = = 0 ) ;
if ( ! oplock_break ) {
/* Dump unexpected reply */
return NT_STATUS_RETRY ;
}
}
2011-08-11 15:29:59 +04:00
req = conn - > pending [ i ] ;
state = tevent_req_data ( req , struct smbXcli_req_state ) ;
2011-07-18 16:35:03 +04:00
if ( ! oplock_break /* oplock breaks are not signed */
2011-08-11 15:29:59 +04:00
& & ! smb_signing_check_pdu ( conn - > smb1 . signing ,
2012-08-03 11:58:29 +04:00
inhdr , len , state - > smb1 . seqnum + 1 ) ) {
2011-07-18 16:35:03 +04:00
DEBUG ( 10 , ( " cli_check_sign_mac failed \n " ) ) ;
return NT_STATUS_ACCESS_DENIED ;
}
2011-11-16 14:35:50 +04:00
status = smb1cli_inbuf_parse_chain ( inbuf , tmp_mem ,
& iov , & num_iov ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " smb1cli_inbuf_parse_chain - %s \n " ,
nt_errstr ( status ) ) ) ;
return status ;
}
cmd = CVAL ( inhdr , HDR_COM ) ;
status = smb1cli_pull_raw_error ( inhdr ) ;
2012-10-22 16:18:20 +04:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_SESSION_EXPIRED ) & &
( state - > session ! = NULL ) & & state - > session - > disconnect_expired )
{
/*
* this should be a short term hack
* until the upper layers have implemented
* re - authentication .
*/
return status ;
}
2011-11-21 16:08:58 +04:00
if ( state - > smb1 . chained_requests = = NULL ) {
if ( num_iov ! = 3 ) {
2011-11-16 14:35:50 +04:00
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
2011-07-18 16:35:03 +04:00
2011-11-21 16:08:58 +04:00
smbXcli_req_unset_pending ( req ) ;
2011-07-18 16:35:03 +04:00
2011-11-28 20:48:44 +04:00
if ( state - > smb1 . recv_iov = = NULL ) {
/*
* For requests with more than
* one response , we have to readd the
* recv_iov array .
*/
state - > smb1 . recv_iov = talloc_zero_array ( state ,
struct iovec ,
3 ) ;
if ( tevent_req_nomem ( state - > smb1 . recv_iov , req ) ) {
return NT_STATUS_OK ;
}
}
2011-11-21 16:08:58 +04:00
state - > smb1 . recv_cmd = cmd ;
state - > smb1 . recv_status = status ;
state - > inbuf = talloc_move ( state - > smb1 . recv_iov , & inbuf ) ;
2011-07-18 16:35:03 +04:00
2011-11-21 16:08:58 +04:00
state - > smb1 . recv_iov [ 0 ] = iov [ 0 ] ;
state - > smb1 . recv_iov [ 1 ] = iov [ 1 ] ;
state - > smb1 . recv_iov [ 2 ] = iov [ 2 ] ;
2011-07-18 16:35:03 +04:00
2011-11-21 16:08:58 +04:00
if ( talloc_array_length ( conn - > pending ) = = 0 ) {
tevent_req_done ( req ) ;
return NT_STATUS_OK ;
}
2011-07-18 16:35:03 +04:00
2011-11-21 16:08:58 +04:00
tevent_req_defer_callback ( req , state - > ev ) ;
tevent_req_done ( req ) ;
return NT_STATUS_RETRY ;
}
2011-07-18 16:35:03 +04:00
2011-11-21 16:08:58 +04:00
chain = talloc_move ( tmp_mem , & state - > smb1 . chained_requests ) ;
num_chained = talloc_array_length ( chain ) ;
num_responses = ( num_iov - 1 ) / 2 ;
2011-11-16 14:35:50 +04:00
2011-11-21 16:08:58 +04:00
if ( num_responses > num_chained ) {
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
2011-11-16 14:35:50 +04:00
2011-11-21 16:08:58 +04:00
for ( i = 0 ; i < num_chained ; i + + ) {
size_t iov_idx = 1 + ( i * 2 ) ;
struct iovec * cur = & iov [ iov_idx ] ;
uint8_t * inbuf_ref ;
2011-07-18 16:35:03 +04:00
2011-11-21 16:08:58 +04:00
req = chain [ i ] ;
state = tevent_req_data ( req , struct smbXcli_req_state ) ;
2011-11-16 14:35:50 +04:00
2011-11-21 16:08:58 +04:00
smbXcli_req_unset_pending ( req ) ;
2011-11-16 14:35:50 +04:00
2011-11-21 16:08:58 +04:00
/*
* as we finish multiple requests here
* we need to defer the callbacks as
* they could destroy our current stack state .
*/
tevent_req_defer_callback ( req , state - > ev ) ;
if ( i > = num_responses ) {
tevent_req_nterror ( req , NT_STATUS_REQUEST_ABORTED ) ;
continue ;
2011-07-18 16:35:03 +04:00
}
2011-11-28 20:48:44 +04:00
if ( state - > smb1 . recv_iov = = NULL ) {
/*
* For requests with more than
* one response , we have to readd the
* recv_iov array .
*/
state - > smb1 . recv_iov = talloc_zero_array ( state ,
struct iovec ,
3 ) ;
if ( tevent_req_nomem ( state - > smb1 . recv_iov , req ) ) {
continue ;
}
}
2011-11-21 16:08:58 +04:00
state - > smb1 . recv_cmd = cmd ;
2011-11-16 14:35:50 +04:00
2011-11-21 16:08:58 +04:00
if ( i = = ( num_responses - 1 ) ) {
/*
* The last request in the chain gets the status
*/
state - > smb1 . recv_status = status ;
} else {
cmd = CVAL ( cur [ 0 ] . iov_base , 0 ) ;
state - > smb1 . recv_status = NT_STATUS_OK ;
}
2011-07-18 16:35:03 +04:00
2011-11-21 16:08:58 +04:00
state - > inbuf = inbuf ;
2011-07-18 16:35:03 +04:00
2011-11-21 16:08:58 +04:00
/*
* Note : here we use talloc_reference ( ) in a way
* that does not expose it to the caller .
*/
inbuf_ref = talloc_reference ( state - > smb1 . recv_iov , inbuf ) ;
if ( tevent_req_nomem ( inbuf_ref , req ) ) {
continue ;
}
/* copy the related buffers */
state - > smb1 . recv_iov [ 0 ] = iov [ 0 ] ;
state - > smb1 . recv_iov [ 1 ] = cur [ 0 ] ;
state - > smb1 . recv_iov [ 2 ] = cur [ 1 ] ;
2011-11-16 14:35:50 +04:00
2011-07-18 16:35:03 +04:00
tevent_req_done ( req ) ;
}
return NT_STATUS_RETRY ;
}
2011-08-11 15:29:59 +04:00
NTSTATUS smb1cli_req_recv ( struct tevent_req * req ,
2011-11-16 14:37:05 +04:00
TALLOC_CTX * mem_ctx ,
struct iovec * * piov ,
uint8_t * * phdr ,
uint8_t * pwct ,
uint16_t * * pvwv ,
uint32_t * pvwv_offset ,
uint32_t * pnum_bytes ,
uint8_t * * pbytes ,
uint32_t * pbytes_offset ,
uint8_t * * pinbuf ,
const struct smb1cli_req_expected_response * expected ,
size_t num_expected )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
2011-07-18 16:35:03 +04:00
NTSTATUS status = NT_STATUS_OK ;
2011-11-16 14:37:05 +04:00
struct iovec * recv_iov = NULL ;
uint8_t * hdr = NULL ;
uint8_t wct = 0 ;
uint32_t vwv_offset = 0 ;
uint16_t * vwv = NULL ;
uint32_t num_bytes = 0 ;
uint32_t bytes_offset = 0 ;
uint8_t * bytes = NULL ;
size_t i ;
bool found_status = false ;
bool found_size = false ;
2011-07-18 16:35:03 +04:00
2011-11-16 14:37:05 +04:00
if ( piov ! = NULL ) {
* piov = NULL ;
2011-07-18 16:35:03 +04:00
}
2011-11-16 14:37:05 +04:00
if ( phdr ! = NULL ) {
* phdr = 0 ;
}
if ( pwct ! = NULL ) {
* pwct = 0 ;
}
if ( pvwv ! = NULL ) {
* pvwv = NULL ;
}
if ( pvwv_offset ! = NULL ) {
* pvwv_offset = 0 ;
}
if ( pnum_bytes ! = NULL ) {
* pnum_bytes = 0 ;
}
if ( pbytes ! = NULL ) {
* pbytes = NULL ;
}
if ( pbytes_offset ! = NULL ) {
* pbytes_offset = 0 ;
}
if ( pinbuf ! = NULL ) {
* pinbuf = NULL ;
2011-07-18 16:35:03 +04:00
}
2011-11-16 14:37:05 +04:00
if ( state - > inbuf ! = NULL ) {
recv_iov = state - > smb1 . recv_iov ;
2011-11-28 20:48:44 +04:00
state - > smb1 . recv_iov = NULL ;
2011-11-25 17:49:24 +04:00
if ( state - > smb1 . recv_cmd ! = SMBreadBraw ) {
hdr = ( uint8_t * ) recv_iov [ 0 ] . iov_base ;
wct = recv_iov [ 1 ] . iov_len / 2 ;
vwv = ( uint16_t * ) recv_iov [ 1 ] . iov_base ;
vwv_offset = PTR_DIFF ( vwv , hdr ) ;
num_bytes = recv_iov [ 2 ] . iov_len ;
bytes = ( uint8_t * ) recv_iov [ 2 ] . iov_base ;
bytes_offset = PTR_DIFF ( bytes , hdr ) ;
}
2011-11-16 14:37:05 +04:00
}
2011-07-18 16:35:03 +04:00
2011-11-16 14:37:05 +04:00
if ( tevent_req_is_nterror ( req , & status ) ) {
for ( i = 0 ; i < num_expected ; i + + ) {
if ( NT_STATUS_EQUAL ( status , expected [ i ] . status ) ) {
found_status = true ;
break ;
2011-07-18 16:35:03 +04:00
}
}
2011-11-16 14:37:05 +04:00
if ( found_status ) {
return NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
2011-07-18 16:35:03 +04:00
}
2011-11-16 14:37:05 +04:00
return status ;
}
2011-07-18 16:35:03 +04:00
2011-11-16 14:37:05 +04:00
if ( num_expected = = 0 ) {
found_status = true ;
found_size = true ;
2011-07-18 16:35:03 +04:00
}
2011-11-16 14:37:05 +04:00
status = state - > smb1 . recv_status ;
2011-07-18 16:35:03 +04:00
2011-11-16 14:37:05 +04:00
for ( i = 0 ; i < num_expected ; i + + ) {
if ( ! NT_STATUS_EQUAL ( status , expected [ i ] . status ) ) {
continue ;
}
2011-07-18 16:35:03 +04:00
2011-11-16 14:37:05 +04:00
found_status = true ;
if ( expected [ i ] . wct = = 0 ) {
found_size = true ;
break ;
2011-07-18 16:35:03 +04:00
}
2011-11-16 14:37:05 +04:00
if ( expected [ i ] . wct = = wct ) {
found_size = true ;
break ;
2011-07-18 16:35:03 +04:00
}
}
2011-11-16 14:37:05 +04:00
if ( ! found_status ) {
return status ;
}
2011-07-18 16:35:03 +04:00
2011-11-16 14:37:05 +04:00
if ( ! found_size ) {
2011-07-18 16:35:03 +04:00
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
2011-11-16 14:37:05 +04:00
if ( piov ! = NULL ) {
* piov = talloc_move ( mem_ctx , & recv_iov ) ;
2011-07-18 16:35:03 +04:00
}
2011-11-16 14:37:05 +04:00
if ( phdr ! = NULL ) {
* phdr = hdr ;
}
2011-07-18 16:35:03 +04:00
if ( pwct ! = NULL ) {
* pwct = wct ;
}
if ( pvwv ! = NULL ) {
2011-11-16 14:37:05 +04:00
* pvwv = vwv ;
}
if ( pvwv_offset ! = NULL ) {
* pvwv_offset = vwv_offset ;
2011-07-18 16:35:03 +04:00
}
if ( pnum_bytes ! = NULL ) {
* pnum_bytes = num_bytes ;
}
if ( pbytes ! = NULL ) {
2011-11-16 14:37:05 +04:00
* pbytes = bytes ;
2011-07-18 16:35:03 +04:00
}
2011-11-16 14:37:05 +04:00
if ( pbytes_offset ! = NULL ) {
* pbytes_offset = bytes_offset ;
}
if ( pinbuf ! = NULL ) {
* pinbuf = state - > inbuf ;
2011-07-18 16:35:03 +04:00
}
return status ;
}
2011-08-11 15:29:59 +04:00
size_t smb1cli_req_wct_ofs ( struct tevent_req * * reqs , int num_reqs )
2011-07-18 16:35:03 +04:00
{
size_t wct_ofs ;
int i ;
2011-08-11 15:29:59 +04:00
wct_ofs = HDR_WCT ;
2011-07-18 16:35:03 +04:00
for ( i = 0 ; i < num_reqs ; i + + ) {
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * state ;
state = tevent_req_data ( reqs [ i ] , struct smbXcli_req_state ) ;
wct_ofs + = smbXcli_iov_len ( state - > smb1 . iov + 2 ,
state - > smb1 . iov_count - 2 ) ;
2011-07-18 16:35:03 +04:00
wct_ofs = ( wct_ofs + 3 ) & ~ 3 ;
}
return wct_ofs ;
}
2011-08-11 15:29:59 +04:00
NTSTATUS smb1cli_req_chain_submit ( struct tevent_req * * reqs , int num_reqs )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * first_state =
tevent_req_data ( reqs [ 0 ] ,
struct smbXcli_req_state ) ;
struct smbXcli_req_state * state ;
2011-07-18 16:35:03 +04:00
size_t wct_offset ;
size_t chain_padding = 0 ;
int i , iovlen ;
struct iovec * iov = NULL ;
struct iovec * this_iov ;
NTSTATUS status ;
2011-08-11 15:29:59 +04:00
size_t nbt_len ;
if ( num_reqs = = 1 ) {
return smb1cli_req_writev_submit ( reqs [ 0 ] , first_state ,
first_state - > smb1 . iov ,
first_state - > smb1 . iov_count ) ;
}
2011-07-18 16:35:03 +04:00
iovlen = 0 ;
for ( i = 0 ; i < num_reqs ; i + + ) {
if ( ! tevent_req_is_in_progress ( reqs [ i ] ) ) {
return NT_STATUS_INTERNAL_ERROR ;
}
2011-08-11 15:29:59 +04:00
state = tevent_req_data ( reqs [ i ] , struct smbXcli_req_state ) ;
if ( state - > smb1 . iov_count < 4 ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
if ( i = = 0 ) {
/*
* The NBT and SMB header
*/
iovlen + = 2 ;
} else {
/*
* Chain padding
*/
iovlen + = 1 ;
}
/*
* words and bytes
*/
iovlen + = state - > smb1 . iov_count - 2 ;
2011-07-18 16:35:03 +04:00
}
2011-11-18 13:28:40 +04:00
iov = talloc_zero_array ( first_state , struct iovec , iovlen ) ;
2011-07-18 16:35:03 +04:00
if ( iov = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2011-08-11 15:29:59 +04:00
first_state - > smb1 . chained_requests = ( struct tevent_req * * ) talloc_memdup (
2011-11-18 13:28:40 +04:00
first_state , reqs , sizeof ( * reqs ) * num_reqs ) ;
2011-08-11 15:29:59 +04:00
if ( first_state - > smb1 . chained_requests = = NULL ) {
2011-07-18 16:35:03 +04:00
TALLOC_FREE ( iov ) ;
return NT_STATUS_NO_MEMORY ;
}
2011-08-11 15:29:59 +04:00
wct_offset = HDR_WCT ;
2011-07-18 16:35:03 +04:00
this_iov = iov ;
for ( i = 0 ; i < num_reqs ; i + + ) {
size_t next_padding = 0 ;
uint16_t * vwv ;
2011-08-11 15:29:59 +04:00
state = tevent_req_data ( reqs [ i ] , struct smbXcli_req_state ) ;
2011-07-18 16:35:03 +04:00
if ( i < num_reqs - 1 ) {
2011-08-11 15:29:59 +04:00
if ( ! smb1cli_is_andx_req ( CVAL ( state - > smb1 . hdr , HDR_COM ) )
| | CVAL ( state - > smb1 . hdr , HDR_WCT ) < 2 ) {
2011-07-18 16:35:03 +04:00
TALLOC_FREE ( iov ) ;
2011-08-11 15:29:59 +04:00
TALLOC_FREE ( first_state - > smb1 . chained_requests ) ;
return NT_STATUS_INVALID_PARAMETER_MIX ;
2011-07-18 16:35:03 +04:00
}
}
2011-08-11 15:29:59 +04:00
wct_offset + = smbXcli_iov_len ( state - > smb1 . iov + 2 ,
state - > smb1 . iov_count - 2 ) + 1 ;
2011-07-18 16:35:03 +04:00
if ( ( wct_offset % 4 ) ! = 0 ) {
next_padding = 4 - ( wct_offset % 4 ) ;
}
wct_offset + = next_padding ;
2011-08-11 15:29:59 +04:00
vwv = state - > smb1 . vwv ;
2011-07-18 16:35:03 +04:00
if ( i < num_reqs - 1 ) {
2011-08-11 15:29:59 +04:00
struct smbXcli_req_state * next_state =
tevent_req_data ( reqs [ i + 1 ] ,
struct smbXcli_req_state ) ;
SCVAL ( vwv + 0 , 0 , CVAL ( next_state - > smb1 . hdr , HDR_COM ) ) ;
2011-07-18 16:35:03 +04:00
SCVAL ( vwv + 0 , 1 , 0 ) ;
SSVAL ( vwv + 1 , 0 , wct_offset ) ;
2011-08-11 15:29:59 +04:00
} else if ( smb1cli_is_andx_req ( CVAL ( state - > smb1 . hdr , HDR_COM ) ) ) {
2011-07-18 16:35:03 +04:00
/* properly end the chain */
SCVAL ( vwv + 0 , 0 , 0xff ) ;
SCVAL ( vwv + 0 , 1 , 0xff ) ;
SSVAL ( vwv + 1 , 0 , 0 ) ;
}
if ( i = = 0 ) {
2011-08-11 15:29:59 +04:00
/*
* The NBT and SMB header
*/
this_iov [ 0 ] = state - > smb1 . iov [ 0 ] ;
this_iov [ 1 ] = state - > smb1 . iov [ 1 ] ;
this_iov + = 2 ;
2011-07-18 16:35:03 +04:00
} else {
/*
* This one is a bit subtle . We have to add
* chain_padding bytes between the requests , and we
* have to also include the wct field of the
* subsequent requests . We use the subsequent header
* for the padding , it contains the wct field in its
* last byte .
*/
this_iov [ 0 ] . iov_len = chain_padding + 1 ;
2011-08-11 15:29:59 +04:00
this_iov [ 0 ] . iov_base = ( void * ) & state - > smb1 . hdr [
sizeof ( state - > smb1 . hdr ) - this_iov [ 0 ] . iov_len ] ;
2011-07-18 16:35:03 +04:00
memset ( this_iov [ 0 ] . iov_base , 0 , this_iov [ 0 ] . iov_len - 1 ) ;
2011-08-11 15:29:59 +04:00
this_iov + = 1 ;
2011-07-18 16:35:03 +04:00
}
2011-08-11 15:29:59 +04:00
/*
* copy the words and bytes
*/
memcpy ( this_iov , state - > smb1 . iov + 2 ,
sizeof ( struct iovec ) * ( state - > smb1 . iov_count - 2 ) ) ;
this_iov + = state - > smb1 . iov_count - 2 ;
2011-07-18 16:35:03 +04:00
chain_padding = next_padding ;
}
2011-08-11 15:29:59 +04:00
nbt_len = smbXcli_iov_len ( & iov [ 1 ] , iovlen - 1 ) ;
if ( nbt_len > first_state - > conn - > smb1 . max_xmit ) {
TALLOC_FREE ( iov ) ;
TALLOC_FREE ( first_state - > smb1 . chained_requests ) ;
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
2011-11-18 13:28:40 +04:00
status = smb1cli_req_writev_submit ( reqs [ 0 ] , first_state , iov , iovlen ) ;
2011-07-18 16:35:03 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( iov ) ;
2011-08-11 15:29:59 +04:00
TALLOC_FREE ( first_state - > smb1 . chained_requests ) ;
2011-07-18 16:35:03 +04:00
return status ;
}
return NT_STATUS_OK ;
}
2011-08-11 15:29:59 +04:00
bool smbXcli_conn_has_async_calls ( struct smbXcli_conn * conn )
2011-07-18 16:35:03 +04:00
{
2011-08-11 15:29:59 +04:00
return ( ( tevent_queue_length ( conn - > outgoing ) ! = 0 )
| | ( talloc_array_length ( conn - > pending ) ! = 0 ) ) ;
2011-07-18 16:35:03 +04:00
}
2011-09-15 13:47:11 +04:00
2013-08-13 17:49:19 +04:00
bool smb2cli_conn_req_possible ( struct smbXcli_conn * conn , uint32_t * max_dyn_len )
{
uint16_t credits = 1 ;
if ( conn - > smb2 . cur_credits = = 0 ) {
if ( max_dyn_len ! = NULL ) {
* max_dyn_len = 0 ;
}
return false ;
}
if ( conn - > smb2 . server . capabilities & SMB2_CAP_LARGE_MTU ) {
credits = conn - > smb2 . cur_credits ;
}
if ( max_dyn_len ! = NULL ) {
* max_dyn_len = credits * 65536 ;
}
return true ;
}
2011-09-15 16:02:49 +04:00
uint32_t smb2cli_conn_server_capabilities ( struct smbXcli_conn * conn )
{
return conn - > smb2 . server . capabilities ;
}
uint16_t smb2cli_conn_server_security_mode ( struct smbXcli_conn * conn )
{
return conn - > smb2 . server . security_mode ;
}
uint32_t smb2cli_conn_max_trans_size ( struct smbXcli_conn * conn )
{
return conn - > smb2 . server . max_trans_size ;
}
uint32_t smb2cli_conn_max_read_size ( struct smbXcli_conn * conn )
{
return conn - > smb2 . server . max_read_size ;
}
uint32_t smb2cli_conn_max_write_size ( struct smbXcli_conn * conn )
{
return conn - > smb2 . server . max_write_size ;
}
2011-09-06 19:52:02 +04:00
void smb2cli_conn_set_max_credits ( struct smbXcli_conn * conn ,
uint16_t max_credits )
{
conn - > smb2 . max_credits = max_credits ;
}
2011-09-24 08:06:46 +04:00
static void smb2cli_req_cancel_done ( struct tevent_req * subreq ) ;
static bool smb2cli_req_cancel ( struct tevent_req * req )
{
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
uint32_t flags = IVAL ( state - > smb2 . hdr , SMB2_HDR_FLAGS ) ;
uint64_t mid = BVAL ( state - > smb2 . hdr , SMB2_HDR_MESSAGE_ID ) ;
uint64_t aid = BVAL ( state - > smb2 . hdr , SMB2_HDR_ASYNC_ID ) ;
2012-07-25 12:36:27 +04:00
struct smbXcli_tcon * tcon = state - > tcon ;
2011-09-24 08:06:46 +04:00
struct smbXcli_session * session = state - > session ;
uint8_t * fixed = state - > smb2 . pad ;
uint16_t fixed_len = 4 ;
struct tevent_req * subreq ;
struct smbXcli_req_state * substate ;
NTSTATUS status ;
SSVAL ( fixed , 0 , 0x04 ) ;
SSVAL ( fixed , 2 , 0 ) ;
subreq = smb2cli_req_create ( state , state - > ev ,
state - > conn ,
SMB2_OP_CANCEL ,
flags , 0 ,
0 , /* timeout */
2012-07-25 12:58:34 +04:00
tcon , session ,
2011-09-24 08:06:46 +04:00
fixed , fixed_len ,
2013-08-13 11:54:42 +04:00
NULL , 0 , 0 ) ;
2011-09-24 08:06:46 +04:00
if ( subreq = = NULL ) {
return false ;
}
substate = tevent_req_data ( subreq , struct smbXcli_req_state ) ;
2012-08-15 16:17:25 +04:00
/*
* clear everything but the SMB2_HDR_FLAG_ASYNC flag
* e . g . if SMB2_HDR_FLAG_CHAINED is set we get INVALID_PARAMETER back
*/
flags & = SMB2_HDR_FLAG_ASYNC ;
2011-09-24 08:06:46 +04:00
if ( flags & SMB2_HDR_FLAG_ASYNC ) {
mid = 0 ;
}
SIVAL ( substate - > smb2 . hdr , SMB2_HDR_FLAGS , flags ) ;
SBVAL ( substate - > smb2 . hdr , SMB2_HDR_MESSAGE_ID , mid ) ;
SBVAL ( substate - > smb2 . hdr , SMB2_HDR_ASYNC_ID , aid ) ;
status = smb2cli_req_compound_submit ( & subreq , 1 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( subreq ) ;
return false ;
}
tevent_req_set_callback ( subreq , smb2cli_req_cancel_done , NULL ) ;
return true ;
}
static void smb2cli_req_cancel_done ( struct tevent_req * subreq )
{
/* we do not care about the result */
TALLOC_FREE ( subreq ) ;
}
2011-09-15 13:47:11 +04:00
struct tevent_req * smb2cli_req_create ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
2011-09-15 14:23:05 +04:00
struct smbXcli_conn * conn ,
2011-09-15 13:47:11 +04:00
uint16_t cmd ,
uint32_t additional_flags ,
uint32_t clear_flags ,
2011-09-15 14:23:05 +04:00
uint32_t timeout_msec ,
2012-07-25 12:36:27 +04:00
struct smbXcli_tcon * tcon ,
2011-09-03 12:18:17 +04:00
struct smbXcli_session * session ,
2011-09-15 13:47:11 +04:00
const uint8_t * fixed ,
uint16_t fixed_len ,
const uint8_t * dyn ,
2013-08-13 11:54:42 +04:00
uint32_t dyn_len ,
uint32_t max_dyn_len )
2011-09-15 13:47:11 +04:00
{
struct tevent_req * req ;
2011-09-15 14:23:05 +04:00
struct smbXcli_req_state * state ;
2011-09-15 13:47:11 +04:00
uint32_t flags = 0 ;
2012-07-25 12:36:27 +04:00
uint32_t tid = 0 ;
2011-09-03 12:18:17 +04:00
uint64_t uid = 0 ;
2012-07-26 03:54:33 +04:00
bool use_channel_sequence = false ;
uint16_t channel_sequence = 0 ;
2011-09-15 13:47:11 +04:00
req = tevent_req_create ( mem_ctx , & state ,
2011-09-15 14:23:05 +04:00
struct smbXcli_req_state ) ;
2011-09-15 13:47:11 +04:00
if ( req = = NULL ) {
return NULL ;
}
2011-09-15 14:23:05 +04:00
2011-09-15 13:47:11 +04:00
state - > ev = ev ;
2011-09-15 14:23:05 +04:00
state - > conn = conn ;
2011-09-03 12:18:17 +04:00
state - > session = session ;
2012-07-25 12:36:27 +04:00
state - > tcon = tcon ;
2011-09-03 12:18:17 +04:00
2012-07-26 03:54:33 +04:00
if ( conn - > smb2 . server . capabilities & SMB2_CAP_PERSISTENT_HANDLES ) {
use_channel_sequence = true ;
} else if ( conn - > smb2 . server . capabilities & SMB2_CAP_MULTI_CHANNEL ) {
use_channel_sequence = true ;
}
2011-09-03 12:18:17 +04:00
if ( session ) {
2012-07-26 04:36:37 +04:00
uid = session - > smb2 - > session_id ;
2012-07-23 12:00:50 +04:00
2012-07-26 03:54:33 +04:00
if ( use_channel_sequence ) {
channel_sequence = session - > smb2 - > channel_sequence ;
}
2012-07-26 04:36:37 +04:00
state - > smb2 . should_sign = session - > smb2 - > should_sign ;
state - > smb2 . should_encrypt = session - > smb2 - > should_encrypt ;
2012-07-23 12:14:53 +04:00
2012-07-23 12:00:50 +04:00
if ( cmd = = SMB2_OP_SESSSETUP & &
2012-07-26 04:36:37 +04:00
session - > smb2 - > signing_key . length ! = 0 ) {
2012-07-23 12:00:50 +04:00
state - > smb2 . should_sign = true ;
}
2012-07-23 12:14:53 +04:00
2012-07-23 13:38:31 +04:00
if ( cmd = = SMB2_OP_SESSSETUP & &
2012-07-26 04:36:37 +04:00
session - > smb2_channel . signing_key . length = = 0 ) {
2012-07-23 12:14:53 +04:00
state - > smb2 . should_encrypt = false ;
}
2012-07-25 12:36:27 +04:00
}
2012-07-23 12:14:53 +04:00
2012-07-25 12:36:27 +04:00
if ( tcon ) {
tid = tcon - > smb2 . tcon_id ;
2012-07-25 14:34:20 +04:00
if ( tcon - > smb2 . should_encrypt ) {
state - > smb2 . should_encrypt = true ;
}
2012-07-25 12:36:27 +04:00
}
if ( state - > smb2 . should_encrypt ) {
state - > smb2 . should_sign = false ;
2011-09-03 12:18:17 +04:00
}
2011-09-15 13:47:11 +04:00
2011-09-15 14:23:05 +04:00
state - > smb2 . recv_iov = talloc_zero_array ( state , struct iovec , 3 ) ;
if ( state - > smb2 . recv_iov = = NULL ) {
2011-09-15 13:47:11 +04:00
TALLOC_FREE ( req ) ;
return NULL ;
}
flags | = additional_flags ;
flags & = ~ clear_flags ;
2011-09-15 14:23:05 +04:00
state - > smb2 . fixed = fixed ;
state - > smb2 . fixed_len = fixed_len ;
state - > smb2 . dyn = dyn ;
state - > smb2 . dyn_len = dyn_len ;
2013-08-13 11:54:42 +04:00
state - > smb2 . max_dyn_len = max_dyn_len ;
2011-09-15 14:23:05 +04:00
2012-07-23 12:14:53 +04:00
if ( state - > smb2 . should_encrypt ) {
SIVAL ( state - > smb2 . transform , SMB2_TF_PROTOCOL_ID , SMB2_TF_MAGIC ) ;
SBVAL ( state - > smb2 . transform , SMB2_TF_SESSION_ID , uid ) ;
}
2011-09-15 14:23:05 +04:00
SIVAL ( state - > smb2 . hdr , SMB2_HDR_PROTOCOL_ID , SMB2_MAGIC ) ;
SSVAL ( state - > smb2 . hdr , SMB2_HDR_LENGTH , SMB2_HDR_BODY ) ;
SSVAL ( state - > smb2 . hdr , SMB2_HDR_OPCODE , cmd ) ;
2012-07-26 03:54:33 +04:00
SSVAL ( state - > smb2 . hdr , SMB2_HDR_CHANNEL_SEQUENCE , channel_sequence ) ;
2011-09-15 14:23:05 +04:00
SIVAL ( state - > smb2 . hdr , SMB2_HDR_FLAGS , flags ) ;
2012-07-25 12:58:34 +04:00
SIVAL ( state - > smb2 . hdr , SMB2_HDR_PID , 0 ) ; /* reserved */
2011-09-15 14:23:05 +04:00
SIVAL ( state - > smb2 . hdr , SMB2_HDR_TID , tid ) ;
SBVAL ( state - > smb2 . hdr , SMB2_HDR_SESSION_ID , uid ) ;
switch ( cmd ) {
case SMB2_OP_CANCEL :
state - > one_way = true ;
break ;
2011-09-22 23:24:42 +04:00
case SMB2_OP_BREAK :
/*
* If this is a dummy request , it will have
* UINT64_MAX as message id .
* If we send on break acknowledgement ,
* this gets overwritten later .
*/
SBVAL ( state - > smb2 . hdr , SMB2_HDR_MESSAGE_ID , UINT64_MAX ) ;
break ;
2011-09-15 14:23:05 +04:00
}
if ( timeout_msec > 0 ) {
2011-09-15 13:47:11 +04:00
struct timeval endtime ;
2011-09-15 14:23:05 +04:00
endtime = timeval_current_ofs_msec ( timeout_msec ) ;
2011-09-15 13:47:11 +04:00
if ( ! tevent_req_set_endtime ( req , ev , endtime ) ) {
return req ;
}
}
return req ;
}
2011-10-27 19:20:27 +04:00
void smb2cli_req_set_notify_async ( struct tevent_req * req )
{
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
state - > smb2 . notify_async = true ;
}
2011-11-28 13:24:18 +04:00
static void smb2cli_req_writev_done ( struct tevent_req * subreq ) ;
2011-09-15 14:23:05 +04:00
static NTSTATUS smb2cli_conn_dispatch_incoming ( struct smbXcli_conn * conn ,
TALLOC_CTX * tmp_mem ,
uint8_t * inbuf ) ;
2011-09-15 13:47:11 +04:00
NTSTATUS smb2cli_req_compound_submit ( struct tevent_req * * reqs ,
int num_reqs )
{
2011-09-15 14:23:05 +04:00
struct smbXcli_req_state * state ;
2011-09-15 13:47:11 +04:00
struct tevent_req * subreq ;
struct iovec * iov ;
int i , num_iov , nbt_len ;
2012-08-14 11:30:43 +04:00
int tf_iov = - 1 ;
const DATA_BLOB * encryption_key = NULL ;
uint64_t encryption_session_id = 0 ;
2011-09-15 13:47:11 +04:00
/*
2012-08-14 11:30:43 +04:00
* 1 for the nbt length , optional TRANSFORM
* per request : HDR , fixed , dyn , padding
2011-09-15 13:47:11 +04:00
* - 1 because the last one does not need padding
*/
2012-08-14 11:30:43 +04:00
iov = talloc_array ( reqs [ 0 ] , struct iovec , 1 + 1 + 4 * num_reqs - 1 ) ;
2011-09-15 13:47:11 +04:00
if ( iov = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
num_iov = 1 ;
nbt_len = 0 ;
2012-08-14 11:30:43 +04:00
/*
* the session of the first request that requires encryption
* specifies the encryption key .
*/
for ( i = 0 ; i < num_reqs ; i + + ) {
if ( ! tevent_req_is_in_progress ( reqs [ i ] ) ) {
return NT_STATUS_INTERNAL_ERROR ;
}
state = tevent_req_data ( reqs [ i ] , struct smbXcli_req_state ) ;
if ( ! smbXcli_conn_is_connected ( state - > conn ) ) {
return NT_STATUS_CONNECTION_DISCONNECTED ;
}
if ( ( state - > conn - > protocol ! = PROTOCOL_NONE ) & &
( state - > conn - > protocol < PROTOCOL_SMB2_02 ) ) {
return NT_STATUS_REVISION_MISMATCH ;
}
if ( state - > session = = NULL ) {
continue ;
}
if ( ! state - > smb2 . should_encrypt ) {
continue ;
}
encryption_key = & state - > session - > smb2 - > encryption_key ;
if ( encryption_key - > length = = 0 ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
encryption_session_id = state - > session - > smb2 - > session_id ;
tf_iov = num_iov ;
iov [ num_iov ] . iov_base = state - > smb2 . transform ;
iov [ num_iov ] . iov_len = sizeof ( state - > smb2 . transform ) ;
num_iov + = 1 ;
SBVAL ( state - > smb2 . transform , SMB2_TF_PROTOCOL_ID , SMB2_TF_MAGIC ) ;
SBVAL ( state - > smb2 . transform , SMB2_TF_NONCE ,
state - > session - > smb2 - > nonce_low ) ;
SBVAL ( state - > smb2 . transform , SMB2_TF_NONCE + 8 ,
state - > session - > smb2 - > nonce_high ) ;
SBVAL ( state - > smb2 . transform , SMB2_TF_SESSION_ID ,
encryption_session_id ) ;
state - > session - > smb2 - > nonce_low + = 1 ;
if ( state - > session - > smb2 - > nonce_low = = 0 ) {
state - > session - > smb2 - > nonce_high + = 1 ;
state - > session - > smb2 - > nonce_low + = 1 ;
}
nbt_len + = SMB2_TF_HDR_SIZE ;
break ;
}
2011-09-15 13:47:11 +04:00
for ( i = 0 ; i < num_reqs ; i + + ) {
2011-09-03 12:18:32 +04:00
int hdr_iov ;
2011-09-15 13:47:11 +04:00
size_t reqlen ;
bool ret ;
2011-09-24 08:06:46 +04:00
uint16_t opcode ;
2011-09-06 19:52:02 +04:00
uint64_t avail ;
uint16_t charge ;
uint16_t credits ;
2011-09-15 13:47:11 +04:00
uint64_t mid ;
2012-02-27 11:48:55 +04:00
const DATA_BLOB * signing_key = NULL ;
2011-09-15 13:47:11 +04:00
if ( ! tevent_req_is_in_progress ( reqs [ i ] ) ) {
return NT_STATUS_INTERNAL_ERROR ;
}
2011-09-15 14:23:05 +04:00
state = tevent_req_data ( reqs [ i ] , struct smbXcli_req_state ) ;
2011-09-15 13:47:11 +04:00
2011-09-15 14:23:05 +04:00
if ( ! smbXcli_conn_is_connected ( state - > conn ) ) {
2011-09-15 13:47:11 +04:00
return NT_STATUS_CONNECTION_DISCONNECTED ;
}
2011-09-15 14:23:05 +04:00
if ( ( state - > conn - > protocol ! = PROTOCOL_NONE ) & &
( state - > conn - > protocol < PROTOCOL_SMB2_02 ) ) {
return NT_STATUS_REVISION_MISMATCH ;
}
2011-09-24 08:06:46 +04:00
opcode = SVAL ( state - > smb2 . hdr , SMB2_HDR_OPCODE ) ;
if ( opcode = = SMB2_OP_CANCEL ) {
goto skip_credits ;
}
2011-09-06 19:52:02 +04:00
avail = UINT64_MAX - state - > conn - > smb2 . mid ;
if ( avail < 1 ) {
2011-09-15 13:47:11 +04:00
return NT_STATUS_CONNECTION_ABORTED ;
}
2011-09-06 19:52:02 +04:00
if ( state - > conn - > smb2 . server . capabilities & SMB2_CAP_LARGE_MTU ) {
2013-08-13 11:50:29 +04:00
uint32_t max_dyn_len = 1 ;
max_dyn_len = MAX ( max_dyn_len , state - > smb2 . dyn_len ) ;
max_dyn_len = MAX ( max_dyn_len , state - > smb2 . max_dyn_len ) ;
charge = ( max_dyn_len - 1 ) / 65536 + 1 ;
2011-09-06 19:52:02 +04:00
} else {
charge = 1 ;
}
charge = MAX ( state - > smb2 . credit_charge , charge ) ;
avail = MIN ( avail , state - > conn - > smb2 . cur_credits ) ;
if ( avail < charge ) {
return NT_STATUS_INTERNAL_ERROR ;
}
credits = 0 ;
if ( state - > conn - > smb2 . max_credits > state - > conn - > smb2 . cur_credits ) {
credits = state - > conn - > smb2 . max_credits -
state - > conn - > smb2 . cur_credits ;
}
if ( state - > conn - > smb2 . max_credits > = state - > conn - > smb2 . cur_credits ) {
credits + = 1 ;
}
2011-09-15 14:23:05 +04:00
mid = state - > conn - > smb2 . mid ;
2011-09-06 19:52:02 +04:00
state - > conn - > smb2 . mid + = charge ;
state - > conn - > smb2 . cur_credits - = charge ;
2011-09-15 13:47:11 +04:00
2011-09-06 19:52:02 +04:00
if ( state - > conn - > smb2 . server . capabilities & SMB2_CAP_LARGE_MTU ) {
SSVAL ( state - > smb2 . hdr , SMB2_HDR_CREDIT_CHARGE , charge ) ;
}
SSVAL ( state - > smb2 . hdr , SMB2_HDR_CREDIT , credits ) ;
2011-09-15 14:23:05 +04:00
SBVAL ( state - > smb2 . hdr , SMB2_HDR_MESSAGE_ID , mid ) ;
2011-09-15 13:47:11 +04:00
2011-09-24 08:06:46 +04:00
skip_credits :
2012-08-14 11:30:43 +04:00
if ( state - > session & & encryption_key = = NULL ) {
2012-02-27 11:48:55 +04:00
/*
* We prefer the channel signing key if it is
* already there .
*/
2012-07-23 12:00:50 +04:00
if ( state - > smb2 . should_sign ) {
2012-07-26 04:36:37 +04:00
signing_key = & state - > session - > smb2_channel . signing_key ;
2012-02-27 11:48:55 +04:00
}
/*
* If it is a channel binding , we already have the main
* signing key and try that one .
*/
if ( signing_key & & signing_key - > length = = 0 ) {
2012-07-26 04:36:37 +04:00
signing_key = & state - > session - > smb2 - > signing_key ;
2012-02-27 11:48:55 +04:00
}
/*
* If we do not have any session key yet , we skip the
* signing of SMB2_OP_SESSSETUP requests .
*/
if ( signing_key & & signing_key - > length = = 0 ) {
signing_key = NULL ;
}
2011-09-19 21:05:20 +04:00
}
2012-07-19 13:16:16 +04:00
hdr_iov = num_iov ;
iov [ num_iov ] . iov_base = state - > smb2 . hdr ;
iov [ num_iov ] . iov_len = sizeof ( state - > smb2 . hdr ) ;
num_iov + = 1 ;
iov [ num_iov ] . iov_base = discard_const ( state - > smb2 . fixed ) ;
iov [ num_iov ] . iov_len = state - > smb2 . fixed_len ;
num_iov + = 1 ;
if ( state - > smb2 . dyn ! = NULL ) {
iov [ num_iov ] . iov_base = discard_const ( state - > smb2 . dyn ) ;
iov [ num_iov ] . iov_len = state - > smb2 . dyn_len ;
num_iov + = 1 ;
}
reqlen = sizeof ( state - > smb2 . hdr ) ;
reqlen + = state - > smb2 . fixed_len ;
reqlen + = state - > smb2 . dyn_len ;
if ( i < num_reqs - 1 ) {
if ( ( reqlen % 8 ) > 0 ) {
uint8_t pad = 8 - ( reqlen % 8 ) ;
iov [ num_iov ] . iov_base = state - > smb2 . pad ;
iov [ num_iov ] . iov_len = pad ;
num_iov + = 1 ;
reqlen + = pad ;
}
SIVAL ( state - > smb2 . hdr , SMB2_HDR_NEXT_COMMAND , reqlen ) ;
}
2012-08-14 11:30:43 +04:00
state - > smb2 . encryption_session_id = encryption_session_id ;
2012-07-23 12:14:53 +04:00
2012-08-14 11:30:43 +04:00
if ( signing_key ! = NULL ) {
2011-09-03 12:18:32 +04:00
NTSTATUS status ;
2012-02-27 11:48:55 +04:00
status = smb2_signing_sign_pdu ( * signing_key ,
2012-02-27 12:29:53 +04:00
state - > session - > conn - > protocol ,
2011-09-03 12:18:32 +04:00
& iov [ hdr_iov ] , num_iov - hdr_iov ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
2012-07-23 12:07:19 +04:00
nbt_len + = reqlen ;
2011-09-15 14:23:05 +04:00
ret = smbXcli_req_set_pending ( reqs [ i ] ) ;
2011-09-15 13:47:11 +04:00
if ( ! ret ) {
return NT_STATUS_NO_MEMORY ;
}
}
2011-09-15 14:23:05 +04:00
state = tevent_req_data ( reqs [ 0 ] , struct smbXcli_req_state ) ;
_smb_setlen_tcp ( state - > length_hdr , nbt_len ) ;
iov [ 0 ] . iov_base = state - > length_hdr ;
iov [ 0 ] . iov_len = sizeof ( state - > length_hdr ) ;
2011-09-15 13:47:11 +04:00
2012-08-14 11:30:43 +04:00
if ( encryption_key ! = NULL ) {
NTSTATUS status ;
size_t buflen = nbt_len - SMB2_TF_HDR_SIZE ;
uint8_t * buf ;
int vi ;
buf = talloc_array ( iov , uint8_t , buflen ) ;
if ( buf = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
/*
* We copy the buffers before encrypting them ,
* this is at least currently needed for the
* to keep state - > smb2 . hdr .
*
* Also the callers may expect there buffers
* to be const .
*/
for ( vi = tf_iov + 1 ; vi < num_iov ; vi + + ) {
struct iovec * v = & iov [ vi ] ;
const uint8_t * o = ( const uint8_t * ) v - > iov_base ;
memcpy ( buf , o , v - > iov_len ) ;
v - > iov_base = ( void * ) buf ;
buf + = v - > iov_len ;
}
status = smb2_signing_encrypt_pdu ( * encryption_key ,
state - > conn - > protocol ,
& iov [ tf_iov ] , num_iov - tf_iov ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
2011-09-15 14:23:05 +04:00
if ( state - > conn - > dispatch_incoming = = NULL ) {
state - > conn - > dispatch_incoming = smb2cli_conn_dispatch_incoming ;
}
subreq = writev_send ( state , state - > ev , state - > conn - > outgoing ,
2011-09-25 05:15:54 +04:00
state - > conn - > write_fd , false , iov , num_iov ) ;
2011-09-15 13:47:11 +04:00
if ( subreq = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2011-11-28 13:24:18 +04:00
tevent_req_set_callback ( subreq , smb2cli_req_writev_done , reqs [ 0 ] ) ;
2011-09-15 13:47:11 +04:00
return NT_STATUS_OK ;
}
2011-09-06 19:52:02 +04:00
void smb2cli_req_set_credit_charge ( struct tevent_req * req , uint16_t charge )
{
struct smbXcli_req_state * state =
tevent_req_data ( req ,
struct smbXcli_req_state ) ;
state - > smb2 . credit_charge = charge ;
}
2011-09-15 13:47:11 +04:00
struct tevent_req * smb2cli_req_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
2011-09-15 14:23:05 +04:00
struct smbXcli_conn * conn ,
2011-09-15 13:47:11 +04:00
uint16_t cmd ,
uint32_t additional_flags ,
uint32_t clear_flags ,
2011-09-15 14:23:05 +04:00
uint32_t timeout_msec ,
2012-07-25 12:36:27 +04:00
struct smbXcli_tcon * tcon ,
2011-09-03 12:18:17 +04:00
struct smbXcli_session * session ,
2011-09-15 13:47:11 +04:00
const uint8_t * fixed ,
uint16_t fixed_len ,
const uint8_t * dyn ,
2013-08-13 12:25:52 +04:00
uint32_t dyn_len ,
uint32_t max_dyn_len )
2011-09-15 13:47:11 +04:00
{
struct tevent_req * req ;
NTSTATUS status ;
2011-09-15 14:23:05 +04:00
req = smb2cli_req_create ( mem_ctx , ev , conn , cmd ,
2011-09-15 13:47:11 +04:00
additional_flags , clear_flags ,
2011-09-15 14:23:05 +04:00
timeout_msec ,
2012-07-25 12:58:34 +04:00
tcon , session ,
2013-08-13 11:54:42 +04:00
fixed , fixed_len ,
dyn , dyn_len ,
max_dyn_len ) ;
2011-09-15 13:47:11 +04:00
if ( req = = NULL ) {
return NULL ;
}
if ( ! tevent_req_is_in_progress ( req ) ) {
return tevent_req_post ( req , ev ) ;
}
status = smb2cli_req_compound_submit ( & req , 1 ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
return req ;
}
2011-11-28 13:24:18 +04:00
static void smb2cli_req_writev_done ( struct tevent_req * subreq )
2011-09-15 13:47:11 +04:00
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
2011-09-15 14:23:05 +04:00
struct smbXcli_req_state * state =
2011-09-15 13:47:11 +04:00
tevent_req_data ( req ,
2011-09-15 14:23:05 +04:00
struct smbXcli_req_state ) ;
2011-09-15 13:47:11 +04:00
ssize_t nwritten ;
int err ;
nwritten = writev_recv ( subreq , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( nwritten = = - 1 ) {
/* here, we need to notify all pending requests */
NTSTATUS status = map_nt_error_from_unix_common ( err ) ;
2011-09-15 14:23:05 +04:00
smbXcli_conn_disconnect ( state - > conn , status ) ;
2011-09-15 13:47:11 +04:00
return ;
}
}
2012-07-20 11:19:24 +04:00
static NTSTATUS smb2cli_inbuf_parse_compound ( struct smbXcli_conn * conn ,
uint8_t * buf ,
size_t buflen ,
TALLOC_CTX * mem_ctx ,
2011-09-15 13:47:11 +04:00
struct iovec * * piov , int * pnum_iov )
{
struct iovec * iov ;
2012-07-20 11:19:24 +04:00
int num_iov = 0 ;
size_t taken = 0 ;
uint8_t * first_hdr = buf ;
2012-08-14 11:33:01 +04:00
size_t verified_buflen = 0 ;
uint8_t * tf = NULL ;
size_t tf_len = 0 ;
2011-09-15 13:47:11 +04:00
iov = talloc_array ( mem_ctx , struct iovec , num_iov ) ;
if ( iov = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
while ( taken < buflen ) {
size_t len = buflen - taken ;
uint8_t * hdr = first_hdr + taken ;
struct iovec * cur ;
size_t full_size ;
size_t next_command_ofs ;
uint16_t body_size ;
struct iovec * iov_tmp ;
2012-08-14 11:33:01 +04:00
if ( verified_buflen > taken ) {
len = verified_buflen - taken ;
} else {
tf = NULL ;
tf_len = 0 ;
}
2012-07-23 11:16:05 +04:00
if ( len < 4 ) {
DEBUG ( 10 , ( " %d bytes left, expected at least %d \n " ,
( int ) len , 4 ) ) ;
goto inval ;
}
if ( IVAL ( hdr , 0 ) = = SMB2_TF_MAGIC ) {
struct smbXcli_session * s ;
uint64_t uid ;
struct iovec tf_iov [ 2 ] ;
2012-08-17 10:31:52 +04:00
size_t enc_len ;
2012-07-23 11:16:05 +04:00
NTSTATUS status ;
if ( len < SMB2_TF_HDR_SIZE ) {
DEBUG ( 10 , ( " %d bytes left, expected at least %d \n " ,
( int ) len , SMB2_TF_HDR_SIZE ) ) ;
goto inval ;
}
tf = hdr ;
tf_len = SMB2_TF_HDR_SIZE ;
taken + = tf_len ;
hdr = first_hdr + taken ;
2012-08-17 10:31:52 +04:00
enc_len = IVAL ( tf , SMB2_TF_MSG_SIZE ) ;
2012-07-23 11:16:05 +04:00
uid = BVAL ( tf , SMB2_TF_SESSION_ID ) ;
2012-08-17 10:31:52 +04:00
if ( len < SMB2_TF_HDR_SIZE + enc_len ) {
DEBUG ( 10 , ( " %d bytes left, expected at least %d \n " ,
( int ) len ,
( int ) ( SMB2_TF_HDR_SIZE + enc_len ) ) ) ;
goto inval ;
}
2012-07-23 11:16:05 +04:00
s = conn - > sessions ;
for ( ; s ; s = s - > next ) {
2012-07-26 04:36:37 +04:00
if ( s - > smb2 - > session_id ! = uid ) {
2012-07-23 11:16:05 +04:00
continue ;
}
break ;
}
if ( s = = NULL ) {
DEBUG ( 10 , ( " unknown session_id %llu \n " ,
( unsigned long long ) uid ) ) ;
goto inval ;
}
tf_iov [ 0 ] . iov_base = ( void * ) tf ;
tf_iov [ 0 ] . iov_len = tf_len ;
tf_iov [ 1 ] . iov_base = ( void * ) hdr ;
2012-08-17 10:31:52 +04:00
tf_iov [ 1 ] . iov_len = enc_len ;
2012-07-23 11:16:05 +04:00
2012-07-26 04:36:37 +04:00
status = smb2_signing_decrypt_pdu ( s - > smb2 - > decryption_key ,
2012-07-23 11:16:05 +04:00
conn - > protocol ,
tf_iov , 2 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( iov ) ;
return status ;
}
2012-08-14 11:33:01 +04:00
2012-08-17 10:31:52 +04:00
verified_buflen = taken + enc_len ;
len = enc_len ;
2012-07-23 11:16:05 +04:00
}
2011-09-15 13:47:11 +04:00
/*
* We need the header plus the body length field
*/
if ( len < SMB2_HDR_BODY + 2 ) {
DEBUG ( 10 , ( " %d bytes left, expected at least %d \n " ,
( int ) len , SMB2_HDR_BODY ) ) ;
goto inval ;
}
if ( IVAL ( hdr , 0 ) ! = SMB2_MAGIC ) {
DEBUG ( 10 , ( " Got non-SMB2 PDU: %x \n " ,
IVAL ( hdr , 0 ) ) ) ;
goto inval ;
}
if ( SVAL ( hdr , 4 ) ! = SMB2_HDR_BODY ) {
DEBUG ( 10 , ( " Got HDR len %d, expected %d \n " ,
SVAL ( hdr , 4 ) , SMB2_HDR_BODY ) ) ;
goto inval ;
}
full_size = len ;
next_command_ofs = IVAL ( hdr , SMB2_HDR_NEXT_COMMAND ) ;
body_size = SVAL ( hdr , SMB2_HDR_BODY ) ;
if ( next_command_ofs ! = 0 ) {
if ( next_command_ofs < ( SMB2_HDR_BODY + 2 ) ) {
goto inval ;
}
if ( next_command_ofs > full_size ) {
goto inval ;
}
full_size = next_command_ofs ;
}
if ( body_size < 2 ) {
goto inval ;
}
body_size & = 0xfffe ;
if ( body_size > ( full_size - SMB2_HDR_BODY ) ) {
goto inval ;
}
iov_tmp = talloc_realloc ( mem_ctx , iov , struct iovec ,
2012-07-23 10:11:59 +04:00
num_iov + 4 ) ;
2011-09-15 13:47:11 +04:00
if ( iov_tmp = = NULL ) {
TALLOC_FREE ( iov ) ;
return NT_STATUS_NO_MEMORY ;
}
iov = iov_tmp ;
cur = & iov [ num_iov ] ;
2012-07-23 10:11:59 +04:00
num_iov + = 4 ;
2011-09-15 13:47:11 +04:00
2012-07-23 10:11:59 +04:00
cur [ 0 ] . iov_base = tf ;
cur [ 0 ] . iov_len = tf_len ;
cur [ 1 ] . iov_base = hdr ;
cur [ 1 ] . iov_len = SMB2_HDR_BODY ;
cur [ 2 ] . iov_base = hdr + SMB2_HDR_BODY ;
cur [ 2 ] . iov_len = body_size ;
cur [ 3 ] . iov_base = hdr + SMB2_HDR_BODY + body_size ;
cur [ 3 ] . iov_len = full_size - ( SMB2_HDR_BODY + body_size ) ;
2011-09-15 13:47:11 +04:00
taken + = full_size ;
}
* piov = iov ;
* pnum_iov = num_iov ;
return NT_STATUS_OK ;
inval :
TALLOC_FREE ( iov ) ;
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
2011-09-15 14:23:05 +04:00
static struct tevent_req * smb2cli_conn_find_pending ( struct smbXcli_conn * conn ,
uint64_t mid )
2011-09-15 13:47:11 +04:00
{
2011-09-15 14:23:05 +04:00
size_t num_pending = talloc_array_length ( conn - > pending ) ;
size_t i ;
2011-09-15 13:47:11 +04:00
for ( i = 0 ; i < num_pending ; i + + ) {
2011-09-15 14:23:05 +04:00
struct tevent_req * req = conn - > pending [ i ] ;
struct smbXcli_req_state * state =
2011-09-15 13:47:11 +04:00
tevent_req_data ( req ,
2011-09-15 14:23:05 +04:00
struct smbXcli_req_state ) ;
2011-09-15 13:47:11 +04:00
2011-09-15 14:23:05 +04:00
if ( mid = = BVAL ( state - > smb2 . hdr , SMB2_HDR_MESSAGE_ID ) ) {
2011-09-15 13:47:11 +04:00
return req ;
}
}
return NULL ;
}
2011-09-15 14:23:05 +04:00
static NTSTATUS smb2cli_conn_dispatch_incoming ( struct smbXcli_conn * conn ,
TALLOC_CTX * tmp_mem ,
uint8_t * inbuf )
2011-09-15 13:47:11 +04:00
{
struct tevent_req * req ;
2011-09-15 14:23:05 +04:00
struct smbXcli_req_state * state = NULL ;
2011-09-15 13:47:11 +04:00
struct iovec * iov ;
int i , num_iov ;
NTSTATUS status ;
bool defer = true ;
2011-09-03 12:18:32 +04:00
struct smbXcli_session * last_session = NULL ;
2012-07-20 11:19:24 +04:00
size_t inbuf_len = smb_len_tcp ( inbuf ) ;
2011-09-15 13:47:11 +04:00
2012-07-20 11:19:24 +04:00
status = smb2cli_inbuf_parse_compound ( conn ,
inbuf + NBT_HDR_SIZE ,
inbuf_len ,
tmp_mem ,
2011-09-15 13:47:11 +04:00
& iov , & num_iov ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2011-09-15 14:23:05 +04:00
return status ;
2011-09-15 13:47:11 +04:00
}
2012-07-23 10:11:59 +04:00
for ( i = 0 ; i < num_iov ; i + = 4 ) {
2011-09-15 13:47:11 +04:00
uint8_t * inbuf_ref = NULL ;
struct iovec * cur = & iov [ i ] ;
2012-07-23 10:11:59 +04:00
uint8_t * inhdr = ( uint8_t * ) cur [ 1 ] . iov_base ;
2011-09-15 13:47:11 +04:00
uint16_t opcode = SVAL ( inhdr , SMB2_HDR_OPCODE ) ;
uint32_t flags = IVAL ( inhdr , SMB2_HDR_FLAGS ) ;
uint64_t mid = BVAL ( inhdr , SMB2_HDR_MESSAGE_ID ) ;
uint16_t req_opcode ;
2011-09-03 12:18:32 +04:00
uint32_t req_flags ;
2011-09-06 19:52:02 +04:00
uint16_t credits = SVAL ( inhdr , SMB2_HDR_CREDIT ) ;
uint32_t new_credits ;
2011-09-03 12:18:32 +04:00
struct smbXcli_session * session = NULL ;
const DATA_BLOB * signing_key = NULL ;
2012-08-14 11:35:59 +04:00
bool was_encrypted = false ;
2011-09-06 19:52:02 +04:00
new_credits = conn - > smb2 . cur_credits ;
new_credits + = credits ;
if ( new_credits > UINT16_MAX ) {
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
conn - > smb2 . cur_credits + = credits ;
2011-09-15 13:47:11 +04:00
2011-09-15 14:23:05 +04:00
req = smb2cli_conn_find_pending ( conn , mid ) ;
2011-09-15 13:47:11 +04:00
if ( req = = NULL ) {
2011-09-15 14:23:05 +04:00
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
2011-09-15 13:47:11 +04:00
}
2011-09-15 14:23:05 +04:00
state = tevent_req_data ( req , struct smbXcli_req_state ) ;
2011-09-15 13:47:11 +04:00
2011-10-27 19:20:27 +04:00
state - > smb2 . got_async = false ;
2011-09-15 14:23:05 +04:00
req_opcode = SVAL ( state - > smb2 . hdr , SMB2_HDR_OPCODE ) ;
2011-09-15 13:47:11 +04:00
if ( opcode ! = req_opcode ) {
2011-09-15 14:23:05 +04:00
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
2011-09-15 13:47:11 +04:00
}
2011-09-03 12:18:32 +04:00
req_flags = SVAL ( state - > smb2 . hdr , SMB2_HDR_FLAGS ) ;
2011-09-15 13:47:11 +04:00
if ( ! ( flags & SMB2_HDR_FLAG_REDIRECT ) ) {
2011-09-15 14:23:05 +04:00
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
2011-09-15 13:47:11 +04:00
}
status = NT_STATUS ( IVAL ( inhdr , SMB2_HDR_STATUS ) ) ;
if ( ( flags & SMB2_HDR_FLAG_ASYNC ) & &
NT_STATUS_EQUAL ( status , STATUS_PENDING ) ) {
uint64_t async_id = BVAL ( inhdr , SMB2_HDR_ASYNC_ID ) ;
2011-09-03 12:18:32 +04:00
/*
* async interim responses are not signed ,
* even if the SMB2_HDR_FLAG_SIGNED flag
* is set .
*/
2011-09-15 13:47:11 +04:00
req_flags | = SMB2_HDR_FLAG_ASYNC ;
2011-09-15 14:23:05 +04:00
SBVAL ( state - > smb2 . hdr , SMB2_HDR_FLAGS , req_flags ) ;
SBVAL ( state - > smb2 . hdr , SMB2_HDR_ASYNC_ID , async_id ) ;
2011-10-27 19:20:27 +04:00
if ( state - > smb2 . notify_async ) {
state - > smb2 . got_async = true ;
tevent_req_defer_callback ( req , state - > ev ) ;
tevent_req_notify_callback ( req ) ;
}
2011-09-15 13:47:11 +04:00
continue ;
}
2011-09-03 12:18:32 +04:00
session = state - > session ;
if ( req_flags & SMB2_HDR_FLAG_CHAINED ) {
session = last_session ;
}
last_session = session ;
2012-07-23 12:00:50 +04:00
if ( state - > smb2 . should_sign ) {
2011-09-03 12:18:32 +04:00
if ( ! ( flags & SMB2_HDR_FLAG_SIGNED ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
}
if ( flags & SMB2_HDR_FLAG_SIGNED ) {
uint64_t uid = BVAL ( inhdr , SMB2_HDR_SESSION_ID ) ;
if ( session = = NULL ) {
struct smbXcli_session * s ;
s = state - > conn - > sessions ;
for ( ; s ; s = s - > next ) {
2012-07-26 04:36:37 +04:00
if ( s - > smb2 - > session_id ! = uid ) {
2011-09-03 12:18:32 +04:00
continue ;
}
session = s ;
break ;
}
}
if ( session = = NULL ) {
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
last_session = session ;
2012-07-26 04:36:37 +04:00
signing_key = & session - > smb2_channel . signing_key ;
2011-09-03 12:18:32 +04:00
}
2012-02-27 11:48:55 +04:00
if ( opcode = = SMB2_OP_SESSSETUP ) {
2011-09-03 12:18:32 +04:00
/*
2012-02-27 11:48:55 +04:00
* We prefer the channel signing key , if it is
* already there .
*
* If we do not have a channel signing key yet ,
* we try the main signing key , if it is not
* the final response .
2011-09-03 12:18:32 +04:00
*/
2012-02-27 11:48:55 +04:00
if ( signing_key & & signing_key - > length = = 0 & &
! NT_STATUS_IS_OK ( status ) ) {
2012-07-26 04:36:37 +04:00
signing_key = & session - > smb2 - > signing_key ;
2012-02-27 11:48:55 +04:00
}
if ( signing_key & & signing_key - > length = = 0 ) {
/*
* If we do not have a session key to
* verify the signature , we defer the
* signing check to the caller .
*
* The caller gets NT_STATUS_OK , it
* has to call
* smb2cli_session_set_session_key ( )
* or
* smb2cli_session_set_channel_key ( )
* which will check the signature
* with the channel signing key .
*/
signing_key = NULL ;
}
2011-09-03 12:18:32 +04:00
}
2012-08-14 11:35:59 +04:00
if ( cur [ 0 ] . iov_len = = SMB2_TF_HDR_SIZE ) {
const uint8_t * tf = ( const uint8_t * ) cur [ 0 ] . iov_base ;
uint64_t uid = BVAL ( tf , SMB2_TF_SESSION_ID ) ;
/*
* If the response was encrypted in a SMB2_TRANSFORM
* pdu , which belongs to the correct session ,
* we do not need to do signing checks
*
* It could be the session the response belongs to
* or the session that was used to encrypt the
* SMB2_TRANSFORM request .
*/
if ( ( session & & session - > smb2 - > session_id = = uid ) | |
( state - > smb2 . encryption_session_id = = uid ) ) {
signing_key = NULL ;
was_encrypted = true ;
}
}
2011-09-03 12:18:32 +04:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_USER_SESSION_DELETED ) ) {
/*
* if the server returns NT_STATUS_USER_SESSION_DELETED
* the response is not signed and we should
* propagate the NT_STATUS_USER_SESSION_DELETED
* status to the caller .
*/
2012-08-14 11:35:59 +04:00
state - > smb2 . signing_skipped = true ;
2012-02-27 12:24:38 +04:00
signing_key = NULL ;
2012-08-14 11:35:59 +04:00
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_INVALID_PARAMETER ) ) {
/*
* if the server returns
* NT_STATUS_INVALID_PARAMETER
* the response might not be encrypted .
*/
if ( state - > smb2 . should_encrypt & & ! was_encrypted ) {
state - > smb2 . signing_skipped = true ;
signing_key = NULL ;
}
}
if ( state - > smb2 . should_encrypt & & ! was_encrypted ) {
if ( ! state - > smb2 . signing_skipped ) {
2012-07-23 12:14:53 +04:00
return NT_STATUS_ACCESS_DENIED ;
}
2011-09-03 12:18:32 +04:00
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_NAME_DELETED ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_FILE_CLOSED ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_INVALID_PARAMETER ) ) {
/*
* if the server returns
* NT_STATUS_NETWORK_NAME_DELETED
* NT_STATUS_FILE_CLOSED
* NT_STATUS_INVALID_PARAMETER
* the response might not be signed
* as this happens before the signing checks .
*
* If server echos the signature ( or all zeros )
* we should report the status from the server
* to the caller .
*/
if ( signing_key ) {
int cmp ;
cmp = memcmp ( inhdr + SMB2_HDR_SIGNATURE ,
state - > smb2 . hdr + SMB2_HDR_SIGNATURE ,
16 ) ;
if ( cmp = = 0 ) {
state - > smb2 . signing_skipped = true ;
signing_key = NULL ;
}
}
if ( signing_key ) {
int cmp ;
static const uint8_t zeros [ 16 ] ;
cmp = memcmp ( inhdr + SMB2_HDR_SIGNATURE ,
zeros ,
16 ) ;
if ( cmp = = 0 ) {
state - > smb2 . signing_skipped = true ;
signing_key = NULL ;
}
}
}
if ( signing_key ) {
2012-02-27 12:29:53 +04:00
status = smb2_signing_check_pdu ( * signing_key ,
state - > conn - > protocol ,
2012-07-23 10:11:59 +04:00
& cur [ 1 ] , 3 ) ;
2011-09-03 12:18:32 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
/*
* If the signing check fails , we disconnect
* the connection .
*/
return status ;
}
}
2012-10-22 16:18:20 +04:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_SESSION_EXPIRED ) & &
( session ! = NULL ) & & session - > disconnect_expired )
{
/*
* this should be a short term hack
* until the upper layers have implemented
* re - authentication .
*/
return status ;
}
2011-09-15 14:23:05 +04:00
smbXcli_req_unset_pending ( req ) ;
2011-09-15 13:47:11 +04:00
/*
* There might be more than one response
* we need to defer the notifications
*/
2012-07-23 10:11:59 +04:00
if ( ( num_iov = = 5 ) & & ( talloc_array_length ( conn - > pending ) = = 0 ) ) {
2011-09-15 13:47:11 +04:00
defer = false ;
}
if ( defer ) {
tevent_req_defer_callback ( req , state - > ev ) ;
}
/*
* Note : here we use talloc_reference ( ) in a way
* that does not expose it to the caller .
*/
2011-09-15 14:23:05 +04:00
inbuf_ref = talloc_reference ( state - > smb2 . recv_iov , inbuf ) ;
2011-09-15 13:47:11 +04:00
if ( tevent_req_nomem ( inbuf_ref , req ) ) {
continue ;
}
/* copy the related buffers */
2012-07-23 10:11:59 +04:00
state - > smb2 . recv_iov [ 0 ] = cur [ 1 ] ;
state - > smb2 . recv_iov [ 1 ] = cur [ 2 ] ;
state - > smb2 . recv_iov [ 2 ] = cur [ 3 ] ;
2011-09-15 13:47:11 +04:00
tevent_req_done ( req ) ;
}
2011-09-15 14:23:05 +04:00
if ( defer ) {
return NT_STATUS_RETRY ;
2011-09-15 13:47:11 +04:00
}
2011-09-15 14:23:05 +04:00
return NT_STATUS_OK ;
2011-09-15 13:47:11 +04:00
}
NTSTATUS smb2cli_req_recv ( struct tevent_req * req , TALLOC_CTX * mem_ctx ,
struct iovec * * piov ,
const struct smb2cli_req_expected_response * expected ,
size_t num_expected )
{
2011-09-15 14:23:05 +04:00
struct smbXcli_req_state * state =
2011-09-15 13:47:11 +04:00
tevent_req_data ( req ,
2011-09-15 14:23:05 +04:00
struct smbXcli_req_state ) ;
2011-09-15 13:47:11 +04:00
NTSTATUS status ;
size_t body_size ;
bool found_status = false ;
bool found_size = false ;
size_t i ;
if ( piov ! = NULL ) {
* piov = NULL ;
}
2011-10-27 19:20:27 +04:00
if ( state - > smb2 . got_async ) {
return STATUS_PENDING ;
}
2011-09-15 13:47:11 +04:00
if ( tevent_req_is_nterror ( req , & status ) ) {
for ( i = 0 ; i < num_expected ; i + + ) {
if ( NT_STATUS_EQUAL ( status , expected [ i ] . status ) ) {
found_status = true ;
break ;
}
}
if ( found_status ) {
return NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
}
return status ;
}
if ( num_expected = = 0 ) {
found_status = true ;
found_size = true ;
}
2011-09-15 14:23:05 +04:00
status = NT_STATUS ( IVAL ( state - > smb2 . recv_iov [ 0 ] . iov_base , SMB2_HDR_STATUS ) ) ;
body_size = SVAL ( state - > smb2 . recv_iov [ 1 ] . iov_base , 0 ) ;
2011-09-15 13:47:11 +04:00
for ( i = 0 ; i < num_expected ; i + + ) {
if ( ! NT_STATUS_EQUAL ( status , expected [ i ] . status ) ) {
continue ;
}
found_status = true ;
if ( expected [ i ] . body_size = = 0 ) {
found_size = true ;
break ;
}
if ( expected [ i ] . body_size = = body_size ) {
found_size = true ;
break ;
}
}
if ( ! found_status ) {
return status ;
}
2011-09-03 12:18:32 +04:00
if ( state - > smb2 . signing_skipped ) {
if ( num_expected > 0 ) {
return NT_STATUS_ACCESS_DENIED ;
}
if ( ! NT_STATUS_IS_ERR ( status ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
}
2011-09-15 13:47:11 +04:00
if ( ! found_size ) {
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
if ( piov ! = NULL ) {
2011-09-15 14:23:05 +04:00
* piov = talloc_move ( mem_ctx , & state - > smb2 . recv_iov ) ;
2011-09-15 13:47:11 +04:00
}
return status ;
}
2011-09-15 15:12:43 +04:00
static const struct {
enum protocol_types proto ;
const char * smb1_name ;
} smb1cli_prots [ ] = {
{ PROTOCOL_CORE , " PC NETWORK PROGRAM 1.0 " } ,
{ PROTOCOL_COREPLUS , " MICROSOFT NETWORKS 1.03 " } ,
{ PROTOCOL_LANMAN1 , " MICROSOFT NETWORKS 3.0 " } ,
{ PROTOCOL_LANMAN1 , " LANMAN1.0 " } ,
{ PROTOCOL_LANMAN2 , " LM1.2X002 " } ,
{ PROTOCOL_LANMAN2 , " DOS LANMAN2.1 " } ,
{ PROTOCOL_LANMAN2 , " LANMAN2.1 " } ,
{ PROTOCOL_LANMAN2 , " Samba " } ,
{ PROTOCOL_NT1 , " NT LANMAN 1.0 " } ,
{ PROTOCOL_NT1 , " NT LM 0.12 " } ,
{ PROTOCOL_SMB2_02 , " SMB 2.002 " } ,
2011-08-30 13:04:46 +04:00
{ PROTOCOL_SMB2_10 , " SMB 2.??? " } ,
2011-09-15 15:12:43 +04:00
} ;
static const struct {
enum protocol_types proto ;
uint16_t smb2_dialect ;
} smb2cli_prots [ ] = {
{ PROTOCOL_SMB2_02 , SMB2_DIALECT_REVISION_202 } ,
2011-08-30 13:04:46 +04:00
{ PROTOCOL_SMB2_10 , SMB2_DIALECT_REVISION_210 } ,
2011-09-16 18:12:50 +04:00
{ PROTOCOL_SMB2_22 , SMB2_DIALECT_REVISION_222 } ,
2011-12-22 16:08:44 +04:00
{ PROTOCOL_SMB2_24 , SMB2_DIALECT_REVISION_224 } ,
2012-05-05 11:33:43 +04:00
{ PROTOCOL_SMB3_00 , SMB3_DIALECT_REVISION_300 } ,
2011-09-15 15:12:43 +04:00
} ;
struct smbXcli_negprot_state {
struct smbXcli_conn * conn ;
struct tevent_context * ev ;
uint32_t timeout_msec ;
enum protocol_types min_protocol ;
enum protocol_types max_protocol ;
struct {
uint8_t fixed [ 36 ] ;
uint8_t dyn [ ARRAY_SIZE ( smb2cli_prots ) * 2 ] ;
} smb2 ;
} ;
static void smbXcli_negprot_invalid_done ( struct tevent_req * subreq ) ;
static struct tevent_req * smbXcli_negprot_smb1_subreq ( struct smbXcli_negprot_state * state ) ;
static void smbXcli_negprot_smb1_done ( struct tevent_req * subreq ) ;
static struct tevent_req * smbXcli_negprot_smb2_subreq ( struct smbXcli_negprot_state * state ) ;
static void smbXcli_negprot_smb2_done ( struct tevent_req * subreq ) ;
static NTSTATUS smbXcli_negprot_dispatch_incoming ( struct smbXcli_conn * conn ,
TALLOC_CTX * frame ,
uint8_t * inbuf ) ;
struct tevent_req * smbXcli_negprot_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct smbXcli_conn * conn ,
uint32_t timeout_msec ,
enum protocol_types min_protocol ,
enum protocol_types max_protocol )
{
struct tevent_req * req , * subreq ;
struct smbXcli_negprot_state * state ;
req = tevent_req_create ( mem_ctx , & state ,
struct smbXcli_negprot_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > conn = conn ;
state - > ev = ev ;
state - > timeout_msec = timeout_msec ;
state - > min_protocol = min_protocol ;
state - > max_protocol = max_protocol ;
if ( min_protocol = = PROTOCOL_NONE ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER_MIX ) ;
return tevent_req_post ( req , ev ) ;
}
if ( max_protocol = = PROTOCOL_NONE ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER_MIX ) ;
return tevent_req_post ( req , ev ) ;
}
if ( min_protocol > max_protocol ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER_MIX ) ;
return tevent_req_post ( req , ev ) ;
}
if ( ( min_protocol < PROTOCOL_SMB2_02 ) & &
( max_protocol < PROTOCOL_SMB2_02 ) ) {
/*
* SMB1 only . . .
*/
conn - > dispatch_incoming = smb1cli_conn_dispatch_incoming ;
subreq = smbXcli_negprot_smb1_subreq ( state ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , smbXcli_negprot_smb1_done , req ) ;
return req ;
}
if ( ( min_protocol > = PROTOCOL_SMB2_02 ) & &
( max_protocol > = PROTOCOL_SMB2_02 ) ) {
/*
* SMB2 only . . .
*/
conn - > dispatch_incoming = smb2cli_conn_dispatch_incoming ;
subreq = smbXcli_negprot_smb2_subreq ( state ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , smbXcli_negprot_smb2_done , req ) ;
return req ;
}
/*
* We send an SMB1 negprot with the SMB2 dialects
* and expect a SMB1 or a SMB2 response .
*
* smbXcli_negprot_dispatch_incoming ( ) will fix the
* callback to match protocol of the response .
*/
conn - > dispatch_incoming = smbXcli_negprot_dispatch_incoming ;
subreq = smbXcli_negprot_smb1_subreq ( state ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , smbXcli_negprot_invalid_done , req ) ;
return req ;
}
static void smbXcli_negprot_invalid_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
NTSTATUS status ;
/*
* we just want the low level error
*/
status = tevent_req_simple_recv_ntstatus ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
/* this should never happen */
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
}
static struct tevent_req * smbXcli_negprot_smb1_subreq ( struct smbXcli_negprot_state * state )
{
size_t i ;
DATA_BLOB bytes = data_blob_null ;
uint8_t flags ;
uint16_t flags2 ;
/* setup the protocol strings */
for ( i = 0 ; i < ARRAY_SIZE ( smb1cli_prots ) ; i + + ) {
uint8_t c = 2 ;
bool ok ;
if ( smb1cli_prots [ i ] . proto < state - > min_protocol ) {
continue ;
}
if ( smb1cli_prots [ i ] . proto > state - > max_protocol ) {
continue ;
}
ok = data_blob_append ( state , & bytes , & c , sizeof ( c ) ) ;
if ( ! ok ) {
return NULL ;
}
/*
* We now it is already ascii and
* we want NULL termination .
*/
ok = data_blob_append ( state , & bytes ,
smb1cli_prots [ i ] . smb1_name ,
strlen ( smb1cli_prots [ i ] . smb1_name ) + 1 ) ;
if ( ! ok ) {
return NULL ;
}
}
smb1cli_req_flags ( state - > max_protocol ,
state - > conn - > smb1 . client . capabilities ,
SMBnegprot ,
0 , 0 , & flags ,
0 , 0 , & flags2 ) ;
return smb1cli_req_send ( state , state - > ev , state - > conn ,
SMBnegprot ,
flags , ~ flags ,
flags2 , ~ flags2 ,
state - > timeout_msec ,
2012-07-23 22:15:21 +04:00
0xFFFE , 0 , NULL , /* pid, tid, session */
2011-09-15 15:12:43 +04:00
0 , NULL , /* wct, vwv */
bytes . length , bytes . data ) ;
}
static void smbXcli_negprot_smb1_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct smbXcli_negprot_state * state =
tevent_req_data ( req ,
struct smbXcli_negprot_state ) ;
struct smbXcli_conn * conn = state - > conn ;
struct iovec * recv_iov = NULL ;
uint8_t * inhdr ;
uint8_t wct ;
uint16_t * vwv ;
uint32_t num_bytes ;
uint8_t * bytes ;
NTSTATUS status ;
uint16_t protnum ;
size_t i ;
size_t num_prots = 0 ;
uint8_t flags ;
uint32_t client_capabilities = conn - > smb1 . client . capabilities ;
uint32_t both_capabilities ;
uint32_t server_capabilities = 0 ;
uint32_t capabilities ;
uint32_t client_max_xmit = conn - > smb1 . client . max_xmit ;
uint32_t server_max_xmit = 0 ;
uint32_t max_xmit ;
uint32_t server_max_mux = 0 ;
uint16_t server_security_mode = 0 ;
uint32_t server_session_key = 0 ;
bool server_readbraw = false ;
bool server_writebraw = false ;
bool server_lockread = false ;
bool server_writeunlock = false ;
struct GUID server_guid = GUID_zero ( ) ;
DATA_BLOB server_gss_blob = data_blob_null ;
uint8_t server_challenge [ 8 ] ;
char * server_workgroup = NULL ;
char * server_name = NULL ;
int server_time_zone = 0 ;
NTTIME server_system_time = 0 ;
static const struct smb1cli_req_expected_response expected [ ] = {
{
. status = NT_STATUS_OK ,
. wct = 0x11 , /* NT1 */
} ,
{
. status = NT_STATUS_OK ,
. wct = 0x0D , /* LM */
} ,
{
. status = NT_STATUS_OK ,
. wct = 0x01 , /* CORE */
}
} ;
ZERO_STRUCT ( server_challenge ) ;
status = smb1cli_req_recv ( subreq , state ,
& recv_iov ,
& inhdr ,
& wct ,
& vwv ,
NULL , /* pvwv_offset */
& num_bytes ,
& bytes ,
NULL , /* pbytes_offset */
NULL , /* pinbuf */
expected , ARRAY_SIZE ( expected ) ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
flags = CVAL ( inhdr , HDR_FLG ) ;
protnum = SVAL ( vwv , 0 ) ;
for ( i = 0 ; i < ARRAY_SIZE ( smb1cli_prots ) ; i + + ) {
if ( smb1cli_prots [ i ] . proto < state - > min_protocol ) {
continue ;
}
if ( smb1cli_prots [ i ] . proto > state - > max_protocol ) {
continue ;
}
if ( protnum ! = num_prots ) {
num_prots + + ;
continue ;
}
conn - > protocol = smb1cli_prots [ i ] . proto ;
break ;
}
if ( conn - > protocol = = PROTOCOL_NONE ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
if ( ( conn - > protocol < PROTOCOL_NT1 ) & & conn - > mandatory_signing ) {
DEBUG ( 0 , ( " smbXcli_negprot: SMB signing is mandatory "
" and the selected protocol level doesn't support it. \n " ) ) ;
tevent_req_nterror ( req , NT_STATUS_ACCESS_DENIED ) ;
return ;
}
if ( flags & FLAG_SUPPORT_LOCKREAD ) {
server_lockread = true ;
server_writeunlock = true ;
}
if ( conn - > protocol > = PROTOCOL_NT1 ) {
const char * client_signing = NULL ;
bool server_mandatory = false ;
bool server_allowed = false ;
const char * server_signing = NULL ;
bool ok ;
uint8_t key_len ;
if ( wct ! = 0x11 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
/* NT protocol */
server_security_mode = CVAL ( vwv + 1 , 0 ) ;
server_max_mux = SVAL ( vwv + 1 , 1 ) ;
server_max_xmit = IVAL ( vwv + 3 , 1 ) ;
server_session_key = IVAL ( vwv + 7 , 1 ) ;
server_time_zone = SVALS ( vwv + 15 , 1 ) ;
server_time_zone * = 60 ;
/* this time arrives in real GMT */
server_system_time = BVAL ( vwv + 11 , 1 ) ;
server_capabilities = IVAL ( vwv + 9 , 1 ) ;
key_len = CVAL ( vwv + 16 , 1 ) ;
if ( server_capabilities & CAP_RAW_MODE ) {
server_readbraw = true ;
server_writebraw = true ;
}
if ( server_capabilities & CAP_LOCK_AND_READ ) {
server_lockread = true ;
}
if ( server_capabilities & CAP_EXTENDED_SECURITY ) {
DATA_BLOB blob1 , blob2 ;
if ( num_bytes < 16 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
blob1 = data_blob_const ( bytes , 16 ) ;
status = GUID_from_data_blob ( & blob1 , & server_guid ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
blob1 = data_blob_const ( bytes + 16 , num_bytes - 16 ) ;
blob2 = data_blob_dup_talloc ( state , blob1 ) ;
if ( blob1 . length > 0 & &
tevent_req_nomem ( blob2 . data , req ) ) {
return ;
}
server_gss_blob = blob2 ;
} else {
DATA_BLOB blob1 , blob2 ;
if ( num_bytes < key_len ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
if ( key_len ! = 0 & & key_len ! = 8 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
if ( key_len = = 8 ) {
memcpy ( server_challenge , bytes , 8 ) ;
}
blob1 = data_blob_const ( bytes + key_len , num_bytes - key_len ) ;
blob2 = data_blob_const ( bytes + key_len , num_bytes - key_len ) ;
if ( blob1 . length > 0 ) {
size_t len ;
len = utf16_len_n ( blob1 . data ,
blob1 . length ) ;
blob1 . length = len ;
ok = convert_string_talloc ( state ,
CH_UTF16LE ,
CH_UNIX ,
blob1 . data ,
blob1 . length ,
& server_workgroup ,
& len ) ;
if ( ! ok ) {
status = map_nt_error_from_unix_common ( errno ) ;
tevent_req_nterror ( req , status ) ;
return ;
}
}
blob2 . data + = blob1 . length ;
blob2 . length - = blob1 . length ;
if ( blob2 . length > 0 ) {
size_t len ;
len = utf16_len_n ( blob1 . data ,
blob1 . length ) ;
blob1 . length = len ;
ok = convert_string_talloc ( state ,
CH_UTF16LE ,
CH_UNIX ,
blob2 . data ,
blob2 . length ,
& server_name ,
& len ) ;
if ( ! ok ) {
status = map_nt_error_from_unix_common ( errno ) ;
tevent_req_nterror ( req , status ) ;
return ;
}
}
}
client_signing = " disabled " ;
if ( conn - > allow_signing ) {
client_signing = " allowed " ;
}
if ( conn - > mandatory_signing ) {
client_signing = " required " ;
}
server_signing = " not supported " ;
if ( server_security_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED ) {
server_signing = " supported " ;
server_allowed = true ;
2013-03-18 18:02:55 +04:00
} else if ( conn - > mandatory_signing ) {
/*
* We have mandatory signing as client
* lets assume the server will look at our
* FLAGS2_SMB_SECURITY_SIGNATURES_REQUIRED
* flag in the session setup
*/
server_signing = " not announced " ;
server_allowed = true ;
2011-09-15 15:12:43 +04:00
}
if ( server_security_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED ) {
server_signing = " required " ;
server_mandatory = true ;
}
ok = smb_signing_set_negotiated ( conn - > smb1 . signing ,
server_allowed ,
server_mandatory ) ;
if ( ! ok ) {
DEBUG ( 1 , ( " cli_negprot: SMB signing is required, "
" but client[%s] and server[%s] mismatch \n " ,
client_signing , server_signing ) ) ;
tevent_req_nterror ( req , NT_STATUS_ACCESS_DENIED ) ;
return ;
}
} else if ( conn - > protocol > = PROTOCOL_LANMAN1 ) {
DATA_BLOB blob1 ;
uint8_t key_len ;
time_t t ;
if ( wct ! = 0x0D ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
server_security_mode = SVAL ( vwv + 1 , 0 ) ;
server_max_xmit = SVAL ( vwv + 2 , 0 ) ;
server_max_mux = SVAL ( vwv + 3 , 0 ) ;
server_readbraw = ( ( SVAL ( vwv + 5 , 0 ) & 0x1 ) ! = 0 ) ;
server_writebraw = ( ( SVAL ( vwv + 5 , 0 ) & 0x2 ) ! = 0 ) ;
server_session_key = IVAL ( vwv + 6 , 0 ) ;
server_time_zone = SVALS ( vwv + 10 , 0 ) ;
server_time_zone * = 60 ;
/* this time is converted to GMT by make_unix_date */
t = pull_dos_date ( ( const uint8_t * ) ( vwv + 8 ) , server_time_zone ) ;
unix_to_nt_time ( & server_system_time , t ) ;
key_len = SVAL ( vwv + 11 , 0 ) ;
if ( num_bytes < key_len ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
if ( key_len ! = 0 & & key_len ! = 8 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
if ( key_len = = 8 ) {
memcpy ( server_challenge , bytes , 8 ) ;
}
blob1 = data_blob_const ( bytes + key_len , num_bytes - key_len ) ;
if ( blob1 . length > 0 ) {
size_t len ;
bool ok ;
len = utf16_len_n ( blob1 . data ,
blob1 . length ) ;
blob1 . length = len ;
ok = convert_string_talloc ( state ,
CH_DOS ,
CH_UNIX ,
blob1 . data ,
blob1 . length ,
& server_workgroup ,
& len ) ;
if ( ! ok ) {
status = map_nt_error_from_unix_common ( errno ) ;
tevent_req_nterror ( req , status ) ;
return ;
}
}
} else {
/* the old core protocol */
server_time_zone = get_time_zone ( time ( NULL ) ) ;
server_max_xmit = 1024 ;
server_max_mux = 1 ;
}
if ( server_max_xmit < 1024 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
if ( server_max_mux < 1 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
/*
* Now calculate the negotiated capabilities
* based on the mask for :
* - client only flags
* - flags used in both directions
* - server only flags
*/
both_capabilities = client_capabilities & server_capabilities ;
capabilities = client_capabilities & SMB_CAP_CLIENT_MASK ;
capabilities | = both_capabilities & SMB_CAP_BOTH_MASK ;
capabilities | = server_capabilities & SMB_CAP_SERVER_MASK ;
max_xmit = MIN ( client_max_xmit , server_max_xmit ) ;
conn - > smb1 . server . capabilities = server_capabilities ;
conn - > smb1 . capabilities = capabilities ;
conn - > smb1 . server . max_xmit = server_max_xmit ;
conn - > smb1 . max_xmit = max_xmit ;
conn - > smb1 . server . max_mux = server_max_mux ;
conn - > smb1 . server . security_mode = server_security_mode ;
conn - > smb1 . server . readbraw = server_readbraw ;
conn - > smb1 . server . writebraw = server_writebraw ;
conn - > smb1 . server . lockread = server_lockread ;
conn - > smb1 . server . writeunlock = server_writeunlock ;
conn - > smb1 . server . session_key = server_session_key ;
talloc_steal ( conn , server_gss_blob . data ) ;
conn - > smb1 . server . gss_blob = server_gss_blob ;
conn - > smb1 . server . guid = server_guid ;
memcpy ( conn - > smb1 . server . challenge , server_challenge , 8 ) ;
conn - > smb1 . server . workgroup = talloc_move ( conn , & server_workgroup ) ;
conn - > smb1 . server . name = talloc_move ( conn , & server_name ) ;
conn - > smb1 . server . time_zone = server_time_zone ;
conn - > smb1 . server . system_time = server_system_time ;
tevent_req_done ( req ) ;
}
static struct tevent_req * smbXcli_negprot_smb2_subreq ( struct smbXcli_negprot_state * state )
{
size_t i ;
uint8_t * buf ;
uint16_t dialect_count = 0 ;
buf = state - > smb2 . dyn ;
for ( i = 0 ; i < ARRAY_SIZE ( smb2cli_prots ) ; i + + ) {
if ( smb2cli_prots [ i ] . proto < state - > min_protocol ) {
continue ;
}
if ( smb2cli_prots [ i ] . proto > state - > max_protocol ) {
continue ;
}
SSVAL ( buf , dialect_count * 2 , smb2cli_prots [ i ] . smb2_dialect ) ;
dialect_count + + ;
}
buf = state - > smb2 . fixed ;
SSVAL ( buf , 0 , 36 ) ;
SSVAL ( buf , 2 , dialect_count ) ;
SSVAL ( buf , 4 , state - > conn - > smb2 . client . security_mode ) ;
SSVAL ( buf , 6 , 0 ) ; /* Reserved */
2012-02-29 05:02:29 +04:00
if ( state - > max_protocol > = PROTOCOL_SMB2_22 ) {
SIVAL ( buf , 8 , state - > conn - > smb2 . client . capabilities ) ;
} else {
SIVAL ( buf , 8 , 0 ) ; /* Capabilities */
}
2011-08-30 13:04:46 +04:00
if ( state - > max_protocol > = PROTOCOL_SMB2_10 ) {
NTSTATUS status ;
DATA_BLOB blob ;
status = GUID_to_ndr_blob ( & state - > conn - > smb2 . client . guid ,
state , & blob ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return NULL ;
}
memcpy ( buf + 12 , blob . data , 16 ) ; /* ClientGuid */
} else {
memset ( buf + 12 , 0 , 16 ) ; /* ClientGuid */
}
2011-09-15 15:12:43 +04:00
SBVAL ( buf , 28 , 0 ) ; /* ClientStartTime */
return smb2cli_req_send ( state , state - > ev ,
state - > conn , SMB2_OP_NEGPROT ,
0 , 0 , /* flags */
state - > timeout_msec ,
2012-07-25 12:58:34 +04:00
NULL , NULL , /* tcon, session */
2011-09-15 15:12:43 +04:00
state - > smb2 . fixed , sizeof ( state - > smb2 . fixed ) ,
2013-08-13 12:25:52 +04:00
state - > smb2 . dyn , dialect_count * 2 ,
UINT16_MAX ) ; /* max_dyn_len */
2011-09-15 15:12:43 +04:00
}
static void smbXcli_negprot_smb2_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct smbXcli_negprot_state * state =
tevent_req_data ( req ,
struct smbXcli_negprot_state ) ;
struct smbXcli_conn * conn = state - > conn ;
size_t security_offset , security_length ;
DATA_BLOB blob ;
NTSTATUS status ;
struct iovec * iov ;
uint8_t * body ;
size_t i ;
uint16_t dialect_revision ;
static const struct smb2cli_req_expected_response expected [ ] = {
{
. status = NT_STATUS_OK ,
. body_size = 0x41
}
} ;
status = smb2cli_req_recv ( subreq , state , & iov ,
expected , ARRAY_SIZE ( expected ) ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
body = ( uint8_t * ) iov [ 1 ] . iov_base ;
dialect_revision = SVAL ( body , 4 ) ;
for ( i = 0 ; i < ARRAY_SIZE ( smb2cli_prots ) ; i + + ) {
if ( smb2cli_prots [ i ] . proto < state - > min_protocol ) {
continue ;
}
if ( smb2cli_prots [ i ] . proto > state - > max_protocol ) {
continue ;
}
if ( smb2cli_prots [ i ] . smb2_dialect ! = dialect_revision ) {
continue ;
}
conn - > protocol = smb2cli_prots [ i ] . proto ;
break ;
}
if ( conn - > protocol = = PROTOCOL_NONE ) {
2011-08-30 13:04:46 +04:00
if ( state - > min_protocol > = PROTOCOL_SMB2_02 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
if ( dialect_revision ! = SMB2_DIALECT_REVISION_2FF ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
/* make sure we do not loop forever */
state - > min_protocol = PROTOCOL_SMB2_02 ;
/*
* send a SMB2 negprot , in order to negotiate
2012-01-27 16:39:54 +04:00
* the SMB2 dialect .
2011-08-30 13:04:46 +04:00
*/
subreq = smbXcli_negprot_smb2_subreq ( state ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , smbXcli_negprot_smb2_done , req ) ;
2011-09-15 15:12:43 +04:00
return ;
}
conn - > smb2 . server . security_mode = SVAL ( body , 2 ) ;
blob = data_blob_const ( body + 8 , 16 ) ;
status = GUID_from_data_blob ( & blob , & conn - > smb2 . server . guid ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
conn - > smb2 . server . capabilities = IVAL ( body , 24 ) ;
conn - > smb2 . server . max_trans_size = IVAL ( body , 28 ) ;
conn - > smb2 . server . max_read_size = IVAL ( body , 32 ) ;
conn - > smb2 . server . max_write_size = IVAL ( body , 36 ) ;
conn - > smb2 . server . system_time = BVAL ( body , 40 ) ;
conn - > smb2 . server . start_time = BVAL ( body , 48 ) ;
security_offset = SVAL ( body , 56 ) ;
security_length = SVAL ( body , 58 ) ;
if ( security_offset ! = SMB2_HDR_BODY + iov [ 1 ] . iov_len ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
if ( security_length > iov [ 2 ] . iov_len ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
conn - > smb2 . server . gss_blob = data_blob_talloc ( conn ,
iov [ 2 ] . iov_base ,
security_length ) ;
if ( tevent_req_nomem ( conn - > smb2 . server . gss_blob . data , req ) ) {
return ;
}
tevent_req_done ( req ) ;
}
static NTSTATUS smbXcli_negprot_dispatch_incoming ( struct smbXcli_conn * conn ,
TALLOC_CTX * tmp_mem ,
uint8_t * inbuf )
{
size_t num_pending = talloc_array_length ( conn - > pending ) ;
struct tevent_req * subreq ;
struct smbXcli_req_state * substate ;
struct tevent_req * req ;
2012-07-20 11:20:43 +04:00
uint32_t protocol_magic ;
size_t inbuf_len = smb_len_nbt ( inbuf ) ;
2011-09-15 15:12:43 +04:00
if ( num_pending ! = 1 ) {
return NT_STATUS_INTERNAL_ERROR ;
}
2012-07-20 11:20:43 +04:00
if ( inbuf_len < 4 ) {
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
2011-09-15 15:12:43 +04:00
subreq = conn - > pending [ 0 ] ;
substate = tevent_req_data ( subreq , struct smbXcli_req_state ) ;
req = tevent_req_callback_data ( subreq , struct tevent_req ) ;
2012-07-20 11:20:43 +04:00
protocol_magic = IVAL ( inbuf , 4 ) ;
2011-09-15 15:12:43 +04:00
switch ( protocol_magic ) {
case SMB_MAGIC :
tevent_req_set_callback ( subreq , smbXcli_negprot_smb1_done , req ) ;
conn - > dispatch_incoming = smb1cli_conn_dispatch_incoming ;
return smb1cli_conn_dispatch_incoming ( conn , tmp_mem , inbuf ) ;
case SMB2_MAGIC :
if ( substate - > smb2 . recv_iov = = NULL ) {
/*
* For the SMB1 negprot we have move it .
*/
substate - > smb2 . recv_iov = substate - > smb1 . recv_iov ;
substate - > smb1 . recv_iov = NULL ;
}
2012-01-27 16:39:54 +04:00
/*
* we got an SMB2 answer , which consumed sequence number 0
2013-08-14 10:12:08 +04:00
* so we need to use 1 as the next one .
*
* we also need to set the current credits to 0
* as we consumed the initial one . The SMB2 answer
* hopefully grant us a new credit .
2012-01-27 16:39:54 +04:00
*/
conn - > smb2 . mid = 1 ;
2013-08-14 10:12:08 +04:00
conn - > smb2 . cur_credits = 0 ;
2011-09-15 15:12:43 +04:00
tevent_req_set_callback ( subreq , smbXcli_negprot_smb2_done , req ) ;
conn - > dispatch_incoming = smb2cli_conn_dispatch_incoming ;
return smb2cli_conn_dispatch_incoming ( conn , tmp_mem , inbuf ) ;
}
DEBUG ( 10 , ( " Got non-SMB PDU \n " ) ) ;
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
NTSTATUS smbXcli_negprot_recv ( struct tevent_req * req )
{
return tevent_req_simple_recv_ntstatus ( req ) ;
}
NTSTATUS smbXcli_negprot ( struct smbXcli_conn * conn ,
uint32_t timeout_msec ,
enum protocol_types min_protocol ,
enum protocol_types max_protocol )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
bool ok ;
if ( smbXcli_conn_has_async_calls ( conn ) ) {
/*
* Can ' t use sync call while an async call is in flight
*/
status = NT_STATUS_INVALID_PARAMETER_MIX ;
goto fail ;
}
2013-02-18 12:07:11 +04:00
ev = samba_tevent_context_init ( frame ) ;
2011-09-15 15:12:43 +04:00
if ( ev = = NULL ) {
goto fail ;
}
req = smbXcli_negprot_send ( frame , ev , conn , timeout_msec ,
min_protocol , max_protocol ) ;
if ( req = = NULL ) {
goto fail ;
}
ok = tevent_req_poll ( req , ev ) ;
if ( ! ok ) {
status = map_nt_error_from_unix_common ( errno ) ;
goto fail ;
}
status = smbXcli_negprot_recv ( req ) ;
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
2011-09-03 12:18:17 +04:00
static int smbXcli_session_destructor ( struct smbXcli_session * session )
{
if ( session - > conn = = NULL ) {
return 0 ;
}
DLIST_REMOVE ( session - > conn - > sessions , session ) ;
return 0 ;
}
struct smbXcli_session * smbXcli_session_create ( TALLOC_CTX * mem_ctx ,
struct smbXcli_conn * conn )
{
struct smbXcli_session * session ;
session = talloc_zero ( mem_ctx , struct smbXcli_session ) ;
if ( session = = NULL ) {
return NULL ;
}
2012-07-26 04:36:37 +04:00
session - > smb2 = talloc_zero ( session , struct smb2cli_session ) ;
if ( session - > smb2 = = NULL ) {
talloc_free ( session ) ;
return NULL ;
}
2011-09-03 12:18:17 +04:00
talloc_set_destructor ( session , smbXcli_session_destructor ) ;
DLIST_ADD_END ( conn - > sessions , session , struct smbXcli_session * ) ;
session - > conn = conn ;
return session ;
}
2013-01-29 04:51:25 +04:00
struct smbXcli_session * smbXcli_session_copy ( TALLOC_CTX * mem_ctx ,
struct smbXcli_session * src )
{
struct smbXcli_session * session ;
session = talloc_zero ( mem_ctx , struct smbXcli_session ) ;
if ( session = = NULL ) {
return NULL ;
}
session - > smb2 = talloc_zero ( session , struct smb2cli_session ) ;
if ( session - > smb2 = = NULL ) {
talloc_free ( session ) ;
return NULL ;
}
session - > conn = src - > conn ;
* session - > smb2 = * src - > smb2 ;
session - > smb2_channel = src - > smb2_channel ;
session - > disconnect_expired = src - > disconnect_expired ;
DLIST_ADD_END ( src - > conn - > sessions , session , struct smbXcli_session * ) ;
talloc_set_destructor ( session , smbXcli_session_destructor ) ;
return session ;
}
2012-08-01 10:59:01 +04:00
NTSTATUS smbXcli_session_application_key ( struct smbXcli_session * session ,
TALLOC_CTX * mem_ctx ,
DATA_BLOB * key )
{
const DATA_BLOB * application_key ;
* key = data_blob_null ;
if ( session - > conn = = NULL ) {
return NT_STATUS_NO_USER_SESSION_KEY ;
}
if ( session - > conn - > protocol > = PROTOCOL_SMB2_02 ) {
application_key = & session - > smb2 - > application_key ;
} else {
application_key = & session - > smb1 . application_key ;
}
if ( application_key - > length = = 0 ) {
return NT_STATUS_NO_USER_SESSION_KEY ;
}
* key = data_blob_dup_talloc ( mem_ctx , * application_key ) ;
if ( key - > data = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
return NT_STATUS_OK ;
}
2012-10-22 16:18:20 +04:00
void smbXcli_session_set_disconnect_expired ( struct smbXcli_session * session )
{
session - > disconnect_expired = true ;
}
2012-06-02 14:49:22 +04:00
uint16_t smb1cli_session_current_id ( struct smbXcli_session * session )
{
return session - > smb1 . session_id ;
}
void smb1cli_session_set_id ( struct smbXcli_session * session ,
uint16_t session_id )
{
session - > smb1 . session_id = session_id ;
}
2012-08-01 10:53:30 +04:00
NTSTATUS smb1cli_session_set_session_key ( struct smbXcli_session * session ,
const DATA_BLOB _session_key )
{
struct smbXcli_conn * conn = session - > conn ;
uint8_t session_key [ 16 ] ;
if ( conn = = NULL ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
if ( session - > smb1 . application_key . length ! = 0 ) {
2012-08-01 16:14:39 +04:00
/*
* TODO : do not allow this . . .
*
* return NT_STATUS_INVALID_PARAMETER_MIX ;
*/
data_blob_clear_free ( & session - > smb1 . application_key ) ;
2012-08-01 11:01:25 +04:00
session - > smb1 . protected_key = false ;
2012-08-01 10:53:30 +04:00
}
if ( _session_key . length = = 0 ) {
return NT_STATUS_OK ;
}
ZERO_STRUCT ( session_key ) ;
memcpy ( session_key , _session_key . data ,
MIN ( _session_key . length , sizeof ( session_key ) ) ) ;
session - > smb1 . application_key = data_blob_talloc ( session ,
session_key ,
sizeof ( session_key ) ) ;
ZERO_STRUCT ( session_key ) ;
if ( session - > smb1 . application_key . data = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2012-08-01 11:01:25 +04:00
session - > smb1 . protected_key = false ;
return NT_STATUS_OK ;
}
NTSTATUS smb1cli_session_protect_session_key ( struct smbXcli_session * session )
{
if ( session - > smb1 . protected_key ) {
/* already protected */
return NT_STATUS_OK ;
}
if ( session - > smb1 . application_key . length ! = 16 ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
smb_key_derivation ( session - > smb1 . application_key . data ,
session - > smb1 . application_key . length ,
session - > smb1 . application_key . data ) ;
session - > smb1 . protected_key = true ;
2012-08-01 10:53:30 +04:00
return NT_STATUS_OK ;
}
2011-09-03 12:18:17 +04:00
uint8_t smb2cli_session_security_mode ( struct smbXcli_session * session )
{
struct smbXcli_conn * conn = session - > conn ;
uint8_t security_mode = 0 ;
if ( conn = = NULL ) {
return security_mode ;
}
security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED ;
if ( conn - > mandatory_signing ) {
security_mode | = SMB2_NEGOTIATE_SIGNING_REQUIRED ;
}
return security_mode ;
}
uint64_t smb2cli_session_current_id ( struct smbXcli_session * session )
{
2012-07-26 04:36:37 +04:00
return session - > smb2 - > session_id ;
2011-09-03 12:18:17 +04:00
}
2012-04-16 14:45:02 +04:00
uint16_t smb2cli_session_get_flags ( struct smbXcli_session * session )
{
2012-07-26 04:36:37 +04:00
return session - > smb2 - > session_flags ;
2012-04-16 14:45:02 +04:00
}
2011-09-03 12:18:17 +04:00
void smb2cli_session_set_id_and_flags ( struct smbXcli_session * session ,
uint64_t session_id ,
uint16_t session_flags )
{
2012-07-26 04:36:37 +04:00
session - > smb2 - > session_id = session_id ;
session - > smb2 - > session_flags = session_flags ;
2011-09-03 12:18:17 +04:00
}
2012-07-26 03:54:33 +04:00
void smb2cli_session_increment_channel_sequence ( struct smbXcli_session * session )
{
session - > smb2 - > channel_sequence + = 1 ;
}
2012-02-27 11:48:55 +04:00
NTSTATUS smb2cli_session_set_session_key ( struct smbXcli_session * session ,
const DATA_BLOB _session_key ,
const struct iovec * recv_iov )
2011-09-03 12:18:17 +04:00
{
struct smbXcli_conn * conn = session - > conn ;
uint16_t no_sign_flags ;
2012-02-27 11:48:55 +04:00
uint8_t session_key [ 16 ] ;
2011-09-03 12:18:17 +04:00
NTSTATUS status ;
if ( conn = = NULL ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
no_sign_flags = SMB2_SESSION_FLAG_IS_GUEST | SMB2_SESSION_FLAG_IS_NULL ;
2012-07-26 04:36:37 +04:00
if ( session - > smb2 - > session_flags & no_sign_flags ) {
session - > smb2 - > should_sign = false ;
2011-09-03 12:18:17 +04:00
return NT_STATUS_OK ;
}
2012-07-26 04:36:37 +04:00
if ( session - > smb2 - > signing_key . length ! = 0 ) {
2012-04-16 14:41:30 +04:00
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
2012-02-27 11:48:55 +04:00
ZERO_STRUCT ( session_key ) ;
memcpy ( session_key , _session_key . data ,
MIN ( _session_key . length , sizeof ( session_key ) ) ) ;
2011-09-19 21:05:20 +04:00
2012-07-26 04:36:37 +04:00
session - > smb2 - > signing_key = data_blob_talloc ( session ,
2012-02-27 11:48:55 +04:00
session_key ,
sizeof ( session_key ) ) ;
2012-07-26 04:36:37 +04:00
if ( session - > smb2 - > signing_key . data = = NULL ) {
2012-02-27 12:33:46 +04:00
ZERO_STRUCT ( session_key ) ;
2012-02-27 11:48:55 +04:00
return NT_STATUS_NO_MEMORY ;
2011-09-03 12:18:17 +04:00
}
2012-02-27 12:33:46 +04:00
if ( conn - > protocol > = PROTOCOL_SMB2_24 ) {
2012-04-16 12:25:12 +04:00
const DATA_BLOB label = data_blob_string_const_null ( " SMB2AESCMAC " ) ;
const DATA_BLOB context = data_blob_string_const_null ( " SmbSign " ) ;
2012-02-27 12:33:46 +04:00
2012-02-29 08:49:49 +04:00
smb2_key_derivation ( session_key , sizeof ( session_key ) ,
2012-02-27 12:33:46 +04:00
label . data , label . length ,
context . data , context . length ,
2012-07-26 04:36:37 +04:00
session - > smb2 - > signing_key . data ) ;
2012-02-27 12:33:46 +04:00
}
2012-07-26 04:36:37 +04:00
session - > smb2 - > encryption_key = data_blob_dup_talloc ( session ,
session - > smb2 - > signing_key ) ;
if ( session - > smb2 - > encryption_key . data = = NULL ) {
2012-07-20 11:30:05 +04:00
ZERO_STRUCT ( session_key ) ;
return NT_STATUS_NO_MEMORY ;
}
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 " ) ;
smb2_key_derivation ( session_key , sizeof ( session_key ) ,
label . data , label . length ,
context . data , context . length ,
2012-07-26 04:36:37 +04:00
session - > smb2 - > encryption_key . data ) ;
2012-07-20 11:30:05 +04:00
}
2012-07-26 04:36:37 +04:00
session - > smb2 - > decryption_key = data_blob_dup_talloc ( session ,
session - > smb2 - > signing_key ) ;
if ( session - > smb2 - > decryption_key . data = = NULL ) {
2012-07-20 11:30:05 +04:00
ZERO_STRUCT ( session_key ) ;
return NT_STATUS_NO_MEMORY ;
}
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 " ) ;
smb2_key_derivation ( session_key , sizeof ( session_key ) ,
label . data , label . length ,
context . data , context . length ,
2012-07-26 04:36:37 +04:00
session - > smb2 - > decryption_key . data ) ;
2012-07-20 11:30:05 +04:00
}
2012-07-26 04:36:37 +04:00
session - > smb2 - > application_key = data_blob_dup_talloc ( session ,
session - > smb2 - > signing_key ) ;
if ( session - > smb2 - > application_key . data = = NULL ) {
2012-02-27 12:33:46 +04:00
ZERO_STRUCT ( session_key ) ;
2012-02-27 12:03:54 +04:00
return NT_STATUS_NO_MEMORY ;
}
2012-02-27 12:33:46 +04:00
if ( conn - > protocol > = PROTOCOL_SMB2_24 ) {
2012-04-16 12:25:12 +04:00
const DATA_BLOB label = data_blob_string_const_null ( " SMB2APP " ) ;
const DATA_BLOB context = data_blob_string_const_null ( " SmbRpc " ) ;
2012-02-27 12:33:46 +04:00
2012-02-29 08:49:49 +04:00
smb2_key_derivation ( session_key , sizeof ( session_key ) ,
2012-02-27 12:33:46 +04:00
label . data , label . length ,
context . data , context . length ,
2012-07-26 04:36:37 +04:00
session - > smb2 - > application_key . data ) ;
2012-02-27 12:33:46 +04:00
}
ZERO_STRUCT ( session_key ) ;
2012-07-26 04:36:37 +04:00
session - > smb2_channel . signing_key = data_blob_dup_talloc ( session ,
session - > smb2 - > signing_key ) ;
if ( session - > smb2_channel . signing_key . data = = NULL ) {
2012-02-27 11:48:55 +04:00
return NT_STATUS_NO_MEMORY ;
2011-09-03 12:18:17 +04:00
}
2012-07-26 04:36:37 +04:00
status = smb2_signing_check_pdu ( session - > smb2_channel . signing_key ,
2012-02-27 12:29:53 +04:00
session - > conn - > protocol ,
2012-02-27 11:48:55 +04:00
recv_iov , 3 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2011-09-03 12:18:17 +04:00
}
2012-07-26 04:36:37 +04:00
session - > smb2 - > should_sign = false ;
session - > smb2 - > should_encrypt = false ;
2011-09-03 12:18:17 +04:00
if ( conn - > desire_signing ) {
2012-07-26 04:36:37 +04:00
session - > smb2 - > should_sign = true ;
2011-09-03 12:18:17 +04:00
}
if ( conn - > smb2 . server . security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED ) {
2012-07-26 04:36:37 +04:00
session - > smb2 - > should_sign = true ;
2011-09-03 12:18:17 +04:00
}
2012-07-26 04:36:37 +04:00
if ( session - > smb2 - > session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA ) {
session - > smb2 - > should_encrypt = true ;
2012-07-23 13:38:31 +04:00
}
if ( conn - > protocol < PROTOCOL_SMB2_24 ) {
2012-07-26 04:36:37 +04:00
session - > smb2 - > should_encrypt = false ;
2012-07-23 13:38:31 +04:00
}
if ( ! ( conn - > smb2 . server . capabilities & SMB2_CAP_ENCRYPTION ) ) {
2012-07-26 04:36:37 +04:00
session - > smb2 - > should_encrypt = false ;
2012-07-23 13:38:31 +04:00
}
2012-07-26 04:36:37 +04:00
generate_random_buffer ( ( uint8_t * ) & session - > smb2 - > nonce_high ,
sizeof ( session - > smb2 - > nonce_high ) ) ;
session - > smb2 - > nonce_low = 1 ;
2012-07-20 11:30:05 +04:00
2011-09-03 12:18:17 +04:00
return NT_STATUS_OK ;
}
2011-09-19 21:05:20 +04:00
NTSTATUS smb2cli_session_create_channel ( TALLOC_CTX * mem_ctx ,
struct smbXcli_session * session1 ,
struct smbXcli_conn * conn ,
struct smbXcli_session * * _session2 )
{
struct smbXcli_session * session2 ;
2012-07-26 04:36:37 +04:00
if ( session1 - > smb2 - > signing_key . length = = 0 ) {
2012-07-20 11:30:05 +04:00
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
2011-09-19 21:05:20 +04:00
if ( conn = = NULL ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
session2 = talloc_zero ( mem_ctx , struct smbXcli_session ) ;
if ( session2 = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2012-07-26 04:36:37 +04:00
session2 - > smb2 = talloc_reference ( session2 , session1 - > smb2 ) ;
if ( session2 - > smb2 = = NULL ) {
talloc_free ( session2 ) ;
2012-07-20 11:30:05 +04:00
return NT_STATUS_NO_MEMORY ;
}
2011-09-19 21:05:20 +04:00
talloc_set_destructor ( session2 , smbXcli_session_destructor ) ;
DLIST_ADD_END ( conn - > sessions , session2 , struct smbXcli_session * ) ;
session2 - > conn = conn ;
* _session2 = session2 ;
return NT_STATUS_OK ;
}
2012-02-27 11:48:55 +04:00
NTSTATUS smb2cli_session_set_channel_key ( struct smbXcli_session * session ,
const DATA_BLOB _channel_key ,
const struct iovec * recv_iov )
{
struct smbXcli_conn * conn = session - > conn ;
uint8_t channel_key [ 16 ] ;
NTSTATUS status ;
if ( conn = = NULL ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
2012-07-26 04:36:37 +04:00
if ( session - > smb2_channel . signing_key . length ! = 0 ) {
2012-02-27 11:48:55 +04:00
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
ZERO_STRUCT ( channel_key ) ;
memcpy ( channel_key , _channel_key . data ,
MIN ( _channel_key . length , sizeof ( channel_key ) ) ) ;
2012-07-26 04:36:37 +04:00
session - > smb2_channel . signing_key = data_blob_talloc ( session ,
2012-02-27 11:48:55 +04:00
channel_key ,
sizeof ( channel_key ) ) ;
2012-07-26 04:36:37 +04:00
if ( session - > smb2_channel . signing_key . data = = NULL ) {
2012-02-27 12:33:46 +04:00
ZERO_STRUCT ( channel_key ) ;
2012-02-27 11:48:55 +04:00
return NT_STATUS_NO_MEMORY ;
}
2012-02-27 12:33:46 +04:00
if ( conn - > protocol > = PROTOCOL_SMB2_24 ) {
2012-04-16 12:25:12 +04:00
const DATA_BLOB label = data_blob_string_const_null ( " SMB2AESCMAC " ) ;
const DATA_BLOB context = data_blob_string_const_null ( " SmbSign " ) ;
2012-02-27 12:33:46 +04:00
2012-02-29 08:49:49 +04:00
smb2_key_derivation ( channel_key , sizeof ( channel_key ) ,
2012-02-27 12:33:46 +04:00
label . data , label . length ,
context . data , context . length ,
2012-07-26 04:36:37 +04:00
session - > smb2_channel . signing_key . data ) ;
2012-02-27 12:33:46 +04:00
}
ZERO_STRUCT ( channel_key ) ;
2012-07-26 04:36:37 +04:00
status = smb2_signing_check_pdu ( session - > smb2_channel . signing_key ,
2012-02-27 12:29:53 +04:00
session - > conn - > protocol ,
2012-02-27 11:48:55 +04:00
recv_iov , 3 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
return NT_STATUS_OK ;
}
2012-06-02 20:03:20 +04:00
struct smbXcli_tcon * smbXcli_tcon_create ( TALLOC_CTX * mem_ctx )
{
struct smbXcli_tcon * tcon ;
tcon = talloc_zero ( mem_ctx , struct smbXcli_tcon ) ;
if ( tcon = = NULL ) {
return NULL ;
}
return tcon ;
}
uint16_t smb1cli_tcon_current_id ( struct smbXcli_tcon * tcon )
{
return tcon - > smb1 . tcon_id ;
}
void smb1cli_tcon_set_id ( struct smbXcli_tcon * tcon , uint16_t tcon_id )
{
tcon - > smb1 . tcon_id = tcon_id ;
}
bool smb1cli_tcon_set_values ( struct smbXcli_tcon * tcon ,
uint16_t tcon_id ,
uint16_t optional_support ,
uint32_t maximal_access ,
uint32_t guest_maximal_access ,
const char * service ,
const char * fs_type )
{
tcon - > smb1 . tcon_id = tcon_id ;
tcon - > smb1 . optional_support = optional_support ;
tcon - > smb1 . maximal_access = maximal_access ;
tcon - > smb1 . guest_maximal_access = guest_maximal_access ;
TALLOC_FREE ( tcon - > smb1 . service ) ;
tcon - > smb1 . service = talloc_strdup ( tcon , service ) ;
if ( service ! = NULL & & tcon - > smb1 . service = = NULL ) {
return false ;
}
TALLOC_FREE ( tcon - > smb1 . fs_type ) ;
tcon - > smb1 . fs_type = talloc_strdup ( tcon , fs_type ) ;
if ( fs_type ! = NULL & & tcon - > smb1 . fs_type = = NULL ) {
return false ;
}
return true ;
}
uint32_t smb2cli_tcon_current_id ( struct smbXcli_tcon * tcon )
{
return tcon - > smb2 . tcon_id ;
}
2012-07-25 11:27:00 +04:00
uint32_t smb2cli_tcon_capabilities ( struct smbXcli_tcon * tcon )
{
return tcon - > smb2 . capabilities ;
}
2012-06-02 20:03:20 +04:00
void smb2cli_tcon_set_values ( struct smbXcli_tcon * tcon ,
2012-07-25 14:33:39 +04:00
struct smbXcli_session * session ,
2012-06-02 20:03:20 +04:00
uint32_t tcon_id ,
uint8_t type ,
uint32_t flags ,
uint32_t capabilities ,
uint32_t maximal_access )
{
tcon - > smb2 . tcon_id = tcon_id ;
tcon - > smb2 . type = type ;
tcon - > smb2 . flags = flags ;
tcon - > smb2 . capabilities = capabilities ;
tcon - > smb2 . maximal_access = maximal_access ;
2012-07-25 14:34:20 +04:00
tcon - > smb2 . should_encrypt = false ;
if ( session = = NULL ) {
return ;
}
2012-07-26 04:36:37 +04:00
tcon - > smb2 . should_encrypt = session - > smb2 - > should_encrypt ;
2012-07-25 14:34:20 +04:00
if ( flags & SMB2_SHAREFLAG_ENCRYPT_DATA ) {
tcon - > smb2 . should_encrypt = true ;
}
2012-06-02 20:03:20 +04:00
}