2009-05-14 16:17:28 +04:00
/*
Unix SMB / CIFS implementation .
Core SMB2 server
Copyright ( C ) Stefan Metzmacher 2009
2010-04-29 01:56:12 +04:00
Copyright ( C ) Jeremy Allison 2010
2009-05-14 16:17:28 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2011-03-22 18:57:01 +03:00
# include "smbd/smbd.h"
2009-05-14 16:17:28 +04:00
# include "smbd/globals.h"
2009-08-12 19:52:55 +04:00
# include "../libcli/smb/smb_common.h"
2009-05-14 16:17:28 +04:00
# include "../lib/tsocket/tsocket.h"
2011-04-28 19:38:09 +04:00
# include "../lib/util/tevent_ntstatus.h"
2011-04-14 02:36:23 +04:00
# include "smbprofile.h"
2011-07-07 15:04:31 +04:00
# include "../lib/util/bitmap.h"
2011-07-08 21:44:29 +04:00
# include "../librpc/gen_ndr/krb5pac.h"
# include "auth.h"
2009-05-14 16:17:28 +04:00
2010-04-20 00:43:42 +04:00
# define OUTVEC_ALLOC_SIZE (SMB2_HDR_BODY + 9)
2012-08-06 11:29:40 +04:00
static const struct smbd_smb2_dispatch_table {
uint16_t opcode ;
const char * name ;
2012-08-06 12:02:54 +04:00
bool need_session ;
2012-08-06 12:04:48 +04:00
bool need_tcon ;
2012-08-06 12:04:48 +04:00
bool as_root ;
2012-08-07 11:44:31 +04:00
uint16_t fileid_ofs ;
bool allow_invalid_fileid ;
2012-08-06 11:29:40 +04:00
} smbd_smb2_table [ ] = {
# define _OP(o) .opcode = o, .name = #o
{
_OP ( SMB2_OP_NEGPROT ) ,
2012-08-06 12:04:48 +04:00
. as_root = true ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_SESSSETUP ) ,
2012-08-06 12:04:48 +04:00
. as_root = true ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_LOGOFF ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. as_root = true ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_TCON ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
/*
* This call needs to be run as root .
*
* smbd_smb2_request_process_tcon ( )
* calls make_connection_snum ( ) , which will call
* change_to_user ( ) , when needed .
*/
. as_root = true ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_TDIS ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. need_tcon = true ,
2012-08-06 12:04:48 +04:00
. as_root = true ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_CREATE ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. need_tcon = true ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_CLOSE ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. need_tcon = true ,
2012-08-07 11:44:31 +04:00
. fileid_ofs = 0x08 ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_FLUSH ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. need_tcon = true ,
2012-08-07 11:44:31 +04:00
. fileid_ofs = 0x08 ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_READ ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. need_tcon = true ,
2012-08-07 11:44:31 +04:00
. fileid_ofs = 0x10 ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_WRITE ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. need_tcon = true ,
2012-08-07 11:44:31 +04:00
. fileid_ofs = 0x10 ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_LOCK ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. need_tcon = true ,
2012-08-07 11:44:31 +04:00
. fileid_ofs = 0x08 ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_IOCTL ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. need_tcon = true ,
2012-08-07 11:44:31 +04:00
. fileid_ofs = 0x08 ,
. allow_invalid_fileid = true ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_CANCEL ) ,
2012-08-06 12:04:48 +04:00
. as_root = true ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_KEEPALIVE ) ,
2012-08-06 12:04:48 +04:00
. as_root = true ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_FIND ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. need_tcon = true ,
2012-08-07 11:44:31 +04:00
. fileid_ofs = 0x08 ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_NOTIFY ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. need_tcon = true ,
2012-08-07 11:44:31 +04:00
. fileid_ofs = 0x08 ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_GETINFO ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. need_tcon = true ,
2012-08-07 11:44:31 +04:00
. fileid_ofs = 0x18 ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_SETINFO ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. need_tcon = true ,
2012-08-07 11:44:31 +04:00
. fileid_ofs = 0x10 ,
2012-08-06 11:29:40 +04:00
} , {
_OP ( SMB2_OP_BREAK ) ,
2012-08-06 12:02:54 +04:00
. need_session = true ,
2012-08-06 12:04:48 +04:00
. need_tcon = true ,
2012-08-07 11:44:31 +04:00
/*
* we do not set
* . fileid_ofs here
* as LEASE breaks does not
* have a file id
*/
2012-08-06 11:29:40 +04:00
}
2010-04-18 08:20:17 +04:00
} ;
const char * smb2_opcode_name ( uint16_t opcode )
{
2012-08-06 11:29:40 +04:00
if ( opcode > = ARRAY_SIZE ( smbd_smb2_table ) ) {
2010-04-18 08:20:17 +04:00
return " Bad SMB2 opcode " ;
}
2012-08-06 11:29:40 +04:00
return smbd_smb2_table [ opcode ] . name ;
2010-04-18 08:20:17 +04:00
}
2012-08-06 12:42:30 +04:00
static const struct smbd_smb2_dispatch_table * smbd_smb2_call ( uint16_t opcode )
{
const struct smbd_smb2_dispatch_table * ret = NULL ;
if ( opcode > = ARRAY_SIZE ( smbd_smb2_table ) ) {
return NULL ;
}
ret = & smbd_smb2_table [ opcode ] ;
SMB_ASSERT ( ret - > opcode = = opcode ) ;
return ret ;
}
2012-08-16 14:00:57 +04:00
static void print_req_vectors ( const struct smbd_smb2_request * req )
2010-04-18 08:20:17 +04:00
{
int i ;
for ( i = 0 ; i < req - > in . vector_count ; i + + ) {
dbgtext ( " \t req->in.vector[%u].iov_len = %u \n " ,
( unsigned int ) i ,
( unsigned int ) req - > in . vector [ i ] . iov_len ) ;
}
for ( i = 0 ; i < req - > out . vector_count ; i + + ) {
dbgtext ( " \t req->out.vector[%u].iov_len = %u \n " ,
( unsigned int ) i ,
( unsigned int ) req - > out . vector [ i ] . iov_len ) ;
}
}
2009-05-14 16:17:28 +04:00
bool smbd_is_smb2_header ( const uint8_t * inbuf , size_t size )
{
if ( size < ( 4 + SMB2_HDR_BODY ) ) {
return false ;
}
if ( IVAL ( inbuf , 4 ) ! = SMB2_MAGIC ) {
return false ;
}
return true ;
}
2009-08-07 17:21:07 +04:00
static NTSTATUS smbd_initialize_smb2 ( struct smbd_server_connection * sconn )
2009-05-14 16:17:28 +04:00
{
NTSTATUS status ;
int ret ;
2009-08-07 17:21:07 +04:00
TALLOC_FREE ( sconn - > smb1 . fde ) ;
2009-05-14 16:17:28 +04:00
2009-08-07 17:21:07 +04:00
sconn - > smb2 . recv_queue = tevent_queue_create ( sconn , " smb2 recv queue " ) ;
if ( sconn - > smb2 . recv_queue = = NULL ) {
2009-05-14 16:17:28 +04:00
return NT_STATUS_NO_MEMORY ;
}
2009-08-07 17:21:07 +04:00
sconn - > smb2 . send_queue = tevent_queue_create ( sconn , " smb2 send queue " ) ;
if ( sconn - > smb2 . send_queue = = NULL ) {
2009-05-14 16:17:28 +04:00
return NT_STATUS_NO_MEMORY ;
}
2010-12-11 02:46:41 +03:00
sconn - > smb2 . seqnum_low = 0 ;
2012-06-27 17:33:43 +04:00
sconn - > smb2 . seqnum_range = 1 ;
2012-06-26 16:23:12 +04:00
sconn - > smb2 . credits_granted = 1 ;
2010-12-11 02:46:41 +03:00
sconn - > smb2 . max_credits = lp_smb2_max_credits ( ) ;
2010-12-22 05:07:52 +03:00
sconn - > smb2 . credits_bitmap = bitmap_talloc ( sconn ,
2012-06-26 01:38:32 +04:00
sconn - > smb2 . max_credits ) ;
2010-12-11 02:46:41 +03:00
if ( sconn - > smb2 . credits_bitmap = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2009-05-15 13:20:34 +04:00
2010-08-15 01:13:11 +04:00
ret = tstream_bsd_existing_socket ( sconn , sconn - > sock ,
2009-08-07 17:21:07 +04:00
& sconn - > smb2 . stream ) ;
2009-05-14 16:17:28 +04:00
if ( ret = = - 1 ) {
status = map_nt_error_from_unix ( errno ) ;
return status ;
}
/* Ensure child is set to non-blocking mode */
2010-08-15 01:13:11 +04:00
set_blocking ( sconn - > sock , false ) ;
2009-05-14 16:17:28 +04:00
return NT_STATUS_OK ;
}
# define smb2_len(buf) (PVAL(buf,3)|(PVAL(buf,2)<<8)|(PVAL(buf,1)<<16))
# define _smb2_setlen(_buf,len) do { \
uint8_t * buf = ( uint8_t * ) _buf ; \
buf [ 0 ] = 0 ; \
buf [ 1 ] = ( ( len ) & 0xFF0000 ) > > 16 ; \
buf [ 2 ] = ( ( len ) & 0xFF00 ) > > 8 ; \
buf [ 3 ] = ( len ) & 0xFF ; \
} while ( 0 )
static void smb2_setup_nbt_length ( struct iovec * vector , int count )
{
size_t len = 0 ;
int i ;
for ( i = 1 ; i < count ; i + + ) {
len + = vector [ i ] . iov_len ;
}
_smb2_setlen ( vector [ 0 ] . iov_base , len ) ;
}
2012-08-07 16:24:28 +04:00
static int smbd_smb2_request_destructor ( struct smbd_smb2_request * req )
{
2012-08-08 09:07:03 +04:00
data_blob_clear_free ( & req - > first_key ) ;
2012-08-07 16:24:28 +04:00
data_blob_clear_free ( & req - > last_key ) ;
return 0 ;
}
2009-06-09 23:29:40 +04:00
static struct smbd_smb2_request * smbd_smb2_request_allocate ( TALLOC_CTX * mem_ctx )
{
TALLOC_CTX * mem_pool ;
struct smbd_smb2_request * req ;
2010-04-23 10:52:19 +04:00
#if 0
/* Enable this to find subtle valgrind errors. */
mem_pool = talloc_init ( " smbd_smb2_request_allocate " ) ;
# else
2009-06-09 23:29:40 +04:00
mem_pool = talloc_pool ( mem_ctx , 8192 ) ;
2010-04-23 10:52:19 +04:00
# endif
2009-06-09 23:29:40 +04:00
if ( mem_pool = = NULL ) {
return NULL ;
}
2012-07-23 20:22:59 +04:00
req = talloc_zero ( mem_pool , struct smbd_smb2_request ) ;
2009-06-09 23:29:40 +04:00
if ( req = = NULL ) {
talloc_free ( mem_pool ) ;
return NULL ;
}
2012-07-23 20:22:59 +04:00
talloc_reparent ( mem_pool , mem_ctx , req ) ;
TALLOC_FREE ( mem_pool ) ;
2009-06-09 23:29:40 +04:00
2011-10-27 18:35:28 +04:00
req - > last_session_id = UINT64_MAX ;
req - > last_tid = UINT32_MAX ;
2012-08-07 16:24:28 +04:00
talloc_set_destructor ( req , smbd_smb2_request_destructor ) ;
2009-06-09 23:29:40 +04:00
return req ;
}
2012-08-05 21:39:39 +04:00
static NTSTATUS smbd_smb2_inbuf_parse_compound ( struct smbXsrv_connection * conn ,
NTTIME now ,
uint8_t * buf ,
size_t buflen ,
TALLOC_CTX * mem_ctx ,
struct iovec * * piov ,
int * pnum_iov )
{
struct iovec * iov ;
int num_iov = 1 ;
size_t taken = 0 ;
uint8_t * first_hdr = buf ;
2012-08-08 09:07:03 +04:00
size_t verified_buflen = 0 ;
uint8_t * tf = NULL ;
size_t tf_len = 0 ;
2012-08-05 21:39:39 +04:00
/*
* Note : index ' 0 ' is reserved for the transport protocol
*/
iov = talloc_zero_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 ;
2012-08-07 14:41:07 +04:00
uint8_t * body = NULL ;
uint32_t dyn_size ;
uint8_t * dyn = NULL ;
2012-08-05 21:39:39 +04:00
struct iovec * iov_tmp ;
2012-08-08 09:07:03 +04:00
if ( verified_buflen > taken ) {
len = verified_buflen - taken ;
} else {
tf = NULL ;
tf_len = 0 ;
}
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 smbXsrv_session * s = NULL ;
uint64_t uid ;
struct iovec tf_iov [ 2 ] ;
NTSTATUS status ;
size_t enc_len ;
if ( conn - > protocol < PROTOCOL_SMB2_24 ) {
DEBUG ( 10 , ( " Got SMB2_TRANSFORM header, "
" but dialect[0x%04X] is used \n " ,
conn - > smb2 . server . dialect ) ) ;
goto inval ;
}
if ( ! ( conn - > smb2 . server . capabilities & SMB2_CAP_ENCRYPTION ) ) {
DEBUG ( 10 , ( " Got SMB2_TRANSFORM header, "
" but not negotiated "
" client[0x%08X] server[0x%08X] \n " ,
conn - > smb2 . client . capabilities ,
conn - > smb2 . server . capabilities ) ) ;
goto inval ;
}
if ( len < SMB2_TF_HDR_SIZE ) {
DEBUG ( 1 , ( " %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 ;
enc_len = IVAL ( tf , SMB2_TF_MSG_SIZE ) ;
uid = BVAL ( tf , SMB2_TF_SESSION_ID ) ;
if ( len < SMB2_TF_HDR_SIZE + enc_len ) {
DEBUG ( 1 , ( " %d bytes left, expected at least %d \n " ,
( int ) len ,
( int ) ( SMB2_TF_HDR_SIZE + enc_len ) ) ) ;
goto inval ;
}
status = smb2srv_session_lookup ( conn , uid , now , & s ) ;
if ( s = = NULL ) {
DEBUG ( 1 , ( " invalid session[%llu] in "
" SMB2_TRANSFORM header \n " ,
( unsigned long long ) uid ) ) ;
TALLOC_FREE ( iov ) ;
return NT_STATUS_USER_SESSION_DELETED ;
}
tf_iov [ 0 ] . iov_base = ( void * ) tf ;
tf_iov [ 0 ] . iov_len = tf_len ;
tf_iov [ 1 ] . iov_base = ( void * ) hdr ;
tf_iov [ 1 ] . iov_len = enc_len ;
status = smb2_signing_decrypt_pdu ( s - > global - > decryption_key ,
conn - > protocol ,
tf_iov , 2 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( iov ) ;
return status ;
}
verified_buflen = taken + enc_len ;
len = enc_len ;
}
2012-08-05 21:39:39 +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 ) ) {
/*
* let the caller handle the error
*/
body_size = full_size - SMB2_HDR_BODY ;
}
2012-08-07 14:41:07 +04:00
body = hdr + SMB2_HDR_BODY ;
dyn = body + body_size ;
dyn_size = full_size - ( SMB2_HDR_BODY + body_size ) ;
2012-08-05 21:39:39 +04:00
iov_tmp = talloc_realloc ( mem_ctx , iov , struct iovec ,
2012-08-07 14:26:38 +04:00
num_iov + SMBD_SMB2_NUM_IOV_PER_REQ ) ;
2012-08-05 21:39:39 +04:00
if ( iov_tmp = = NULL ) {
TALLOC_FREE ( iov ) ;
return NT_STATUS_NO_MEMORY ;
}
iov = iov_tmp ;
cur = & iov [ num_iov ] ;
2012-08-07 14:26:38 +04:00
num_iov + = SMBD_SMB2_NUM_IOV_PER_REQ ;
2012-08-05 21:39:39 +04:00
2012-08-08 09:07:03 +04:00
cur [ SMBD_SMB2_TF_IOV_OFS ] . iov_base = tf ;
cur [ SMBD_SMB2_TF_IOV_OFS ] . iov_len = tf_len ;
2012-08-07 14:41:07 +04:00
cur [ SMBD_SMB2_HDR_IOV_OFS ] . iov_base = hdr ;
cur [ SMBD_SMB2_HDR_IOV_OFS ] . iov_len = SMB2_HDR_BODY ;
cur [ SMBD_SMB2_BODY_IOV_OFS ] . iov_base = body ;
cur [ SMBD_SMB2_BODY_IOV_OFS ] . iov_len = body_size ;
cur [ SMBD_SMB2_DYN_IOV_OFS ] . iov_base = dyn ;
cur [ SMBD_SMB2_DYN_IOV_OFS ] . iov_len = dyn_size ;
2012-08-05 21:39:39 +04:00
taken + = full_size ;
}
* piov = iov ;
* pnum_iov = num_iov ;
return NT_STATUS_OK ;
inval :
TALLOC_FREE ( iov ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
2009-08-07 17:21:07 +04:00
static NTSTATUS smbd_smb2_request_create ( struct smbd_server_connection * sconn ,
2012-08-05 22:48:51 +04:00
uint8_t * inbuf , size_t size ,
2009-05-14 16:17:28 +04:00
struct smbd_smb2_request * * _req )
{
struct smbd_smb2_request * req ;
uint32_t protocol_version ;
const uint8_t * inhdr = NULL ;
uint16_t cmd ;
uint32_t next_command_ofs ;
2012-08-05 22:48:51 +04:00
NTSTATUS status ;
NTTIME now ;
2009-05-14 16:17:28 +04:00
if ( size < ( 4 + SMB2_HDR_BODY + 2 ) ) {
DEBUG ( 0 , ( " Invalid SMB2 packet length count %ld \n " , ( long ) size ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
inhdr = inbuf + 4 ;
protocol_version = IVAL ( inhdr , SMB2_HDR_PROTOCOL_ID ) ;
if ( protocol_version ! = SMB2_MAGIC ) {
DEBUG ( 0 , ( " Invalid SMB packet: protocol prefix: 0x%08X \n " ,
protocol_version ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
cmd = SVAL ( inhdr , SMB2_HDR_OPCODE ) ;
if ( cmd ! = SMB2_OP_NEGPROT ) {
DEBUG ( 0 , ( " Invalid SMB packet: first request: 0x%04X \n " ,
cmd ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
next_command_ofs = IVAL ( inhdr , SMB2_HDR_NEXT_COMMAND ) ;
if ( next_command_ofs ! = 0 ) {
DEBUG ( 0 , ( " Invalid SMB packet: next_command: 0x%08X \n " ,
next_command_ofs ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
2009-08-07 17:21:07 +04:00
req = smbd_smb2_request_allocate ( sconn ) ;
2009-05-14 16:17:28 +04:00
if ( req = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2009-08-07 17:21:07 +04:00
req - > sconn = sconn ;
2009-05-14 16:17:28 +04:00
talloc_steal ( req , inbuf ) ;
2012-08-05 22:48:51 +04:00
req - > request_time = timeval_current ( ) ;
now = timeval_to_nttime ( & req - > request_time ) ;
2009-05-14 16:17:28 +04:00
2012-08-05 22:48:51 +04:00
status = smbd_smb2_inbuf_parse_compound ( sconn - > conn ,
now ,
inbuf + NBT_HDR_SIZE ,
size - NBT_HDR_SIZE ,
req , & req - > in . vector ,
& req - > in . vector_count ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( req ) ;
return status ;
2009-05-14 16:17:28 +04:00
}
req - > current_idx = 1 ;
* _req = req ;
return NT_STATUS_OK ;
}
2012-06-26 01:17:55 +04:00
static bool smb2_validate_sequence_number ( struct smbd_server_connection * sconn ,
uint64_t message_id , uint64_t seq_id )
{
struct bitmap * credits_bm = sconn - > smb2 . credits_bitmap ;
unsigned int offset ;
if ( seq_id < sconn - > smb2 . seqnum_low ) {
DEBUG ( 0 , ( " smb2_validate_sequence_number: bad message_id "
2012-06-27 17:33:43 +04:00
" %llu (sequence id %llu) "
" (granted = %u, low = %llu, range = %u) \n " ,
2012-06-26 01:17:55 +04:00
( unsigned long long ) message_id ,
( unsigned long long ) seq_id ,
2012-06-27 17:33:43 +04:00
( unsigned int ) sconn - > smb2 . credits_granted ,
2012-06-26 01:17:55 +04:00
( unsigned long long ) sconn - > smb2 . seqnum_low ,
2012-06-27 17:33:43 +04:00
( unsigned int ) sconn - > smb2 . seqnum_range ) ) ;
2012-06-26 01:17:55 +04:00
return false ;
}
2012-06-27 17:33:43 +04:00
if ( seq_id > = sconn - > smb2 . seqnum_low + sconn - > smb2 . seqnum_range ) {
2012-06-26 01:17:55 +04:00
DEBUG ( 0 , ( " smb2_validate_sequence_number: bad message_id "
2012-06-27 17:33:43 +04:00
" %llu (sequence id %llu) "
" (granted = %u, low = %llu, range = %u) \n " ,
2012-06-26 01:17:55 +04:00
( unsigned long long ) message_id ,
( unsigned long long ) seq_id ,
2012-06-27 17:33:43 +04:00
( unsigned int ) sconn - > smb2 . credits_granted ,
2012-06-26 01:17:55 +04:00
( unsigned long long ) sconn - > smb2 . seqnum_low ,
2012-06-27 17:33:43 +04:00
( unsigned int ) sconn - > smb2 . seqnum_range ) ) ;
2012-06-26 01:17:55 +04:00
return false ;
}
offset = seq_id % sconn - > smb2 . max_credits ;
if ( bitmap_query ( credits_bm , offset ) ) {
DEBUG ( 0 , ( " smb2_validate_sequence_number: duplicate message_id "
2012-06-27 17:33:43 +04:00
" %llu (sequence id %llu) "
" (granted = %u, low = %llu, range = %u) "
2012-06-26 01:17:55 +04:00
" (bm offset %u) \n " ,
( unsigned long long ) message_id ,
( unsigned long long ) seq_id ,
2012-06-27 17:33:43 +04:00
( unsigned int ) sconn - > smb2 . credits_granted ,
2012-06-26 01:17:55 +04:00
( unsigned long long ) sconn - > smb2 . seqnum_low ,
2012-06-27 17:33:43 +04:00
( unsigned int ) sconn - > smb2 . seqnum_range ,
2012-06-26 01:17:55 +04:00
offset ) ) ;
return false ;
}
/* Mark the message_ids as seen in the bitmap. */
bitmap_set ( credits_bm , offset ) ;
if ( seq_id ! = sconn - > smb2 . seqnum_low ) {
return true ;
}
/*
* Move the window forward by all the message_id ' s
* already seen .
*/
while ( bitmap_query ( credits_bm , offset ) ) {
DEBUG ( 10 , ( " smb2_validate_sequence_number: clearing "
" id %llu (position %u) from bitmap \n " ,
( unsigned long long ) ( sconn - > smb2 . seqnum_low ) ,
offset ) ) ;
bitmap_clear ( credits_bm , offset ) ;
sconn - > smb2 . seqnum_low + = 1 ;
2012-06-27 17:33:43 +04:00
sconn - > smb2 . seqnum_range - = 1 ;
2012-06-26 01:17:55 +04:00
offset = sconn - > smb2 . seqnum_low % sconn - > smb2 . max_credits ;
}
return true ;
}
2010-12-11 02:46:41 +03:00
static bool smb2_validate_message_id ( struct smbd_server_connection * sconn ,
const uint8_t * inhdr )
{
uint64_t message_id = BVAL ( inhdr , SMB2_HDR_MESSAGE_ID ) ;
uint16_t opcode = IVAL ( inhdr , SMB2_HDR_OPCODE ) ;
2012-02-28 05:53:04 +04:00
uint16_t credit_charge = 1 ;
uint64_t i ;
2010-12-11 02:46:41 +03:00
if ( opcode = = SMB2_OP_CANCEL ) {
/* SMB2_CANCEL requests by definition resend messageids. */
return true ;
}
2012-02-28 05:53:04 +04:00
if ( sconn - > smb2 . supports_multicredit ) {
2012-03-10 04:47:21 +04:00
credit_charge = SVAL ( inhdr , SMB2_HDR_CREDIT_CHARGE ) ;
2012-02-28 05:53:04 +04:00
credit_charge = MAX ( credit_charge , 1 ) ;
2010-12-11 02:46:41 +03:00
}
2012-02-28 05:53:04 +04:00
2012-06-27 17:33:43 +04:00
DEBUG ( 11 , ( " smb2_validate_message_id: mid %llu (charge %llu), "
" credits_granted %llu, "
" seqnum low/range: %llu/%llu \n " ,
2012-02-28 05:53:04 +04:00
( unsigned long long ) message_id ,
( unsigned long long ) credit_charge ,
2012-06-27 17:33:43 +04:00
( unsigned long long ) sconn - > smb2 . credits_granted ,
( unsigned long long ) sconn - > smb2 . seqnum_low ,
( unsigned long long ) sconn - > smb2 . seqnum_range ) ) ;
2012-02-28 05:53:04 +04:00
2012-06-26 16:28:07 +04:00
if ( sconn - > smb2 . credits_granted < credit_charge ) {
DEBUG ( 0 , ( " smb2_validate_message_id: client used more "
2012-06-27 17:33:43 +04:00
" credits than granted, mid %llu, charge %llu, "
" credits_granted %llu, "
" seqnum low/range: %llu/%llu \n " ,
2012-06-26 16:28:07 +04:00
( unsigned long long ) message_id ,
( unsigned long long ) credit_charge ,
2012-06-27 17:33:43 +04:00
( unsigned long long ) sconn - > smb2 . credits_granted ,
( unsigned long long ) sconn - > smb2 . seqnum_low ,
( unsigned long long ) sconn - > smb2 . seqnum_range ) ) ;
2012-06-26 16:28:07 +04:00
return false ;
}
2012-02-28 05:53:04 +04:00
/*
* now check the message ids
*
* for multi - credit requests we need to check all current mid plus
* the implicit mids caused by the credit charge
* e . g . current mid = 15 , charge 5 = > mark 15 - 19 as used
*/
2012-06-26 01:17:55 +04:00
for ( i = 0 ; i < = ( credit_charge - 1 ) ; i + + ) {
uint64_t id = message_id + i ;
bool ok ;
2012-02-28 05:53:04 +04:00
2012-06-26 01:17:55 +04:00
DEBUG ( 11 , ( " Iterating mid %llu charge %u (sequence %llu) \n " ,
( unsigned long long ) message_id ,
credit_charge ,
( unsigned long long ) id ) ) ;
2012-02-28 05:53:04 +04:00
2012-06-26 01:17:55 +04:00
ok = smb2_validate_sequence_number ( sconn , message_id , id ) ;
if ( ! ok ) {
2012-02-28 05:53:04 +04:00
return false ;
}
2010-12-11 02:46:41 +03:00
}
2012-06-26 01:17:55 +04:00
/* substract used credits */
sconn - > smb2 . credits_granted - = credit_charge ;
2010-12-11 02:46:41 +03:00
return true ;
}
static NTSTATUS smbd_smb2_request_validate ( struct smbd_smb2_request * req )
2009-06-08 15:30:32 +04:00
{
int count ;
int idx ;
count = req - > in . vector_count ;
2012-08-07 14:26:38 +04:00
if ( count < 1 + SMBD_SMB2_NUM_IOV_PER_REQ ) {
2009-06-08 15:30:32 +04:00
/* It's not a SMB2 request */
return NT_STATUS_INVALID_PARAMETER ;
}
2012-08-07 14:26:38 +04:00
for ( idx = 1 ; idx < count ; idx + = SMBD_SMB2_NUM_IOV_PER_REQ ) {
2012-08-07 14:30:54 +04:00
struct iovec * hdr = SMBD_SMB2_IDX_HDR_IOV ( req , in , idx ) ;
struct iovec * body = SMBD_SMB2_IDX_BODY_IOV ( req , in , idx ) ;
2009-06-08 15:30:32 +04:00
const uint8_t * inhdr = NULL ;
2012-08-07 14:30:54 +04:00
if ( hdr - > iov_len ! = SMB2_HDR_BODY ) {
2009-06-08 15:30:32 +04:00
return NT_STATUS_INVALID_PARAMETER ;
}
2012-08-07 14:30:54 +04:00
if ( body - > iov_len < 2 ) {
2009-06-08 15:30:32 +04:00
return NT_STATUS_INVALID_PARAMETER ;
}
2012-08-07 14:30:54 +04:00
inhdr = ( const uint8_t * ) hdr - > iov_base ;
2009-06-08 15:30:32 +04:00
2010-12-11 02:46:41 +03:00
/* Check the SMB2 header */
2009-06-08 15:30:32 +04:00
if ( IVAL ( inhdr , SMB2_HDR_PROTOCOL_ID ) ! = SMB2_MAGIC ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2010-12-11 02:46:41 +03:00
if ( ! smb2_validate_message_id ( req - > sconn , inhdr ) ) {
return NT_STATUS_INVALID_PARAMETER ;
2010-04-07 03:31:26 +04:00
}
2009-06-08 15:30:32 +04:00
}
return NT_STATUS_OK ;
}
2010-12-11 02:46:41 +03:00
static void smb2_set_operation_credit ( struct smbd_server_connection * sconn ,
const struct iovec * in_vector ,
struct iovec * out_vector )
{
2011-08-06 12:19:21 +04:00
const uint8_t * inhdr = ( const uint8_t * ) in_vector - > iov_base ;
2011-01-09 21:54:33 +03:00
uint8_t * outhdr = ( uint8_t * ) out_vector - > iov_base ;
2012-06-25 23:45:54 +04:00
uint16_t credit_charge = 1 ;
2011-08-06 12:19:21 +04:00
uint16_t credits_requested ;
uint32_t out_flags ;
2012-06-27 17:33:43 +04:00
uint16_t cmd ;
NTSTATUS out_status ;
2010-12-11 02:46:41 +03:00
uint16_t credits_granted = 0 ;
2012-06-27 17:33:43 +04:00
uint64_t credits_possible ;
2012-06-27 17:33:43 +04:00
uint16_t current_max_credits ;
2010-12-11 02:46:41 +03:00
2012-06-27 17:33:43 +04:00
/*
* first we grant only 1 / 16 th of the max range .
*
* Windows also starts with the 1 / 16 th and then grants
* more later . I was only able to trigger higher
2012-11-01 20:55:59 +04:00
* values , when using a very high credit charge .
2012-06-27 17:33:43 +04:00
*
2012-11-02 13:41:01 +04:00
* TODO : scale up depending on load , free memory
2012-06-27 17:33:43 +04:00
* or other stuff .
* Maybe also on the relationship between number
* of requests and the used sequence number .
* Which means we would grant more credits
* for client which use multi credit requests .
*/
current_max_credits = sconn - > smb2 . max_credits / 16 ;
current_max_credits = MAX ( current_max_credits , 1 ) ;
2012-06-25 23:45:54 +04:00
if ( sconn - > smb2 . supports_multicredit ) {
credit_charge = SVAL ( inhdr , SMB2_HDR_CREDIT_CHARGE ) ;
credit_charge = MAX ( credit_charge , 1 ) ;
}
2012-06-27 17:33:43 +04:00
cmd = SVAL ( inhdr , SMB2_HDR_OPCODE ) ;
2011-08-06 12:19:21 +04:00
credits_requested = SVAL ( inhdr , SMB2_HDR_CREDIT ) ;
out_flags = IVAL ( outhdr , SMB2_HDR_FLAGS ) ;
2012-06-27 17:33:43 +04:00
out_status = NT_STATUS ( IVAL ( outhdr , SMB2_HDR_STATUS ) ) ;
2010-12-11 02:46:41 +03:00
SMB_ASSERT ( sconn - > smb2 . max_credits > = sconn - > smb2 . credits_granted ) ;
2012-10-03 04:30:54 +04:00
if ( sconn - > smb2 . max_credits < credit_charge ) {
smbd_server_connection_terminate ( sconn ,
" client error: credit charge > max credits \n " ) ;
return ;
}
2010-12-11 02:46:41 +03:00
2011-08-06 12:19:21 +04:00
if ( out_flags & SMB2_HDR_FLAG_ASYNC ) {
/*
* In case we already send an async interim
* response , we should not grant
* credits on the final response .
*/
2012-06-27 17:33:43 +04:00
credits_granted = 0 ;
} else if ( credits_requested > 0 ) {
2012-06-27 17:33:43 +04:00
uint16_t additional_max = 0 ;
uint16_t additional_credits = credits_requested - 1 ;
2011-07-29 07:23:30 +04:00
2012-06-27 17:33:43 +04:00
switch ( cmd ) {
case SMB2_OP_NEGPROT :
break ;
case SMB2_OP_SESSSETUP :
/*
* Windows 2012 RC1 starts to grant
* additional credits
* with a successful session setup
*/
if ( NT_STATUS_IS_OK ( out_status ) ) {
additional_max = 32 ;
}
break ;
default :
/*
* We match windows and only grant additional credits
* in chunks of 32.
*/
additional_max = 32 ;
break ;
2011-07-29 07:23:30 +04:00
}
2012-06-27 17:33:43 +04:00
additional_credits = MIN ( additional_credits , additional_max ) ;
2012-06-25 23:45:54 +04:00
credits_granted = credit_charge + additional_credits ;
2012-06-27 17:33:43 +04:00
} else if ( sconn - > smb2 . credits_granted = = 0 ) {
/*
* Make sure the client has always at least one credit
*/
2010-12-11 02:46:41 +03:00
credits_granted = 1 ;
}
2012-06-27 17:33:43 +04:00
/*
2012-06-27 17:33:43 +04:00
* sequence numbers should not wrap
*
* 1. calculate the possible credits until
* the sequence numbers start to wrap on 64 - bit .
*
* 2. UINT64_MAX is used for Break Notifications .
*
* 2. truncate the possible credits to the maximum
* credits we want to grant to the client in total .
*
* 3. remove the range we ' ll already granted to the client
2012-06-27 17:33:43 +04:00
* this makes sure the client consumes the lowest sequence
* number , before we can grant additional credits .
*/
2012-06-27 17:33:43 +04:00
credits_possible = UINT64_MAX - sconn - > smb2 . seqnum_low ;
if ( credits_possible > 0 ) {
/* remove UINT64_MAX */
credits_possible - = 1 ;
}
2012-06-27 17:33:43 +04:00
credits_possible = MIN ( credits_possible , current_max_credits ) ;
2012-06-27 17:33:43 +04:00
credits_possible - = sconn - > smb2 . seqnum_range ;
credits_granted = MIN ( credits_granted , credits_possible ) ;
2010-12-11 02:46:41 +03:00
SSVAL ( outhdr , SMB2_HDR_CREDIT , credits_granted ) ;
sconn - > smb2 . credits_granted + = credits_granted ;
2012-06-27 17:33:43 +04:00
sconn - > smb2 . seqnum_range + = credits_granted ;
2010-12-11 02:46:41 +03:00
2012-06-25 23:45:54 +04:00
DEBUG ( 10 , ( " smb2_set_operation_credit: requested %u, charge %u, "
2012-06-27 17:33:43 +04:00
" granted %u, current possible/max %u/%u, "
2012-06-27 17:33:43 +04:00
" total granted/max/low/range %u/%u/%llu/%u \n " ,
2010-12-11 02:46:41 +03:00
( unsigned int ) credits_requested ,
2012-06-25 23:45:54 +04:00
( unsigned int ) credit_charge ,
2010-12-11 02:46:41 +03:00
( unsigned int ) credits_granted ,
2012-06-27 17:33:43 +04:00
( unsigned int ) credits_possible ,
2012-06-27 17:33:43 +04:00
( unsigned int ) current_max_credits ,
2012-06-27 17:33:43 +04:00
( unsigned int ) sconn - > smb2 . credits_granted ,
( unsigned int ) sconn - > smb2 . max_credits ,
( unsigned long long ) sconn - > smb2 . seqnum_low ,
( unsigned int ) sconn - > smb2 . seqnum_range ) ) ;
2010-12-11 02:46:41 +03:00
}
static void smb2_calculate_credits ( const struct smbd_smb2_request * inreq ,
struct smbd_smb2_request * outreq )
{
int count , idx ;
2011-11-15 23:27:56 +04:00
uint16_t total_credits = 0 ;
2010-12-11 02:46:41 +03:00
count = outreq - > out . vector_count ;
2012-08-07 14:26:38 +04:00
for ( idx = 1 ; idx < count ; idx + = SMBD_SMB2_NUM_IOV_PER_REQ ) {
2012-08-07 14:31:36 +04:00
struct iovec * inhdr_v = SMBD_SMB2_IDX_HDR_IOV ( inreq , in , idx ) ;
struct iovec * outhdr_v = SMBD_SMB2_IDX_HDR_IOV ( outreq , out , idx ) ;
uint8_t * outhdr = ( uint8_t * ) outhdr_v - > iov_base ;
smb2_set_operation_credit ( outreq - > sconn , inhdr_v , outhdr_v ) ;
2011-11-15 23:27:56 +04:00
/* To match Windows, count up what we
just granted . */
total_credits + = SVAL ( outhdr , SMB2_HDR_CREDIT ) ;
/* Set to zero in all but the last reply. */
2012-08-07 14:26:38 +04:00
if ( idx + SMBD_SMB2_NUM_IOV_PER_REQ < count ) {
2011-11-15 23:27:56 +04:00
SSVAL ( outhdr , SMB2_HDR_CREDIT , 0 ) ;
} else {
SSVAL ( outhdr , SMB2_HDR_CREDIT , total_credits ) ;
}
2010-12-11 02:46:41 +03:00
}
}
2010-12-14 00:17:49 +03:00
static NTSTATUS smbd_smb2_request_setup_out ( struct smbd_smb2_request * req )
2009-05-14 16:17:28 +04:00
{
struct iovec * vector ;
int count ;
int idx ;
count = req - > in . vector_count ;
2010-04-20 00:43:42 +04:00
vector = talloc_zero_array ( req , struct iovec , count ) ;
2009-05-14 16:17:28 +04:00
if ( vector = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
vector [ 0 ] . iov_base = req - > out . nbt_hdr ;
vector [ 0 ] . iov_len = 4 ;
SIVAL ( req - > out . nbt_hdr , 0 , 0 ) ;
2012-08-07 14:26:38 +04:00
for ( idx = 1 ; idx < count ; idx + = SMBD_SMB2_NUM_IOV_PER_REQ ) {
2012-08-07 14:41:07 +04:00
struct iovec * inhdr_v = SMBD_SMB2_IDX_HDR_IOV ( req , in , idx ) ;
const uint8_t * inhdr = ( const uint8_t * ) inhdr_v - > iov_base ;
2009-05-14 16:17:28 +04:00
uint8_t * outhdr = NULL ;
uint8_t * outbody = NULL ;
uint32_t next_command_ofs = 0 ;
struct iovec * current = & vector [ idx ] ;
2012-08-07 14:26:38 +04:00
if ( ( idx + SMBD_SMB2_NUM_IOV_PER_REQ ) < count ) {
2010-04-20 01:32:08 +04:00
/* we have a next command -
* setup for the error case . */
next_command_ofs = SMB2_HDR_BODY + 9 ;
2009-05-14 16:17:28 +04:00
}
2010-04-20 00:43:42 +04:00
outhdr = talloc_zero_array ( vector , uint8_t ,
OUTVEC_ALLOC_SIZE ) ;
2009-05-14 16:17:28 +04:00
if ( outhdr = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
outbody = outhdr + SMB2_HDR_BODY ;
2012-08-08 09:07:03 +04:00
/*
* SMBD_SMB2_TF_IOV_OFS might be used later
*/
current [ SMBD_SMB2_TF_IOV_OFS ] . iov_base = NULL ;
current [ SMBD_SMB2_TF_IOV_OFS ] . iov_len = 0 ;
2012-08-07 14:41:07 +04:00
current [ SMBD_SMB2_HDR_IOV_OFS ] . iov_base = ( void * ) outhdr ;
current [ SMBD_SMB2_HDR_IOV_OFS ] . iov_len = SMB2_HDR_BODY ;
2009-05-14 16:17:28 +04:00
2012-08-07 14:41:07 +04:00
current [ SMBD_SMB2_BODY_IOV_OFS ] . iov_base = ( void * ) outbody ;
current [ SMBD_SMB2_BODY_IOV_OFS ] . iov_len = 8 ;
2009-05-14 16:17:28 +04:00
2012-08-07 14:41:07 +04:00
current [ SMBD_SMB2_DYN_IOV_OFS ] . iov_base = NULL ;
current [ SMBD_SMB2_DYN_IOV_OFS ] . iov_len = 0 ;
2009-05-14 16:17:28 +04:00
/* setup the SMB2 header */
SIVAL ( outhdr , SMB2_HDR_PROTOCOL_ID , SMB2_MAGIC ) ;
SSVAL ( outhdr , SMB2_HDR_LENGTH , SMB2_HDR_BODY ) ;
2011-10-27 18:42:44 +04:00
SSVAL ( outhdr , SMB2_HDR_CREDIT_CHARGE ,
SVAL ( inhdr , SMB2_HDR_CREDIT_CHARGE ) ) ;
2009-05-14 16:17:28 +04:00
SIVAL ( outhdr , SMB2_HDR_STATUS ,
NT_STATUS_V ( NT_STATUS_INTERNAL_ERROR ) ) ;
SSVAL ( outhdr , SMB2_HDR_OPCODE ,
SVAL ( inhdr , SMB2_HDR_OPCODE ) ) ;
2009-06-09 21:46:29 +04:00
SIVAL ( outhdr , SMB2_HDR_FLAGS ,
IVAL ( inhdr , SMB2_HDR_FLAGS ) | SMB2_HDR_FLAG_REDIRECT ) ;
2009-05-14 16:17:28 +04:00
SIVAL ( outhdr , SMB2_HDR_NEXT_COMMAND , next_command_ofs ) ;
2009-05-20 21:32:55 +04:00
SBVAL ( outhdr , SMB2_HDR_MESSAGE_ID ,
BVAL ( inhdr , SMB2_HDR_MESSAGE_ID ) ) ;
2009-05-14 16:17:28 +04:00
SIVAL ( outhdr , SMB2_HDR_PID ,
IVAL ( inhdr , SMB2_HDR_PID ) ) ;
SIVAL ( outhdr , SMB2_HDR_TID ,
IVAL ( inhdr , SMB2_HDR_TID ) ) ;
2009-05-20 21:32:55 +04:00
SBVAL ( outhdr , SMB2_HDR_SESSION_ID ,
BVAL ( inhdr , SMB2_HDR_SESSION_ID ) ) ;
2011-10-27 18:42:44 +04:00
memcpy ( outhdr + SMB2_HDR_SIGNATURE ,
inhdr + SMB2_HDR_SIGNATURE , 16 ) ;
2009-05-14 16:17:28 +04:00
/* setup error body header */
2009-06-09 19:06:40 +04:00
SSVAL ( outbody , 0x00 , 0x08 + 1 ) ;
2009-05-14 16:17:28 +04:00
SSVAL ( outbody , 0x02 , 0 ) ;
SIVAL ( outbody , 0x04 , 0 ) ;
}
req - > out . vector = vector ;
req - > out . vector_count = count ;
/* setup the length of the NBT packet */
smb2_setup_nbt_length ( req - > out . vector , req - > out . vector_count ) ;
2009-08-07 17:21:07 +04:00
DLIST_ADD_END ( req - > sconn - > smb2 . requests , req , struct smbd_smb2_request * ) ;
2009-06-09 23:29:40 +04:00
2009-05-14 16:17:28 +04:00
return NT_STATUS_OK ;
}
2009-06-02 18:07:08 +04:00
void smbd_server_connection_terminate_ex ( struct smbd_server_connection * sconn ,
const char * reason ,
const char * location )
2009-05-14 16:17:28 +04:00
{
2009-06-02 18:07:08 +04:00
DEBUG ( 10 , ( " smbd_server_connection_terminate_ex: reason[%s] at %s \n " ,
reason , location ) ) ;
2009-05-14 16:17:28 +04:00
exit_server_cleanly ( reason ) ;
}
2012-08-08 09:07:03 +04:00
static bool dup_smb2_vec4 ( TALLOC_CTX * ctx ,
2010-06-09 04:44:05 +04:00
struct iovec * outvec ,
const struct iovec * srcvec )
2010-04-18 08:20:17 +04:00
{
2012-08-08 09:07:03 +04:00
const uint8_t * srctf ;
size_t srctf_len ;
2012-08-08 10:32:40 +04:00
const uint8_t * srchdr ;
size_t srchdr_len ;
const uint8_t * srcbody ;
size_t srcbody_len ;
const uint8_t * expected_srcbody ;
const uint8_t * srcdyn ;
size_t srcdyn_len ;
const uint8_t * expected_srcdyn ;
2012-08-08 09:07:03 +04:00
uint8_t * dsttf ;
2012-08-08 10:32:40 +04:00
uint8_t * dsthdr ;
uint8_t * dstbody ;
uint8_t * dstdyn ;
2012-08-08 09:07:03 +04:00
srctf = ( const uint8_t * ) srcvec [ SMBD_SMB2_TF_IOV_OFS ] . iov_base ;
srctf_len = srcvec [ SMBD_SMB2_TF_IOV_OFS ] . iov_len ;
2012-08-08 10:32:40 +04:00
srchdr = ( const uint8_t * ) srcvec [ SMBD_SMB2_HDR_IOV_OFS ] . iov_base ;
srchdr_len = srcvec [ SMBD_SMB2_HDR_IOV_OFS ] . iov_len ;
srcbody = ( const uint8_t * ) srcvec [ SMBD_SMB2_BODY_IOV_OFS ] . iov_base ;
srcbody_len = srcvec [ SMBD_SMB2_BODY_IOV_OFS ] . iov_len ;
expected_srcbody = srchdr + SMB2_HDR_BODY ;
srcdyn = ( const uint8_t * ) srcvec [ SMBD_SMB2_DYN_IOV_OFS ] . iov_base ;
srcdyn_len = srcvec [ SMBD_SMB2_DYN_IOV_OFS ] . iov_len ;
expected_srcdyn = srcbody + 8 ;
2012-08-08 09:07:03 +04:00
if ( ( srctf_len ! = SMB2_TF_HDR_SIZE ) & & ( srctf_len ! = 0 ) ) {
return false ;
}
2012-08-08 10:32:40 +04:00
if ( srchdr_len ! = SMB2_HDR_BODY ) {
return false ;
}
2012-08-08 09:07:03 +04:00
if ( srctf_len = = SMB2_TF_HDR_SIZE ) {
dsttf = talloc_memdup ( ctx , srctf , SMB2_TF_HDR_SIZE ) ;
if ( dsttf = = NULL ) {
return false ;
}
} else {
dsttf = NULL ;
}
outvec [ SMBD_SMB2_TF_IOV_OFS ] . iov_base = ( void * ) dsttf ;
outvec [ SMBD_SMB2_TF_IOV_OFS ] . iov_len = srctf_len ;
2012-08-08 10:32:40 +04:00
/* vec[SMBD_SMB2_HDR_IOV_OFS] is always boilerplate and must
2010-06-09 04:44:05 +04:00
* be allocated with size OUTVEC_ALLOC_SIZE . */
2009-06-10 00:33:32 +04:00
2012-08-08 10:32:40 +04:00
dsthdr = talloc_memdup ( ctx , srchdr , OUTVEC_ALLOC_SIZE ) ;
if ( dsthdr = = NULL ) {
2010-06-09 04:44:05 +04:00
return false ;
}
2012-08-08 10:32:40 +04:00
outvec [ SMBD_SMB2_HDR_IOV_OFS ] . iov_base = ( void * ) dsthdr ;
outvec [ SMBD_SMB2_HDR_IOV_OFS ] . iov_len = SMB2_HDR_BODY ;
2010-06-09 04:44:05 +04:00
/*
2012-08-08 10:32:40 +04:00
* If this is a " standard " vec [ SMBD_SMB2_BOFY_IOV_OFS ] of length 8 ,
* pointing to srcvec [ SMBD_SMB2_HDR_IOV_OFS ] . iov_base + SMB2_HDR_BODY ,
2010-06-09 04:44:05 +04:00
* then duplicate this . Else use talloc_memdup ( ) .
*/
2012-08-08 10:32:40 +04:00
if ( ( srcbody = = expected_srcbody ) & & ( srcbody_len = = 8 ) ) {
dstbody = dsthdr + SMB2_HDR_BODY ;
2010-06-09 04:44:05 +04:00
} else {
2012-08-08 10:32:40 +04:00
dstbody = talloc_memdup ( ctx , srcbody , srcbody_len ) ;
if ( dstbody = = NULL ) {
2010-04-18 08:20:17 +04:00
return false ;
}
2010-06-09 04:44:05 +04:00
}
2012-08-08 10:32:40 +04:00
outvec [ SMBD_SMB2_BODY_IOV_OFS ] . iov_base = ( void * ) dstbody ;
outvec [ SMBD_SMB2_BODY_IOV_OFS ] . iov_len = srcbody_len ;
2010-06-09 04:44:05 +04:00
/*
2012-08-08 10:32:40 +04:00
* If this is a " standard " vec [ SMBD_SMB2_DYN_IOV_OFS ] of length 1 ,
* pointing to
* srcvec [ SMBD_SMB2_HDR_IOV_OFS ] . iov_base + 8
2010-06-09 04:44:05 +04:00
* then duplicate this . Else use talloc_memdup ( ) .
*/
2012-08-08 10:32:40 +04:00
if ( ( srcdyn = = expected_srcdyn ) & & ( srcdyn_len = = 1 ) ) {
dstdyn = dsthdr + SMB2_HDR_BODY + 8 ;
} else if ( srcdyn = = NULL ) {
dstdyn = NULL ;
2010-04-18 08:20:17 +04:00
} else {
2012-08-08 10:32:40 +04:00
dstdyn = talloc_memdup ( ctx , srcdyn , srcdyn_len ) ;
if ( dstdyn = = NULL ) {
return false ;
}
2010-04-18 08:20:17 +04:00
}
2012-08-08 10:32:40 +04:00
outvec [ SMBD_SMB2_DYN_IOV_OFS ] . iov_base = ( void * ) dstdyn ;
outvec [ SMBD_SMB2_DYN_IOV_OFS ] . iov_len = srcdyn_len ;
2010-04-18 08:20:17 +04:00
return true ;
}
2010-04-18 09:42:23 +04:00
static struct smbd_smb2_request * dup_smb2_req ( const struct smbd_smb2_request * req )
2010-04-18 08:20:17 +04:00
{
struct smbd_smb2_request * newreq = NULL ;
struct iovec * outvec = NULL ;
int count = req - > out . vector_count ;
int i ;
newreq = smbd_smb2_request_allocate ( req - > sconn ) ;
if ( ! newreq ) {
return NULL ;
}
newreq - > sconn = req - > sconn ;
2011-06-29 04:45:49 +04:00
newreq - > session = req - > session ;
2012-08-08 08:35:03 +04:00
newreq - > do_encryption = req - > do_encryption ;
2010-04-18 08:20:17 +04:00
newreq - > do_signing = req - > do_signing ;
newreq - > current_idx = req - > current_idx ;
2010-04-20 00:43:42 +04:00
outvec = talloc_zero_array ( newreq , struct iovec , count ) ;
2010-04-18 08:20:17 +04:00
if ( ! outvec ) {
TALLOC_FREE ( newreq ) ;
return NULL ;
}
newreq - > out . vector = outvec ;
newreq - > out . vector_count = count ;
/* Setup the outvec's identically to req. */
outvec [ 0 ] . iov_base = newreq - > out . nbt_hdr ;
outvec [ 0 ] . iov_len = 4 ;
memcpy ( newreq - > out . nbt_hdr , req - > out . nbt_hdr , 4 ) ;
2010-04-20 00:43:42 +04:00
2010-06-09 04:44:05 +04:00
/* Setup the vectors identically to the ones in req. */
2012-08-07 14:26:38 +04:00
for ( i = 1 ; i < count ; i + = SMBD_SMB2_NUM_IOV_PER_REQ ) {
2012-08-08 09:07:03 +04:00
if ( ! dup_smb2_vec4 ( outvec , & outvec [ i ] , & req - > out . vector [ i ] ) ) {
2010-04-20 00:43:42 +04:00
break ;
2010-04-18 08:20:17 +04:00
}
}
2010-04-20 00:43:42 +04:00
if ( i < count ) {
/* Alloc failed. */
TALLOC_FREE ( newreq ) ;
return NULL ;
}
2010-04-18 08:20:17 +04:00
smb2_setup_nbt_length ( newreq - > out . vector ,
newreq - > out . vector_count ) ;
return newreq ;
}
static void smbd_smb2_request_writev_done ( struct tevent_req * subreq ) ;
2009-06-10 00:33:32 +04:00
2010-04-18 09:42:23 +04:00
static NTSTATUS smb2_send_async_interim_response ( const struct smbd_smb2_request * req )
{
2012-08-07 16:24:28 +04:00
struct smbXsrv_connection * conn = req - > sconn - > conn ;
2012-08-08 09:07:03 +04:00
int first_idx = 1 ;
struct iovec * firsttf = NULL ;
2012-08-07 16:24:28 +04:00
struct iovec * outhdr_v = NULL ;
2010-04-18 09:42:23 +04:00
uint8_t * outhdr = NULL ;
struct smbd_smb2_request * nreq = NULL ;
2012-08-07 16:24:28 +04:00
NTSTATUS status ;
2010-04-18 09:42:23 +04:00
/* Create a new smb2 request we'll use
for the interim return . */
nreq = dup_smb2_req ( req ) ;
if ( ! nreq ) {
return NT_STATUS_NO_MEMORY ;
}
2012-08-07 14:26:38 +04:00
/* Lose the last X out vectors. They're the
2010-04-18 09:42:23 +04:00
ones we ' ll be using for the async reply . */
2012-08-07 14:26:38 +04:00
nreq - > out . vector_count - = SMBD_SMB2_NUM_IOV_PER_REQ ;
2010-04-18 09:42:23 +04:00
smb2_setup_nbt_length ( nreq - > out . vector ,
nreq - > out . vector_count ) ;
/* Step back to the previous reply. */
2012-08-07 16:24:28 +04:00
nreq - > current_idx - = SMBD_SMB2_NUM_IOV_PER_REQ ;
2012-08-08 09:07:03 +04:00
firsttf = SMBD_SMB2_IDX_TF_IOV ( nreq , out , first_idx ) ;
2012-08-07 16:24:28 +04:00
outhdr_v = SMBD_SMB2_OUT_HDR_IOV ( nreq ) ;
outhdr = SMBD_SMB2_OUT_HDR_PTR ( nreq ) ;
2010-04-18 09:42:23 +04:00
/* And end the chain. */
SIVAL ( outhdr , SMB2_HDR_NEXT_COMMAND , 0 ) ;
2010-12-11 02:46:41 +03:00
/* Calculate outgoing credits */
smb2_calculate_credits ( req , nreq ) ;
2012-08-08 09:07:03 +04:00
if ( DEBUGLEVEL > = 10 ) {
dbgtext ( " smb2_send_async_interim_response: nreq->current_idx = %u \n " ,
( unsigned int ) nreq - > current_idx ) ;
dbgtext ( " smb2_send_async_interim_response: returning %u vectors \n " ,
( unsigned int ) nreq - > out . vector_count ) ;
print_req_vectors ( nreq ) ;
}
2012-08-07 16:24:28 +04:00
/*
* As we have changed the header ( SMB2_HDR_NEXT_COMMAND ) ,
2012-08-08 09:07:03 +04:00
* we need to sign / encrypt here with the last / first key we remembered
2012-08-07 16:24:28 +04:00
*/
2012-08-08 09:07:03 +04:00
if ( firsttf - > iov_len = = SMB2_TF_HDR_SIZE ) {
status = smb2_signing_encrypt_pdu ( req - > first_key ,
conn - > protocol ,
firsttf ,
nreq - > out . vector_count - first_idx ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
} else if ( req - > last_key . length > 0 ) {
2012-08-07 16:24:28 +04:00
status = smb2_signing_sign_pdu ( req - > last_key ,
2012-03-16 18:01:27 +04:00
conn - > protocol ,
2012-08-07 16:24:28 +04:00
outhdr_v ,
2012-08-08 09:07:03 +04:00
SMBD_SMB2_NUM_IOV_PER_REQ - 1 ) ;
2010-04-18 09:42:23 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
2012-08-07 16:24:28 +04:00
2010-04-18 09:42:23 +04:00
nreq - > subreq = tstream_writev_queue_send ( nreq ,
2011-12-12 17:15:03 +04:00
nreq - > sconn - > ev_ctx ,
2010-04-18 09:42:23 +04:00
nreq - > sconn - > smb2 . stream ,
nreq - > sconn - > smb2 . send_queue ,
nreq - > out . vector ,
nreq - > out . vector_count ) ;
if ( nreq - > subreq = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
tevent_req_set_callback ( nreq - > subreq ,
smbd_smb2_request_writev_done ,
nreq ) ;
return NT_STATUS_OK ;
}
struct smbd_smb2_request_pending_state {
struct smbd_server_connection * sconn ;
2012-08-08 09:07:03 +04:00
uint8_t buf [ NBT_HDR_SIZE + SMB2_TF_HDR_SIZE + SMB2_HDR_BODY + 0x08 + 1 ] ;
2012-08-08 08:40:51 +04:00
struct iovec vector [ 1 + SMBD_SMB2_NUM_IOV_PER_REQ ] ;
2010-04-18 09:42:23 +04:00
} ;
static void smbd_smb2_request_pending_writev_done ( struct tevent_req * subreq )
{
struct smbd_smb2_request_pending_state * state =
tevent_req_callback_data ( subreq ,
struct smbd_smb2_request_pending_state ) ;
struct smbd_server_connection * sconn = state - > sconn ;
int ret ;
int sys_errno ;
ret = tstream_writev_queue_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
NTSTATUS status = map_nt_error_from_unix ( sys_errno ) ;
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
return ;
}
TALLOC_FREE ( state ) ;
}
2011-11-09 14:47:33 +04:00
static void smbd_smb2_request_pending_timer ( struct tevent_context * ev ,
struct tevent_timer * te ,
struct timeval current_time ,
void * private_data ) ;
2009-08-15 12:01:38 +04:00
NTSTATUS smbd_smb2_request_pending_queue ( struct smbd_smb2_request * req ,
2011-11-14 18:42:55 +04:00
struct tevent_req * subreq ,
uint32_t defer_time )
2009-06-10 00:33:32 +04:00
{
2010-04-18 09:42:23 +04:00
NTSTATUS status ;
2012-08-08 11:15:17 +04:00
int idx = req - > current_idx ;
2011-11-09 14:47:33 +04:00
struct timeval defer_endtime ;
uint8_t * outhdr = NULL ;
uint32_t flags ;
2009-06-10 00:33:32 +04:00
2009-08-15 12:01:38 +04:00
if ( ! tevent_req_is_in_progress ( subreq ) ) {
return NT_STATUS_OK ;
}
2010-06-11 00:19:25 +04:00
req - > subreq = subreq ;
subreq = NULL ;
2011-11-09 14:47:33 +04:00
if ( req - > async_te ) {
/* We're already async. */
return NT_STATUS_OK ;
}
2012-08-08 11:13:17 +04:00
outhdr = SMBD_SMB2_OUT_HDR_PTR ( req ) ;
2011-11-09 14:47:33 +04:00
flags = IVAL ( outhdr , SMB2_HDR_FLAGS ) ;
if ( flags & SMB2_HDR_FLAG_ASYNC ) {
2010-04-18 08:20:17 +04:00
/* We're already async. */
return NT_STATUS_OK ;
}
2012-08-08 11:15:17 +04:00
if ( req - > in . vector_count > idx + SMBD_SMB2_NUM_IOV_PER_REQ ) {
2010-04-18 08:20:17 +04:00
/*
* We ' re trying to go async in a compound
* request chain . This is not allowed .
* Cancel the outstanding request .
*/
2012-09-20 18:04:01 +04:00
bool ok = tevent_req_cancel ( req - > subreq ) ;
if ( ok ) {
return NT_STATUS_OK ;
}
TALLOC_FREE ( req - > subreq ) ;
2010-04-18 08:20:17 +04:00
return smbd_smb2_request_error ( req ,
2012-09-20 18:04:01 +04:00
NT_STATUS_INTERNAL_ERROR ) ;
2010-04-18 08:20:17 +04:00
}
if ( DEBUGLEVEL > = 10 ) {
dbgtext ( " smbd_smb2_request_pending_queue: req->current_idx = %u \n " ,
( unsigned int ) req - > current_idx ) ;
print_req_vectors ( req ) ;
}
2012-08-07 14:47:44 +04:00
if ( req - > out . vector_count > = ( 2 * SMBD_SMB2_NUM_IOV_PER_REQ ) ) {
/*
* This is a compound reply . We
2010-04-18 09:42:23 +04:00
* must do an interim response
* followed by the async response
* to match W2K8R2 .
*/
status = smb2_send_async_interim_response ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2012-08-08 09:07:03 +04:00
data_blob_clear_free ( & req - > first_key ) ;
2011-08-27 01:23:26 +04:00
/*
* We ' re splitting off the last SMB2
* request in a compound set , and the
* smb2_send_async_interim_response ( )
* call above just sent all the replies
* for the previous SMB2 requests in
* this compound set . So we ' re no longer
* in the " compound_related_in_progress "
* state , and this is no longer a compound
* request .
*/
req - > compound_related = false ;
req - > sconn - > smb2 . compound_related_in_progress = false ;
2011-11-09 14:47:33 +04:00
2012-08-07 15:00:50 +04:00
req - > current_idx = 1 ;
2011-11-09 14:47:33 +04:00
/* Re-arrange the in.vectors. */
2012-08-07 15:00:50 +04:00
memmove ( & req - > in . vector [ req - > current_idx ] ,
2012-08-08 11:15:17 +04:00
& req - > in . vector [ idx ] ,
2012-08-07 15:00:50 +04:00
sizeof ( req - > in . vector [ 0 ] ) * SMBD_SMB2_NUM_IOV_PER_REQ ) ;
req - > in . vector_count = req - > current_idx + SMBD_SMB2_NUM_IOV_PER_REQ ;
2011-11-09 14:47:33 +04:00
2012-08-07 15:02:14 +04:00
/* Re-arrange the out.vectors. */
memmove ( & req - > out . vector [ req - > current_idx ] ,
2012-08-08 11:15:17 +04:00
& req - > out . vector [ idx ] ,
2012-08-07 15:02:14 +04:00
sizeof ( req - > out . vector [ 0 ] ) * SMBD_SMB2_NUM_IOV_PER_REQ ) ;
req - > out . vector_count = req - > current_idx + SMBD_SMB2_NUM_IOV_PER_REQ ;
2011-11-09 14:47:33 +04:00
2012-08-07 14:57:14 +04:00
outhdr = SMBD_SMB2_OUT_HDR_PTR ( req ) ;
2011-11-09 14:47:33 +04:00
flags = ( IVAL ( outhdr , SMB2_HDR_FLAGS ) & ~ SMB2_HDR_FLAG_CHAINED ) ;
SIVAL ( outhdr , SMB2_HDR_FLAGS , flags ) ;
2010-04-18 08:20:17 +04:00
}
2012-08-07 16:24:28 +04:00
data_blob_clear_free ( & req - > last_key ) ;
2010-04-18 08:20:17 +04:00
2011-11-09 14:47:33 +04:00
defer_endtime = timeval_current_ofs_usec ( defer_time ) ;
2011-12-12 17:15:03 +04:00
req - > async_te = tevent_add_timer ( req - > sconn - > ev_ctx ,
2011-11-09 14:47:33 +04:00
req , defer_endtime ,
smbd_smb2_request_pending_timer ,
req ) ;
if ( req - > async_te = = NULL ) {
return NT_STATUS_NO_MEMORY ;
2010-04-28 03:07:12 +04:00
}
2011-11-09 14:47:33 +04:00
return NT_STATUS_OK ;
}
static void smbd_smb2_request_pending_timer ( struct tevent_context * ev ,
struct tevent_timer * te ,
struct timeval current_time ,
void * private_data )
{
struct smbd_smb2_request * req =
talloc_get_type_abort ( private_data ,
struct smbd_smb2_request ) ;
struct smbd_smb2_request_pending_state * state = NULL ;
uint8_t * outhdr = NULL ;
const uint8_t * inhdr = NULL ;
2012-08-08 09:07:03 +04:00
uint8_t * tf = NULL ;
size_t tf_len = 0 ;
2011-11-09 14:47:33 +04:00
uint8_t * hdr = NULL ;
uint8_t * body = NULL ;
2012-08-08 08:40:51 +04:00
uint8_t * dyn = NULL ;
2011-11-09 14:47:33 +04:00
uint32_t flags = 0 ;
2012-08-08 09:07:03 +04:00
uint64_t session_id = 0 ;
2011-11-09 14:47:33 +04:00
uint64_t message_id = 0 ;
2012-08-22 12:30:52 +04:00
uint64_t nonce_high = 0 ;
uint64_t nonce_low = 0 ;
2011-11-09 14:47:33 +04:00
uint64_t async_id = 0 ;
struct tevent_req * subreq = NULL ;
TALLOC_FREE ( req - > async_te ) ;
/* Ensure our final reply matches the interim one. */
2012-08-05 17:00:23 +04:00
inhdr = SMBD_SMB2_IN_HDR_PTR ( req ) ;
outhdr = SMBD_SMB2_OUT_HDR_PTR ( req ) ;
2011-11-09 14:47:33 +04:00
flags = IVAL ( outhdr , SMB2_HDR_FLAGS ) ;
message_id = BVAL ( outhdr , SMB2_HDR_MESSAGE_ID ) ;
2012-08-08 09:07:03 +04:00
session_id = BVAL ( outhdr , SMB2_HDR_SESSION_ID ) ;
2011-11-09 14:47:33 +04:00
2009-06-10 00:33:32 +04:00
async_id = message_id ; /* keep it simple for now... */
2010-04-18 09:42:23 +04:00
2011-11-09 14:47:33 +04:00
SIVAL ( outhdr , SMB2_HDR_FLAGS , flags | SMB2_HDR_FLAG_ASYNC ) ;
SBVAL ( outhdr , SMB2_HDR_ASYNC_ID , async_id ) ;
DEBUG ( 10 , ( " smbd_smb2_request_pending_queue: opcode[%s] mid %llu "
" going async \n " ,
smb2_opcode_name ( ( uint16_t ) IVAL ( inhdr , SMB2_HDR_OPCODE ) ) ,
( unsigned long long ) async_id ) ) ;
2010-04-19 07:23:54 +04:00
/*
* What we send is identical to a smbd_smb2_request_error
* packet with an error status of STATUS_PENDING . Make use
* of this fact sometime when refactoring . JRA .
*/
2010-04-18 09:42:23 +04:00
state = talloc_zero ( req - > sconn , struct smbd_smb2_request_pending_state ) ;
if ( state = = NULL ) {
2011-11-09 14:47:33 +04:00
smbd_server_connection_terminate ( req - > sconn ,
nt_errstr ( NT_STATUS_NO_MEMORY ) ) ;
return ;
2009-06-10 00:33:32 +04:00
}
2010-04-18 09:42:23 +04:00
state - > sconn = req - > sconn ;
2009-06-10 00:33:32 +04:00
2012-08-08 09:07:03 +04:00
tf = state - > buf + NBT_HDR_SIZE ;
tf_len = SMB2_TF_HDR_SIZE ;
2009-06-10 00:33:32 +04:00
2012-08-08 09:07:03 +04:00
hdr = tf + SMB2_TF_HDR_SIZE ;
2012-08-08 08:40:51 +04:00
body = hdr + SMB2_HDR_BODY ;
dyn = body + 8 ;
2009-06-10 00:33:32 +04:00
2012-08-22 12:30:52 +04:00
if ( req - > do_encryption ) {
struct smbXsrv_session * x = req - > session ;
nonce_high = x - > nonce_high ;
nonce_low = x - > nonce_low ;
x - > nonce_low + = 1 ;
if ( x - > nonce_low = = 0 ) {
x - > nonce_low + = 1 ;
x - > nonce_high + = 1 ;
}
}
2009-06-10 00:33:32 +04:00
2012-08-08 09:07:03 +04:00
SIVAL ( tf , SMB2_TF_PROTOCOL_ID , SMB2_TF_MAGIC ) ;
SBVAL ( tf , SMB2_TF_NONCE + 0 , nonce_low ) ;
SBVAL ( tf , SMB2_TF_NONCE + 8 , nonce_high ) ;
SBVAL ( tf , SMB2_TF_SESSION_ID , session_id ) ;
2010-04-18 09:42:23 +04:00
SIVAL ( hdr , SMB2_HDR_PROTOCOL_ID , SMB2_MAGIC ) ;
SSVAL ( hdr , SMB2_HDR_LENGTH , SMB2_HDR_BODY ) ;
SSVAL ( hdr , SMB2_HDR_EPOCH , 0 ) ;
SIVAL ( hdr , SMB2_HDR_STATUS , NT_STATUS_V ( STATUS_PENDING ) ) ;
2011-11-09 14:47:33 +04:00
SSVAL ( hdr , SMB2_HDR_OPCODE , SVAL ( outhdr , SMB2_HDR_OPCODE ) ) ;
2010-05-19 04:11:54 +04:00
2011-11-08 20:50:43 +04:00
SIVAL ( hdr , SMB2_HDR_FLAGS , flags ) ;
2010-04-18 09:42:23 +04:00
SIVAL ( hdr , SMB2_HDR_NEXT_COMMAND , 0 ) ;
SBVAL ( hdr , SMB2_HDR_MESSAGE_ID , message_id ) ;
SBVAL ( hdr , SMB2_HDR_PID , async_id ) ;
SBVAL ( hdr , SMB2_HDR_SESSION_ID ,
2011-11-09 14:47:33 +04:00
BVAL ( outhdr , SMB2_HDR_SESSION_ID ) ) ;
memcpy ( hdr + SMB2_HDR_SIGNATURE ,
outhdr + SMB2_HDR_SIGNATURE , 16 ) ;
2010-04-18 09:42:23 +04:00
SSVAL ( body , 0x00 , 0x08 + 1 ) ;
SCVAL ( body , 0x02 , 0 ) ;
SCVAL ( body , 0x03 , 0 ) ;
SIVAL ( body , 0x04 , 0 ) ;
/* Match W2K8R2... */
2012-08-08 08:40:51 +04:00
SCVAL ( dyn , 0x00 , 0x21 ) ;
state - > vector [ 0 ] . iov_base = ( void * ) state - > buf ;
state - > vector [ 0 ] . iov_len = NBT_HDR_SIZE ;
2012-08-08 09:07:03 +04:00
if ( req - > do_encryption ) {
state - > vector [ 1 + SMBD_SMB2_TF_IOV_OFS ] . iov_base = tf ;
state - > vector [ 1 + SMBD_SMB2_TF_IOV_OFS ] . iov_len = tf_len ;
} else {
state - > vector [ 1 + SMBD_SMB2_TF_IOV_OFS ] . iov_base = NULL ;
state - > vector [ 1 + SMBD_SMB2_TF_IOV_OFS ] . iov_len = 0 ;
}
2012-08-08 08:40:51 +04:00
state - > vector [ 1 + SMBD_SMB2_HDR_IOV_OFS ] . iov_base = hdr ;
state - > vector [ 1 + SMBD_SMB2_HDR_IOV_OFS ] . iov_len = SMB2_HDR_BODY ;
state - > vector [ 1 + SMBD_SMB2_BODY_IOV_OFS ] . iov_base = body ;
state - > vector [ 1 + SMBD_SMB2_BODY_IOV_OFS ] . iov_len = 8 ;
state - > vector [ 1 + SMBD_SMB2_DYN_IOV_OFS ] . iov_base = dyn ;
state - > vector [ 1 + SMBD_SMB2_DYN_IOV_OFS ] . iov_len = 1 ;
smb2_setup_nbt_length ( state - > vector , 1 + SMBD_SMB2_NUM_IOV_PER_REQ ) ;
2010-04-18 09:42:23 +04:00
2010-12-14 02:22:47 +03:00
/* Ensure we correctly go through crediting. Grant
the credits now , and zero credits on the final
response . */
2010-12-11 02:46:41 +03:00
smb2_set_operation_credit ( req - > sconn ,
2012-08-05 17:00:23 +04:00
SMBD_SMB2_IN_HDR_IOV ( req ) ,
2012-08-08 08:40:51 +04:00
& state - > vector [ 1 + SMBD_SMB2_HDR_IOV_OFS ] ) ;
2010-12-11 02:46:41 +03:00
2011-11-08 20:50:43 +04:00
SIVAL ( hdr , SMB2_HDR_FLAGS , flags | SMB2_HDR_FLAG_ASYNC ) ;
2012-08-08 09:07:03 +04:00
if ( DEBUGLVL ( 10 ) ) {
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( state - > vector ) ; i + + ) {
dbgtext ( " \t state->vector[%u/%u].iov_len = %u \n " ,
( unsigned int ) i ,
( unsigned int ) ARRAY_SIZE ( state - > vector ) ,
( unsigned int ) state - > vector [ i ] . iov_len ) ;
}
}
if ( req - > do_encryption ) {
NTSTATUS status ;
struct smbXsrv_session * x = req - > session ;
struct smbXsrv_connection * conn = x - > connection ;
DATA_BLOB encryption_key = x - > global - > encryption_key ;
status = smb2_signing_encrypt_pdu ( encryption_key ,
conn - > protocol ,
& state - > vector [ 1 + SMBD_SMB2_TF_IOV_OFS ] ,
SMBD_SMB2_NUM_IOV_PER_REQ ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
smbd_server_connection_terminate ( req - > sconn ,
nt_errstr ( status ) ) ;
return ;
}
} else if ( req - > do_signing ) {
2011-11-09 14:47:33 +04:00
NTSTATUS status ;
2012-03-27 13:09:05 +04:00
struct smbXsrv_session * x = req - > session ;
2012-03-16 18:01:27 +04:00
struct smbXsrv_connection * conn = x - > connection ;
DATA_BLOB signing_key = x - > global - > channels [ 0 ] . signing_key ;
2011-11-09 14:47:33 +04:00
2012-03-16 18:01:27 +04:00
status = smb2_signing_sign_pdu ( signing_key ,
2012-08-08 08:40:51 +04:00
conn - > protocol ,
& state - > vector [ 1 + SMBD_SMB2_HDR_IOV_OFS ] ,
2012-08-08 09:07:03 +04:00
SMBD_SMB2_NUM_IOV_PER_REQ - 1 ) ;
2010-04-18 08:20:17 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2011-11-09 14:47:33 +04:00
smbd_server_connection_terminate ( req - > sconn ,
nt_errstr ( status ) ) ;
return ;
2010-04-18 08:20:17 +04:00
}
}
2009-06-10 00:33:32 +04:00
2010-04-18 09:42:23 +04:00
subreq = tstream_writev_queue_send ( state ,
2011-12-12 17:15:03 +04:00
state - > sconn - > ev_ctx ,
2011-11-09 14:47:33 +04:00
state - > sconn - > smb2 . stream ,
state - > sconn - > smb2 . send_queue ,
2010-04-18 09:42:23 +04:00
state - > vector ,
2012-08-08 08:40:51 +04:00
ARRAY_SIZE ( state - > vector ) ) ;
2010-04-18 09:42:23 +04:00
if ( subreq = = NULL ) {
2011-11-09 14:47:33 +04:00
smbd_server_connection_terminate ( state - > sconn ,
nt_errstr ( NT_STATUS_NO_MEMORY ) ) ;
return ;
2009-06-10 00:33:32 +04:00
}
2010-04-18 09:42:23 +04:00
tevent_req_set_callback ( subreq ,
smbd_smb2_request_pending_writev_done ,
state ) ;
2009-06-10 00:33:32 +04:00
}
static NTSTATUS smbd_smb2_request_process_cancel ( struct smbd_smb2_request * req )
{
2009-08-07 17:21:07 +04:00
struct smbd_server_connection * sconn = req - > sconn ;
2009-06-10 00:33:32 +04:00
struct smbd_smb2_request * cur ;
const uint8_t * inhdr ;
uint32_t flags ;
uint64_t search_message_id ;
uint64_t search_async_id ;
2010-04-18 08:20:17 +04:00
uint64_t found_id ;
2009-06-10 00:33:32 +04:00
2012-08-05 17:00:23 +04:00
inhdr = SMBD_SMB2_IN_HDR_PTR ( req ) ;
2009-06-10 00:33:32 +04:00
flags = IVAL ( inhdr , SMB2_HDR_FLAGS ) ;
search_message_id = BVAL ( inhdr , SMB2_HDR_MESSAGE_ID ) ;
search_async_id = BVAL ( inhdr , SMB2_HDR_PID ) ;
/*
* we don ' t need the request anymore
* cancel requests never have a response
*/
2010-06-03 03:57:08 +04:00
DLIST_REMOVE ( req - > sconn - > smb2 . requests , req ) ;
2009-06-10 00:33:32 +04:00
TALLOC_FREE ( req ) ;
for ( cur = sconn - > smb2 . requests ; cur ; cur = cur - > next ) {
const uint8_t * outhdr ;
uint64_t message_id ;
uint64_t async_id ;
2012-08-05 17:00:23 +04:00
outhdr = SMBD_SMB2_OUT_HDR_PTR ( cur ) ;
2009-06-10 00:33:32 +04:00
message_id = BVAL ( outhdr , SMB2_HDR_MESSAGE_ID ) ;
async_id = BVAL ( outhdr , SMB2_HDR_PID ) ;
if ( flags & SMB2_HDR_FLAG_ASYNC ) {
if ( search_async_id = = async_id ) {
2010-04-18 08:20:17 +04:00
found_id = async_id ;
2009-06-10 00:33:32 +04:00
break ;
}
} else {
if ( search_message_id = = message_id ) {
2010-04-18 08:20:17 +04:00
found_id = message_id ;
2009-06-10 00:33:32 +04:00
break ;
}
}
}
2009-08-15 12:01:38 +04:00
if ( cur & & cur - > subreq ) {
2012-08-05 17:00:23 +04:00
inhdr = SMBD_SMB2_IN_HDR_PTR ( cur ) ;
2010-04-18 08:20:17 +04:00
DEBUG ( 10 , ( " smbd_smb2_request_process_cancel: attempting to "
" cancel opcode[%s] mid %llu \n " ,
smb2_opcode_name ( ( uint16_t ) IVAL ( inhdr , SMB2_HDR_OPCODE ) ) ,
( unsigned long long ) found_id ) ) ;
2009-08-15 12:01:38 +04:00
tevent_req_cancel ( cur - > subreq ) ;
2009-06-10 00:33:32 +04:00
}
return NT_STATUS_OK ;
}
2011-07-08 21:44:29 +04:00
/*************************************************************
Ensure an incoming tid is a valid one for us to access .
Change to the associated uid credentials and chdir to the
valid tid directory .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS smbd_smb2_request_check_tcon ( struct smbd_smb2_request * req )
{
const uint8_t * inhdr ;
2011-10-27 18:35:28 +04:00
uint32_t in_flags ;
2011-07-08 21:44:29 +04:00
uint32_t in_tid ;
2012-07-02 15:31:12 +04:00
struct smbXsrv_tcon * tcon ;
2012-03-27 13:09:05 +04:00
NTSTATUS status ;
NTTIME now = timeval_to_nttime ( & req - > request_time ) ;
2011-07-08 21:44:29 +04:00
2011-10-27 18:35:28 +04:00
req - > tcon = NULL ;
2012-08-05 17:00:23 +04:00
inhdr = SMBD_SMB2_IN_HDR_PTR ( req ) ;
2011-07-08 21:44:29 +04:00
2011-10-27 18:35:28 +04:00
in_flags = IVAL ( inhdr , SMB2_HDR_FLAGS ) ;
2011-07-08 21:44:29 +04:00
in_tid = IVAL ( inhdr , SMB2_HDR_TID ) ;
2011-10-27 18:35:28 +04:00
if ( in_flags & SMB2_HDR_FLAG_CHAINED ) {
in_tid = req - > last_tid ;
2011-07-08 21:44:29 +04:00
}
2012-09-20 07:10:28 +04:00
req - > last_tid = 0 ;
2012-03-27 13:09:05 +04:00
status = smb2srv_tcon_lookup ( req - > session ,
in_tid , now , & tcon ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2011-07-08 21:44:29 +04:00
}
2012-03-27 13:09:05 +04:00
if ( ! change_to_user ( tcon - > compat , req - > session - > compat - > vuid ) ) {
2011-07-08 21:44:29 +04:00
return NT_STATUS_ACCESS_DENIED ;
}
/* should we pass FLAG_CASELESS_PATHNAMES here? */
2012-03-27 13:09:05 +04:00
if ( ! set_current_service ( tcon - > compat , 0 , true ) ) {
2011-07-08 21:44:29 +04:00
return NT_STATUS_ACCESS_DENIED ;
}
req - > tcon = tcon ;
2011-10-27 18:35:28 +04:00
req - > last_tid = in_tid ;
2011-07-08 21:44:29 +04:00
return NT_STATUS_OK ;
}
/*************************************************************
Ensure an incoming session_id is a valid one for us to access .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS smbd_smb2_request_check_session ( struct smbd_smb2_request * req )
{
const uint8_t * inhdr ;
2011-10-27 18:35:28 +04:00
uint32_t in_flags ;
2012-05-14 16:24:08 +04:00
uint16_t in_opcode ;
2011-07-08 21:44:29 +04:00
uint64_t in_session_id ;
2012-05-14 16:24:08 +04:00
struct smbXsrv_session * session = NULL ;
2012-03-27 13:09:05 +04:00
struct auth_session_info * session_info ;
2012-03-16 18:01:27 +04:00
NTSTATUS status ;
NTTIME now = timeval_to_nttime ( & req - > request_time ) ;
2011-10-27 18:35:28 +04:00
req - > session = NULL ;
req - > tcon = NULL ;
2011-07-08 21:44:29 +04:00
2012-08-05 17:00:23 +04:00
inhdr = SMBD_SMB2_IN_HDR_PTR ( req ) ;
2011-07-08 21:44:29 +04:00
2011-10-27 18:35:28 +04:00
in_flags = IVAL ( inhdr , SMB2_HDR_FLAGS ) ;
2012-05-14 16:24:08 +04:00
in_opcode = IVAL ( inhdr , SMB2_HDR_OPCODE ) ;
2011-07-08 21:44:29 +04:00
in_session_id = BVAL ( inhdr , SMB2_HDR_SESSION_ID ) ;
2011-10-27 18:35:28 +04:00
if ( in_flags & SMB2_HDR_FLAG_CHAINED ) {
in_session_id = req - > last_session_id ;
2011-07-08 21:44:29 +04:00
}
2012-09-20 07:10:28 +04:00
req - > last_session_id = 0 ;
2011-07-08 21:44:29 +04:00
/* lookup an existing session */
2012-03-16 18:01:27 +04:00
status = smb2srv_session_lookup ( req - > sconn - > conn ,
in_session_id , now ,
2012-03-27 13:09:05 +04:00
& session ) ;
2012-05-14 16:24:08 +04:00
if ( session ) {
req - > session = session ;
req - > last_session_id = in_session_id ;
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_SESSION_EXPIRED ) ) {
switch ( in_opcode ) {
case SMB2_OP_SESSSETUP :
status = NT_STATUS_OK ;
break ;
default :
break ;
}
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
switch ( in_opcode ) {
case SMB2_OP_TCON :
case SMB2_OP_CREATE :
case SMB2_OP_GETINFO :
case SMB2_OP_SETINFO :
return NT_STATUS_INVALID_HANDLE ;
default :
/*
* Notice the check for
* ( session_info = = NULL )
* below .
*/
status = NT_STATUS_OK ;
break ;
}
}
2012-03-16 18:01:27 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2011-07-08 21:44:29 +04:00
}
2012-03-27 13:09:05 +04:00
session_info = session - > global - > auth_session_info ;
if ( session_info = = NULL ) {
return NT_STATUS_INVALID_HANDLE ;
2011-07-08 21:44:29 +04:00
}
2012-03-27 13:09:05 +04:00
set_current_user_info ( session_info - > unix_info - > sanitized_username ,
session_info - > unix_info - > unix_name ,
session_info - > info - > domain_name ) ;
2011-07-08 21:44:29 +04:00
return NT_STATUS_OK ;
}
2012-02-28 05:49:12 +04:00
NTSTATUS smbd_smb2_request_verify_creditcharge ( struct smbd_smb2_request * req ,
uint32_t data_length )
{
uint16_t needed_charge ;
2012-06-26 01:35:46 +04:00
uint16_t credit_charge = 1 ;
2012-02-28 05:49:12 +04:00
const uint8_t * inhdr ;
2012-08-05 17:00:23 +04:00
inhdr = SMBD_SMB2_IN_HDR_PTR ( req ) ;
2012-06-26 01:35:46 +04:00
if ( req - > sconn - > smb2 . supports_multicredit ) {
credit_charge = SVAL ( inhdr , SMB2_HDR_CREDIT_CHARGE ) ;
credit_charge = MAX ( credit_charge , 1 ) ;
}
2012-02-28 05:49:12 +04:00
needed_charge = ( data_length - 1 ) / 65536 + 1 ;
2012-03-10 16:45:41 +04:00
DEBUG ( 10 , ( " mid %llu, CreditCharge: %d, NeededCharge: %d \n " ,
2012-03-13 16:08:50 +04:00
( unsigned long long ) BVAL ( inhdr , SMB2_HDR_MESSAGE_ID ) ,
credit_charge , needed_charge ) ) ;
2012-02-28 05:49:12 +04:00
if ( needed_charge > credit_charge ) {
DEBUG ( 2 , ( " CreditCharge too low, given %d, needed %d \n " ,
2012-06-26 01:35:46 +04:00
credit_charge , needed_charge ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
2012-02-28 05:49:12 +04:00
return NT_STATUS_OK ;
}
2011-09-06 16:00:04 +04:00
NTSTATUS smbd_smb2_request_verify_sizes ( struct smbd_smb2_request * req ,
size_t expected_body_size )
{
2012-08-08 10:05:55 +04:00
struct iovec * inhdr_v ;
2011-09-14 15:04:28 +04:00
const uint8_t * inhdr ;
uint16_t opcode ;
2011-09-06 16:00:04 +04:00
const uint8_t * inbody ;
size_t body_size ;
2011-09-14 15:04:28 +04:00
size_t min_dyn_size = expected_body_size & 0x00000001 ;
2012-08-08 10:05:55 +04:00
int max_idx = req - > in . vector_count - SMBD_SMB2_NUM_IOV_PER_REQ ;
2011-09-06 16:00:04 +04:00
/*
* The following should be checked already .
*/
2012-08-08 10:05:55 +04:00
if ( req - > in . vector_count < SMBD_SMB2_NUM_IOV_PER_REQ ) {
2011-09-06 16:00:04 +04:00
return NT_STATUS_INTERNAL_ERROR ;
}
2012-08-08 10:05:55 +04:00
if ( req - > current_idx > max_idx ) {
2011-09-06 16:00:04 +04:00
return NT_STATUS_INTERNAL_ERROR ;
}
2012-08-08 10:05:55 +04:00
inhdr_v = SMBD_SMB2_IN_HDR_IOV ( req ) ;
if ( inhdr_v - > iov_len ! = SMB2_HDR_BODY ) {
return NT_STATUS_INTERNAL_ERROR ;
}
if ( SMBD_SMB2_IN_BODY_LEN ( req ) < 2 ) {
2011-09-06 16:00:04 +04:00
return NT_STATUS_INTERNAL_ERROR ;
}
2012-08-08 10:05:55 +04:00
inhdr = SMBD_SMB2_IN_HDR_PTR ( req ) ;
2011-09-14 15:04:28 +04:00
opcode = SVAL ( inhdr , SMB2_HDR_OPCODE ) ;
switch ( opcode ) {
2011-09-26 01:39:07 +04:00
case SMB2_OP_IOCTL :
2011-09-14 15:04:28 +04:00
case SMB2_OP_GETINFO :
min_dyn_size = 0 ;
break ;
}
2011-09-06 16:00:04 +04:00
/*
* Now check the expected body size ,
* where the last byte might be in the
2012-02-28 05:52:21 +04:00
* dynamic section . .
2011-09-06 16:00:04 +04:00
*/
2012-08-08 10:05:55 +04:00
if ( SMBD_SMB2_IN_BODY_LEN ( req ) ! = ( expected_body_size & 0xFFFFFFFE ) ) {
2011-09-06 16:00:04 +04:00
return NT_STATUS_INVALID_PARAMETER ;
}
2012-08-08 10:05:55 +04:00
if ( SMBD_SMB2_IN_DYN_LEN ( req ) < min_dyn_size ) {
2011-09-06 16:00:04 +04:00
return NT_STATUS_INVALID_PARAMETER ;
}
2012-08-08 10:05:55 +04:00
inbody = SMBD_SMB2_IN_BODY_PTR ( req ) ;
2011-09-06 16:00:04 +04:00
body_size = SVAL ( inbody , 0x00 ) ;
if ( body_size ! = expected_body_size ) {
return NT_STATUS_INVALID_PARAMETER ;
}
return NT_STATUS_OK ;
}
2010-04-23 10:52:19 +04:00
NTSTATUS smbd_smb2_request_dispatch ( struct smbd_smb2_request * req )
2009-05-14 16:17:28 +04:00
{
2012-08-07 11:48:22 +04:00
struct smbXsrv_connection * conn = req - > sconn - > conn ;
2012-08-06 12:42:30 +04:00
const struct smbd_smb2_dispatch_table * call = NULL ;
2012-08-08 09:07:03 +04:00
const struct iovec * intf_v = SMBD_SMB2_IN_TF_IOV ( req ) ;
2009-05-14 16:17:28 +04:00
const uint8_t * inhdr ;
uint16_t opcode ;
2009-05-23 00:58:39 +04:00
uint32_t flags ;
2010-04-18 08:20:17 +04:00
uint64_t mid ;
2009-05-20 22:51:10 +04:00
NTSTATUS status ;
2009-05-23 00:58:39 +04:00
NTSTATUS session_status ;
2009-08-14 13:24:30 +04:00
uint32_t allowed_flags ;
2010-07-09 00:30:12 +04:00
NTSTATUS return_value ;
2012-03-16 18:01:27 +04:00
struct smbXsrv_session * x = NULL ;
2012-05-14 16:24:08 +04:00
bool signing_required = false ;
2012-08-08 09:07:03 +04:00
bool encryption_required = false ;
2009-05-14 16:17:28 +04:00
2012-08-05 17:00:23 +04:00
inhdr = SMBD_SMB2_IN_HDR_PTR ( req ) ;
2009-05-14 16:17:28 +04:00
/* TODO: verify more things */
2009-05-23 00:58:39 +04:00
flags = IVAL ( inhdr , SMB2_HDR_FLAGS ) ;
2009-05-14 16:17:28 +04:00
opcode = IVAL ( inhdr , SMB2_HDR_OPCODE ) ;
2010-04-18 08:20:17 +04:00
mid = BVAL ( inhdr , SMB2_HDR_MESSAGE_ID ) ;
DEBUG ( 10 , ( " smbd_smb2_request_dispatch: opcode[%s] mid = %llu \n " ,
smb2_opcode_name ( opcode ) ,
( unsigned long long ) mid ) ) ;
2009-05-23 00:58:39 +04:00
2012-08-07 11:48:22 +04:00
if ( conn - > protocol > = PROTOCOL_SMB2_02 ) {
2011-09-23 08:37:35 +04:00
/*
* once the protocol is negotiated
* SMB2_OP_NEGPROT is not allowed anymore
*/
if ( opcode = = SMB2_OP_NEGPROT ) {
/* drop the connection */
return NT_STATUS_INVALID_PARAMETER ;
}
} else {
/*
* if the protocol is not negotiated yet
* only SMB2_OP_NEGPROT is allowed .
*/
if ( opcode ! = SMB2_OP_NEGPROT ) {
/* drop the connection */
return NT_STATUS_INVALID_PARAMETER ;
}
}
2011-07-08 11:08:39 +04:00
/*
* Check if the client provided a valid session id ,
* if so smbd_smb2_request_check_session ( ) calls
* set_current_user_info ( ) .
*
* As some command don ' t require a valid session id
* we defer the check of the session_status
*/
2009-05-23 00:58:39 +04:00
session_status = smbd_smb2_request_check_session ( req ) ;
2012-03-27 13:09:05 +04:00
x = req - > session ;
2012-05-14 16:24:08 +04:00
if ( x ! = NULL ) {
signing_required = x - > global - > signing_required ;
2012-08-08 09:07:03 +04:00
encryption_required = x - > global - > encryption_required ;
2012-05-14 16:24:08 +04:00
if ( opcode = = SMB2_OP_SESSSETUP & &
x - > global - > channels [ 0 ] . signing_key . length ) {
signing_required = true ;
}
}
2012-08-08 09:07:03 +04:00
req - > do_signing = false ;
req - > do_encryption = false ;
if ( intf_v - > iov_len = = SMB2_TF_HDR_SIZE ) {
const uint8_t * intf = SMBD_SMB2_IN_TF_PTR ( req ) ;
uint64_t tf_session_id = BVAL ( intf , SMB2_TF_SESSION_ID ) ;
if ( x ! = NULL & & x - > global - > session_wire_id ! = tf_session_id ) {
DEBUG ( 0 , ( " smbd_smb2_request_dispatch: invalid session_id "
" in SMB2_HDR[%llu], SMB2_TF[%llu] \n " ,
( unsigned long long ) x - > global - > session_wire_id ,
( unsigned long long ) tf_session_id ) ) ;
/*
* TODO : windows allows this . . .
* should we drop the connection ?
*
* For now we just return ACCESS_DENIED
* ( Windows clients never trigger this )
* and wait for an update of [ MS - SMB2 ] .
*/
return smbd_smb2_request_error ( req ,
NT_STATUS_ACCESS_DENIED ) ;
}
req - > do_encryption = true ;
}
if ( encryption_required & & ! req - > do_encryption ) {
return smbd_smb2_request_error ( req ,
NT_STATUS_ACCESS_DENIED ) ;
}
2012-08-08 08:57:45 +04:00
call = smbd_smb2_call ( opcode ) ;
if ( call = = NULL ) {
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
allowed_flags = SMB2_HDR_FLAG_CHAINED |
SMB2_HDR_FLAG_SIGNED |
SMB2_HDR_FLAG_DFS ;
if ( opcode = = SMB2_OP_CANCEL ) {
allowed_flags | = SMB2_HDR_FLAG_ASYNC ;
}
if ( ( flags & ~ allowed_flags ) ! = 0 ) {
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
2012-09-20 02:52:19 +04:00
if ( flags & SMB2_HDR_FLAG_CHAINED ) {
/*
* This check is mostly for giving the correct error code
* for compounded requests .
*/
2012-09-22 06:06:27 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
2012-09-20 02:52:19 +04:00
} else {
req - > compat_chain_fsp = NULL ;
}
2012-08-08 09:07:03 +04:00
if ( req - > do_encryption ) {
signing_required = false ;
} else if ( flags & SMB2_HDR_FLAG_SIGNED ) {
2012-06-29 15:30:44 +04:00
DATA_BLOB signing_key ;
if ( x = = NULL ) {
return smbd_smb2_request_error (
req , NT_STATUS_ACCESS_DENIED ) ;
}
signing_key = x - > global - > channels [ 0 ] . signing_key ;
2012-03-16 18:01:27 +04:00
2012-08-16 17:14:51 +04:00
/*
* If we have a signing key , we should
* sign the response
*/
if ( signing_key . length > 0 ) {
req - > do_signing = true ;
}
2009-05-23 00:58:39 +04:00
2012-03-16 18:01:27 +04:00
status = smb2_signing_check_pdu ( signing_key ,
conn - > protocol ,
2012-08-07 14:26:38 +04:00
SMBD_SMB2_IN_HDR_IOV ( req ) ,
2012-09-20 02:42:02 +04:00
SMBD_SMB2_NUM_IOV_PER_REQ - 1 ) ;
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return smbd_smb2_request_error ( req , status ) ;
}
2012-08-16 17:08:40 +04:00
2012-08-16 17:14:51 +04:00
/*
* Now that we know the request was correctly signed
* we have to sign the response too .
*/
req - > do_signing = true ;
2012-08-16 17:08:40 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
return smbd_smb2_request_error ( req , session_status ) ;
}
2011-09-28 10:28:08 +04:00
} else if ( opcode = = SMB2_OP_CANCEL ) {
/* Cancel requests are allowed to skip the signing */
2012-05-14 16:24:08 +04:00
} else if ( signing_required ) {
2012-08-16 17:14:51 +04:00
/*
* If signing is required we try to sign
* a possible error response
*/
req - > do_signing = true ;
2009-05-23 00:58:39 +04:00
return smbd_smb2_request_error ( req , NT_STATUS_ACCESS_DENIED ) ;
}
2012-09-21 23:43:36 +04:00
if ( flags & SMB2_HDR_FLAG_CHAINED ) {
req - > compound_related = true ;
2011-08-27 01:23:26 +04:00
req - > sconn - > smb2 . compound_related_in_progress = true ;
}
2012-08-06 14:32:50 +04:00
if ( call - > need_session ) {
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
return smbd_smb2_request_error ( req , session_status ) ;
}
}
2012-08-06 14:32:50 +04:00
if ( call - > need_tcon ) {
SMB_ASSERT ( call - > need_session ) ;
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return smbd_smb2_request_error ( req , status ) ;
}
2012-08-08 09:07:03 +04:00
if ( req - > tcon - > global - > encryption_required ) {
encryption_required = true ;
}
if ( encryption_required & & ! req - > do_encryption ) {
return smbd_smb2_request_error ( req ,
NT_STATUS_ACCESS_DENIED ) ;
}
2012-08-06 14:32:50 +04:00
}
2012-08-07 11:44:31 +04:00
if ( call - > fileid_ofs ! = 0 ) {
size_t needed = call - > fileid_ofs + 16 ;
const uint8_t * body = SMBD_SMB2_IN_BODY_PTR ( req ) ;
size_t body_size = SMBD_SMB2_IN_BODY_LEN ( req ) ;
uint64_t file_id_persistent ;
uint64_t file_id_volatile ;
struct files_struct * fsp ;
SMB_ASSERT ( call - > need_tcon ) ;
if ( needed > body_size ) {
return smbd_smb2_request_error ( req ,
NT_STATUS_INVALID_PARAMETER ) ;
}
file_id_persistent = BVAL ( body , call - > fileid_ofs + 0 ) ;
file_id_volatile = BVAL ( body , call - > fileid_ofs + 8 ) ;
fsp = file_fsp_smb2 ( req , file_id_persistent , file_id_volatile ) ;
if ( fsp = = NULL ) {
if ( ! call - > allow_invalid_fileid ) {
return smbd_smb2_request_error ( req ,
NT_STATUS_FILE_CLOSED ) ;
}
if ( file_id_persistent ! = UINT64_MAX ) {
return smbd_smb2_request_error ( req ,
NT_STATUS_FILE_CLOSED ) ;
}
if ( file_id_volatile ! = UINT64_MAX ) {
return smbd_smb2_request_error ( req ,
NT_STATUS_FILE_CLOSED ) ;
}
}
}
2012-08-06 14:32:50 +04:00
if ( call - > as_root ) {
2012-08-07 11:44:31 +04:00
SMB_ASSERT ( call - > fileid_ofs = = 0 ) ;
s3:smb2_server: call change_to_root_user() or smbd_smb2_request_check_tcon()
For all requests which don't operate on a tcon, we should call
change_to_root_user(), to match the SMB1 behavior.
For SMB1 we do the following operations without AS_USER:
/* 0x70 */ { "SMBtcon",reply_tcon,0},
/* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR},
/* 0x72 */ { "SMBnegprot",reply_negprot,0},
/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
/* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
...
/* 0x2b */ { "SMBecho",reply_echo,0},
...
/* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 },
For SMB2tdis we still call smbd_smb2_request_check_tcon()
as close_cnum() calls change_to_root_user() when needed.
metze
Signed-off-by: Jeremy Allison <jra@samba.org>
2011-07-07 18:38:33 +04:00
/* This call needs to be run as root */
change_to_root_user ( ) ;
2012-08-06 14:32:50 +04:00
} else {
SMB_ASSERT ( call - > need_tcon ) ;
}
s3:smb2_server: call change_to_root_user() or smbd_smb2_request_check_tcon()
For all requests which don't operate on a tcon, we should call
change_to_root_user(), to match the SMB1 behavior.
For SMB1 we do the following operations without AS_USER:
/* 0x70 */ { "SMBtcon",reply_tcon,0},
/* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR},
/* 0x72 */ { "SMBnegprot",reply_negprot,0},
/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
/* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
...
/* 0x2b */ { "SMBecho",reply_echo,0},
...
/* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 },
For SMB2tdis we still call smbd_smb2_request_check_tcon()
as close_cnum() calls change_to_root_user() when needed.
metze
Signed-off-by: Jeremy Allison <jra@samba.org>
2011-07-07 18:38:33 +04:00
2012-08-06 14:32:50 +04:00
switch ( opcode ) {
case SMB2_OP_NEGPROT :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_negprot ) ;
return_value = smbd_smb2_request_process_negprot ( req ) ;
END_PROFILE ( smb2_negprot ) ;
}
break ;
2009-05-14 17:32:02 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_SESSSETUP :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_sesssetup ) ;
return_value = smbd_smb2_request_process_sesssetup ( req ) ;
END_PROFILE ( smb2_sesssetup ) ;
}
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_LOGOFF :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_logoff ) ;
return_value = smbd_smb2_request_process_logoff ( req ) ;
END_PROFILE ( smb2_logoff ) ;
2009-05-20 22:51:10 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_TCON :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_tcon ) ;
return_value = smbd_smb2_request_process_tcon ( req ) ;
END_PROFILE ( smb2_tcon ) ;
2009-05-20 22:51:10 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_TDIS :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_tdis ) ;
return_value = smbd_smb2_request_process_tdis ( req ) ;
END_PROFILE ( smb2_tdis ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_CREATE :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_create ) ;
return_value = smbd_smb2_request_process_create ( req ) ;
END_PROFILE ( smb2_create ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_CLOSE :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_close ) ;
return_value = smbd_smb2_request_process_close ( req ) ;
END_PROFILE ( smb2_close ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_FLUSH :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_flush ) ;
return_value = smbd_smb2_request_process_flush ( req ) ;
END_PROFILE ( smb2_flush ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_READ :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_read ) ;
return_value = smbd_smb2_request_process_read ( req ) ;
END_PROFILE ( smb2_read ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_WRITE :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_write ) ;
return_value = smbd_smb2_request_process_write ( req ) ;
END_PROFILE ( smb2_write ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_LOCK :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_lock ) ;
return_value = smbd_smb2_request_process_lock ( req ) ;
END_PROFILE ( smb2_lock ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_IOCTL :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_ioctl ) ;
return_value = smbd_smb2_request_process_ioctl ( req ) ;
END_PROFILE ( smb2_ioctl ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_CANCEL :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_cancel ) ;
return_value = smbd_smb2_request_process_cancel ( req ) ;
END_PROFILE ( smb2_cancel ) ;
}
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_KEEPALIVE :
s3:smb2_server: call change_to_root_user() or smbd_smb2_request_check_tcon()
For all requests which don't operate on a tcon, we should call
change_to_root_user(), to match the SMB1 behavior.
For SMB1 we do the following operations without AS_USER:
/* 0x70 */ { "SMBtcon",reply_tcon,0},
/* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR},
/* 0x72 */ { "SMBnegprot",reply_negprot,0},
/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
/* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
...
/* 0x2b */ { "SMBecho",reply_echo,0},
...
/* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 },
For SMB2tdis we still call smbd_smb2_request_check_tcon()
as close_cnum() calls change_to_root_user() when needed.
metze
Signed-off-by: Jeremy Allison <jra@samba.org>
2011-07-07 18:38:33 +04:00
{
START_PROFILE ( smb2_keepalive ) ;
return_value = smbd_smb2_request_process_keepalive ( req ) ;
END_PROFILE ( smb2_keepalive ) ;
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_FIND :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_find ) ;
return_value = smbd_smb2_request_process_find ( req ) ;
END_PROFILE ( smb2_find ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_NOTIFY :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_notify ) ;
return_value = smbd_smb2_request_process_notify ( req ) ;
END_PROFILE ( smb2_notify ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_GETINFO :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_getinfo ) ;
return_value = smbd_smb2_request_process_getinfo ( req ) ;
END_PROFILE ( smb2_getinfo ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_SETINFO :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_setinfo ) ;
return_value = smbd_smb2_request_process_setinfo ( req ) ;
END_PROFILE ( smb2_setinfo ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_BREAK :
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_break ) ;
return_value = smbd_smb2_request_process_break ( req ) ;
END_PROFILE ( smb2_break ) ;
}
break ;
default :
return_value = smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
break ;
}
return return_value ;
2009-05-14 16:17:28 +04:00
}
static NTSTATUS smbd_smb2_request_reply ( struct smbd_smb2_request * req )
{
2012-08-07 16:24:28 +04:00
struct smbXsrv_connection * conn = req - > sconn - > conn ;
2009-05-14 16:17:28 +04:00
struct tevent_req * subreq ;
2012-08-08 09:07:03 +04:00
int first_idx = 1 ;
struct iovec * firsttf = SMBD_SMB2_IDX_TF_IOV ( req , out , first_idx ) ;
2012-08-07 14:55:28 +04:00
struct iovec * outhdr = SMBD_SMB2_OUT_HDR_IOV ( req ) ;
struct iovec * outdyn = SMBD_SMB2_OUT_DYN_IOV ( req ) ;
2009-05-14 16:17:28 +04:00
2009-08-15 12:01:38 +04:00
req - > subreq = NULL ;
2011-11-09 14:47:33 +04:00
TALLOC_FREE ( req - > async_te ) ;
2009-08-15 12:01:38 +04:00
2012-08-08 09:07:03 +04:00
if ( req - > do_encryption & &
( firsttf - > iov_len = = 0 ) & &
( req - > first_key . length = = 0 ) & &
( req - > session ! = NULL ) & &
( req - > session - > global - > encryption_key . length ! = 0 ) )
{
DATA_BLOB encryption_key = req - > session - > global - > encryption_key ;
uint8_t * tf ;
uint64_t session_id = req - > session - > global - > session_wire_id ;
2012-08-22 12:30:52 +04:00
struct smbXsrv_session * x = req - > session ;
uint64_t nonce_high ;
uint64_t nonce_low ;
nonce_high = x - > nonce_high ;
nonce_low = x - > nonce_low ;
2012-08-08 09:07:03 +04:00
2012-08-22 12:30:52 +04:00
x - > nonce_low + = 1 ;
if ( x - > nonce_low = = 0 ) {
x - > nonce_low + = 1 ;
x - > nonce_high + = 1 ;
2012-08-08 09:07:03 +04:00
}
/*
* We need to place the SMB2_TRANSFORM header before the
* first SMB2 header
*/
/*
* we need to remember the encryption key
* and defer the signing / encryption until
* we are sure that we do not change
* the header again .
*/
req - > first_key = data_blob_dup_talloc ( req , encryption_key ) ;
if ( req - > first_key . data = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
tf = talloc_zero_array ( req - > out . vector , uint8_t ,
SMB2_TF_HDR_SIZE ) ;
if ( tf = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
SIVAL ( tf , SMB2_TF_PROTOCOL_ID , SMB2_TF_MAGIC ) ;
SBVAL ( tf , SMB2_TF_NONCE + 0 , nonce_low ) ;
SBVAL ( tf , SMB2_TF_NONCE + 8 , nonce_high ) ;
SBVAL ( tf , SMB2_TF_SESSION_ID , session_id ) ;
firsttf - > iov_base = ( void * ) tf ;
firsttf - > iov_len = SMB2_TF_HDR_SIZE ;
}
2012-08-07 16:24:28 +04:00
if ( ( req - > current_idx > SMBD_SMB2_NUM_IOV_PER_REQ ) & &
2012-08-08 09:07:03 +04:00
( req - > last_key . length > 0 ) & &
( firsttf - > iov_len = = 0 ) )
{
2012-08-07 16:24:28 +04:00
int last_idx = req - > current_idx - SMBD_SMB2_NUM_IOV_PER_REQ ;
2012-08-08 06:35:15 +04:00
struct iovec * lasthdr = SMBD_SMB2_IDX_HDR_IOV ( req , out , last_idx ) ;
NTSTATUS status ;
/*
* As we are sure the header of the last request in the
* compound chain will not change , we can to sign here
* with the last signing key we remembered .
*/
status = smb2_signing_sign_pdu ( req - > last_key ,
conn - > protocol ,
lasthdr ,
2012-08-08 09:07:03 +04:00
SMBD_SMB2_NUM_IOV_PER_REQ - 1 ) ;
2012-08-08 06:35:15 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2012-08-07 16:24:28 +04:00
}
2012-08-08 06:35:15 +04:00
data_blob_clear_free ( & req - > last_key ) ;
2012-08-07 16:24:28 +04:00
2012-08-07 14:26:38 +04:00
req - > current_idx + = SMBD_SMB2_NUM_IOV_PER_REQ ;
2009-05-14 16:17:28 +04:00
2009-06-05 21:49:40 +04:00
if ( req - > current_idx < req - > out . vector_count ) {
2010-04-18 08:20:17 +04:00
/*
* We must process the remaining compound
* SMB2 requests before any new incoming SMB2
* requests . This is because incoming SMB2
* requests may include a cancel for a
* compound request we haven ' t processed
* yet .
*/
struct tevent_immediate * im = tevent_create_immediate ( req ) ;
if ( ! im ) {
2009-05-14 16:17:28 +04:00
return NT_STATUS_NO_MEMORY ;
}
2012-08-07 16:24:28 +04:00
2012-08-08 09:07:03 +04:00
if ( req - > do_signing & & firsttf - > iov_len = = 0 ) {
2012-08-07 16:24:28 +04:00
struct smbXsrv_session * x = req - > session ;
DATA_BLOB signing_key = x - > global - > channels [ 0 ] . signing_key ;
/*
* we need to remember the signing key
* and defer the signing until
* we are sure that we do not change
* the header again .
*/
req - > last_key = data_blob_dup_talloc ( req , signing_key ) ;
if ( req - > last_key . data = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
}
2010-04-18 08:20:17 +04:00
tevent_schedule_immediate ( im ,
2011-12-12 17:15:03 +04:00
req - > sconn - > ev_ctx ,
2010-04-23 10:52:19 +04:00
smbd_smb2_request_dispatch_immediate ,
2009-05-14 16:17:28 +04:00
req ) ;
return NT_STATUS_OK ;
}
2011-08-27 01:23:26 +04:00
if ( req - > compound_related ) {
2012-09-21 23:43:36 +04:00
req - > compound_related = false ;
2011-08-27 01:23:26 +04:00
req - > sconn - > smb2 . compound_related_in_progress = false ;
}
2010-12-20 21:44:48 +03:00
smb2_setup_nbt_length ( req - > out . vector , req - > out . vector_count ) ;
2011-11-15 23:27:56 +04:00
/* Set credit for these operations (zero credits if this
2010-12-20 21:44:48 +03:00
is a final reply for an async operation ) . */
2011-11-15 23:27:56 +04:00
smb2_calculate_credits ( req , req ) ;
2010-12-20 21:44:48 +03:00
2012-08-07 16:24:28 +04:00
/*
* now check if we need to sign the current response
*/
2012-08-08 09:07:03 +04:00
if ( firsttf - > iov_len = = SMB2_TF_HDR_SIZE ) {
NTSTATUS status ;
status = smb2_signing_encrypt_pdu ( req - > first_key ,
conn - > protocol ,
firsttf ,
req - > out . vector_count - first_idx ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
} else if ( req - > do_signing ) {
2010-12-20 21:44:48 +03:00
NTSTATUS status ;
2012-03-27 13:09:05 +04:00
struct smbXsrv_session * x = req - > session ;
2012-03-16 18:01:27 +04:00
DATA_BLOB signing_key = x - > global - > channels [ 0 ] . signing_key ;
status = smb2_signing_sign_pdu ( signing_key ,
conn - > protocol ,
2012-08-07 14:55:28 +04:00
outhdr ,
2012-08-08 09:07:03 +04:00
SMBD_SMB2_NUM_IOV_PER_REQ - 1 ) ;
2010-12-20 21:44:48 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
2012-08-08 09:07:03 +04:00
data_blob_clear_free ( & req - > first_key ) ;
2010-04-18 08:20:17 +04:00
2010-12-15 00:36:08 +03:00
/* I am a sick, sick man... :-). Sendfile hack ... JRA. */
2012-08-07 14:56:23 +04:00
if ( req - > out . vector_count < ( 2 * SMBD_SMB2_NUM_IOV_PER_REQ ) & &
2012-08-07 14:55:28 +04:00
outdyn - > iov_base = = NULL & & outdyn - > iov_len ! = 0 ) {
2010-12-15 00:36:08 +03:00
/* Dynamic part is NULL. Chop it off,
We ' re going to send it via sendfile . */
req - > out . vector_count - = 1 ;
}
2009-05-14 16:17:28 +04:00
subreq = tstream_writev_queue_send ( req ,
2011-12-12 17:15:03 +04:00
req - > sconn - > ev_ctx ,
2009-08-07 17:21:07 +04:00
req - > sconn - > smb2 . stream ,
req - > sconn - > smb2 . send_queue ,
2009-05-14 16:17:28 +04:00
req - > out . vector ,
req - > out . vector_count ) ;
if ( subreq = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
tevent_req_set_callback ( subreq , smbd_smb2_request_writev_done , req ) ;
2010-06-03 03:43:31 +04:00
/*
* We ' re done with this request -
* move it off the " being processed " queue .
*/
DLIST_REMOVE ( req - > sconn - > smb2 . requests , req ) ;
2009-05-14 16:17:28 +04:00
return NT_STATUS_OK ;
}
2011-08-27 01:23:26 +04:00
static NTSTATUS smbd_smb2_request_next_incoming ( struct smbd_server_connection * sconn ) ;
2010-04-23 10:52:19 +04:00
void smbd_smb2_request_dispatch_immediate ( struct tevent_context * ctx ,
2010-04-18 08:20:17 +04:00
struct tevent_immediate * im ,
void * private_data )
2009-05-20 21:48:47 +04:00
{
2010-04-18 08:20:17 +04:00
struct smbd_smb2_request * req = talloc_get_type_abort ( private_data ,
2009-05-20 21:48:47 +04:00
struct smbd_smb2_request ) ;
2009-08-07 17:21:07 +04:00
struct smbd_server_connection * sconn = req - > sconn ;
2009-05-20 21:48:47 +04:00
NTSTATUS status ;
2010-04-18 08:20:17 +04:00
TALLOC_FREE ( im ) ;
2009-05-20 21:48:47 +04:00
2010-04-18 08:20:17 +04:00
if ( DEBUGLEVEL > = 10 ) {
2010-04-23 10:52:19 +04:00
DEBUG ( 10 , ( " smbd_smb2_request_dispatch_immediate: idx[%d] of %d vectors \n " ,
2010-04-18 08:20:17 +04:00
req - > current_idx , req - > in . vector_count ) ) ;
print_req_vectors ( req ) ;
}
2009-05-20 21:48:47 +04:00
status = smbd_smb2_request_dispatch ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-20 21:48:47 +04:00
return ;
}
2011-08-27 01:23:26 +04:00
status = smbd_smb2_request_next_incoming ( sconn ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
return ;
}
}
2011-08-05 22:34:43 +04:00
2009-05-20 21:48:47 +04:00
static void smbd_smb2_request_writev_done ( struct tevent_req * subreq )
{
struct smbd_smb2_request * req = tevent_req_callback_data ( subreq ,
struct smbd_smb2_request ) ;
2009-08-07 17:21:07 +04:00
struct smbd_server_connection * sconn = req - > sconn ;
2009-05-20 21:48:47 +04:00
int ret ;
int sys_errno ;
2011-08-05 22:34:43 +04:00
NTSTATUS status ;
2009-05-20 21:48:47 +04:00
ret = tstream_writev_queue_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
2009-06-09 23:29:40 +04:00
TALLOC_FREE ( req ) ;
2009-05-20 21:48:47 +04:00
if ( ret = = - 1 ) {
2011-08-05 22:34:43 +04:00
status = map_nt_error_from_unix ( sys_errno ) ;
2010-04-18 08:20:17 +04:00
DEBUG ( 2 , ( " smbd_smb2_request_writev_done: client write error %s \n " ,
nt_errstr ( status ) ) ) ;
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-20 21:48:47 +04:00
return ;
}
2011-08-05 22:34:43 +04:00
status = smbd_smb2_request_next_incoming ( sconn ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
return ;
}
2009-05-20 21:48:47 +04:00
}
2009-05-20 21:35:39 +04:00
NTSTATUS smbd_smb2_request_done_ex ( struct smbd_smb2_request * req ,
NTSTATUS status ,
2009-06-05 13:05:03 +04:00
DATA_BLOB body , DATA_BLOB * dyn ,
const char * location )
2009-05-14 16:17:28 +04:00
{
uint8_t * outhdr ;
2012-08-08 10:05:55 +04:00
struct iovec * outbody_v ;
struct iovec * outdyn_v ;
2009-05-14 16:17:28 +04:00
uint32_t next_command_ofs ;
2009-05-20 21:35:39 +04:00
DEBUG ( 10 , ( " smbd_smb2_request_done_ex: "
2009-06-05 13:05:03 +04:00
" idx[%d] status[%s] body[%u] dyn[%s:%u] at %s \n " ,
2012-08-08 10:05:55 +04:00
req - > current_idx , nt_errstr ( status ) , ( unsigned int ) body . length ,
2009-05-14 16:17:28 +04:00
dyn ? " yes " : " no " ,
2009-06-05 13:05:03 +04:00
( unsigned int ) ( dyn ? dyn - > length : 0 ) ,
location ) ) ;
2009-05-14 16:17:28 +04:00
if ( body . length < 2 ) {
return smbd_smb2_request_error ( req , NT_STATUS_INTERNAL_ERROR ) ;
}
if ( ( body . length % 2 ) ! = 0 ) {
return smbd_smb2_request_error ( req , NT_STATUS_INTERNAL_ERROR ) ;
}
2012-08-08 10:05:55 +04:00
outhdr = SMBD_SMB2_OUT_HDR_PTR ( req ) ;
outbody_v = SMBD_SMB2_OUT_BODY_IOV ( req ) ;
outdyn_v = SMBD_SMB2_OUT_DYN_IOV ( req ) ;
2009-05-14 16:17:28 +04:00
2009-05-21 18:17:53 +04:00
next_command_ofs = IVAL ( outhdr , SMB2_HDR_NEXT_COMMAND ) ;
2009-05-20 21:35:39 +04:00
SIVAL ( outhdr , SMB2_HDR_STATUS , NT_STATUS_V ( status ) ) ;
2009-05-14 16:17:28 +04:00
2012-08-08 10:05:55 +04:00
outbody_v - > iov_base = ( void * ) body . data ;
outbody_v - > iov_len = body . length ;
2009-05-14 16:17:28 +04:00
if ( dyn ) {
2012-08-08 10:05:55 +04:00
outdyn_v - > iov_base = ( void * ) dyn - > data ;
outdyn_v - > iov_len = dyn - > length ;
2009-05-14 16:17:28 +04:00
} else {
2012-08-08 10:05:55 +04:00
outdyn_v - > iov_base = NULL ;
outdyn_v - > iov_len = 0 ;
2009-05-14 16:17:28 +04:00
}
/* see if we need to recalculate the offset to the next response */
if ( next_command_ofs > 0 ) {
next_command_ofs = SMB2_HDR_BODY ;
2012-08-08 10:05:55 +04:00
next_command_ofs + = SMBD_SMB2_OUT_BODY_LEN ( req ) ;
next_command_ofs + = SMBD_SMB2_OUT_DYN_LEN ( req ) ;
2009-05-14 16:17:28 +04:00
}
if ( ( next_command_ofs % 8 ) ! = 0 ) {
2009-06-09 21:21:26 +04:00
size_t pad_size = 8 - ( next_command_ofs % 8 ) ;
2012-08-08 10:05:55 +04:00
if ( SMBD_SMB2_OUT_DYN_LEN ( req ) = = 0 ) {
2009-06-09 21:21:26 +04:00
/*
* if the dyn buffer is empty
* we can use it to add padding
*/
uint8_t * pad ;
pad = talloc_zero_array ( req - > out . vector ,
uint8_t , pad_size ) ;
if ( pad = = NULL ) {
return smbd_smb2_request_error ( req ,
NT_STATUS_NO_MEMORY ) ;
}
2012-08-08 10:05:55 +04:00
outdyn_v - > iov_base = ( void * ) pad ;
outdyn_v - > iov_len = pad_size ;
2009-06-09 21:21:26 +04:00
} else {
/*
* For now we copy the dynamic buffer
* and add the padding to the new buffer
*/
size_t old_size ;
uint8_t * old_dyn ;
size_t new_size ;
uint8_t * new_dyn ;
2012-08-08 10:05:55 +04:00
old_size = SMBD_SMB2_OUT_DYN_LEN ( req ) ;
old_dyn = SMBD_SMB2_OUT_DYN_PTR ( req ) ;
2009-06-09 21:21:26 +04:00
new_size = old_size + pad_size ;
2010-04-20 00:43:42 +04:00
new_dyn = talloc_zero_array ( req - > out . vector ,
2009-06-09 21:21:26 +04:00
uint8_t , new_size ) ;
if ( new_dyn = = NULL ) {
return smbd_smb2_request_error ( req ,
NT_STATUS_NO_MEMORY ) ;
}
memcpy ( new_dyn , old_dyn , old_size ) ;
memset ( new_dyn + old_size , 0 , pad_size ) ;
2012-08-08 10:05:55 +04:00
outdyn_v - > iov_base = ( void * ) new_dyn ;
outdyn_v - > iov_len = new_size ;
2009-06-09 21:21:26 +04:00
}
next_command_ofs + = pad_size ;
2009-05-14 16:17:28 +04:00
}
SIVAL ( outhdr , SMB2_HDR_NEXT_COMMAND , next_command_ofs ) ;
return smbd_smb2_request_reply ( req ) ;
}
2010-04-20 01:32:08 +04:00
NTSTATUS smbd_smb2_request_error_ex ( struct smbd_smb2_request * req ,
NTSTATUS status ,
DATA_BLOB * info ,
const char * location )
{
DATA_BLOB body ;
2012-08-05 17:00:23 +04:00
uint8_t * outhdr = SMBD_SMB2_OUT_HDR_PTR ( req ) ;
2010-04-20 01:32:08 +04:00
DEBUG ( 10 , ( " smbd_smb2_request_error_ex: idx[%d] status[%s] |%s| at %s \n " ,
2012-08-08 11:12:31 +04:00
req - > current_idx , nt_errstr ( status ) , info ? " +info " : " " ,
2010-04-20 01:32:08 +04:00
location ) ) ;
body . data = outhdr + SMB2_HDR_BODY ;
body . length = 8 ;
SSVAL ( body . data , 0 , 9 ) ;
if ( info ) {
SIVAL ( body . data , 0x04 , info - > length ) ;
} else {
/* Allocated size of req->out.vector[i].iov_base
* * MUST BE * OUTVEC_ALLOC_SIZE . So we have room for
* 1 byte without having to do an alloc .
*/
info = talloc_zero_array ( req - > out . vector ,
DATA_BLOB ,
1 ) ;
if ( ! info ) {
return NT_STATUS_NO_MEMORY ;
}
info - > data = ( ( uint8_t * ) outhdr ) +
OUTVEC_ALLOC_SIZE - 1 ;
info - > length = 1 ;
SCVAL ( info - > data , 0 , 0 ) ;
}
/*
2012-09-19 22:39:07 +04:00
* Note : Even if there is an error , continue to process the request .
* per MS - SMB2 .
2010-04-20 01:32:08 +04:00
*/
return smbd_smb2_request_done_ex ( req , status , body , info , __location__ ) ;
}
2009-06-09 22:44:13 +04:00
struct smbd_smb2_send_oplock_break_state {
struct smbd_server_connection * sconn ;
2012-08-22 12:33:07 +04:00
uint8_t buf [ NBT_HDR_SIZE + SMB2_TF_HDR_SIZE + SMB2_HDR_BODY + 0x18 ] ;
struct iovec vector [ 1 + SMBD_SMB2_NUM_IOV_PER_REQ ] ;
2009-06-09 22:44:13 +04:00
} ;
static void smbd_smb2_oplock_break_writev_done ( struct tevent_req * subreq ) ;
NTSTATUS smbd_smb2_send_oplock_break ( struct smbd_server_connection * sconn ,
2012-08-22 12:33:07 +04:00
struct smbXsrv_session * session ,
struct smbXsrv_tcon * tcon ,
struct smbXsrv_open * op ,
2009-06-09 22:44:13 +04:00
uint8_t oplock_level )
{
struct smbd_smb2_send_oplock_break_state * state ;
2012-08-22 12:33:07 +04:00
struct smbXsrv_connection * conn = sconn - > conn ;
2009-06-09 22:44:13 +04:00
struct tevent_req * subreq ;
2012-08-22 12:33:07 +04:00
uint8_t * tf ;
size_t tf_len ;
2009-06-09 22:44:13 +04:00
uint8_t * hdr ;
uint8_t * body ;
2012-08-22 12:33:07 +04:00
size_t body_len ;
uint8_t * dyn ;
size_t dyn_len ;
bool do_encryption = session - > global - > encryption_required ;
uint64_t nonce_high = 0 ;
uint64_t nonce_low = 0 ;
if ( tcon - > global - > encryption_required ) {
do_encryption = true ;
}
2009-06-09 22:44:13 +04:00
state = talloc ( sconn , struct smbd_smb2_send_oplock_break_state ) ;
if ( state = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
state - > sconn = sconn ;
2012-08-22 12:33:07 +04:00
tf = state - > buf + NBT_HDR_SIZE ;
tf_len = SMB2_TF_HDR_SIZE ;
hdr = tf + tf_len ;
2009-06-09 22:44:13 +04:00
body = hdr + SMB2_HDR_BODY ;
2012-08-22 12:33:07 +04:00
body_len = 0x18 ;
dyn = body + body_len ;
dyn_len = 0 ;
if ( do_encryption ) {
nonce_high = session - > nonce_high ;
nonce_low = session - > nonce_low ;
session - > nonce_low + = 1 ;
if ( session - > nonce_low = = 0 ) {
session - > nonce_low + = 1 ;
session - > nonce_high + = 1 ;
}
}
SIVAL ( tf , SMB2_TF_PROTOCOL_ID , SMB2_TF_MAGIC ) ;
SBVAL ( tf , SMB2_TF_NONCE + 0 , nonce_low ) ;
SBVAL ( tf , SMB2_TF_NONCE + 8 , nonce_high ) ;
SBVAL ( tf , SMB2_TF_SESSION_ID , session - > global - > session_wire_id ) ;
2009-06-09 22:44:13 +04:00
SIVAL ( hdr , 0 , SMB2_MAGIC ) ;
SSVAL ( hdr , SMB2_HDR_LENGTH , SMB2_HDR_BODY ) ;
SSVAL ( hdr , SMB2_HDR_EPOCH , 0 ) ;
SIVAL ( hdr , SMB2_HDR_STATUS , 0 ) ;
SSVAL ( hdr , SMB2_HDR_OPCODE , SMB2_OP_BREAK ) ;
SSVAL ( hdr , SMB2_HDR_CREDIT , 0 ) ;
SIVAL ( hdr , SMB2_HDR_FLAGS , SMB2_HDR_FLAG_REDIRECT ) ;
SIVAL ( hdr , SMB2_HDR_NEXT_COMMAND , 0 ) ;
SBVAL ( hdr , SMB2_HDR_MESSAGE_ID , UINT64_MAX ) ;
SIVAL ( hdr , SMB2_HDR_PID , 0 ) ;
SIVAL ( hdr , SMB2_HDR_TID , 0 ) ;
SBVAL ( hdr , SMB2_HDR_SESSION_ID , 0 ) ;
memset ( hdr + SMB2_HDR_SIGNATURE , 0 , 16 ) ;
2012-08-22 12:33:07 +04:00
SSVAL ( body , 0x00 , body_len ) ;
2009-06-09 22:44:13 +04:00
SCVAL ( body , 0x02 , oplock_level ) ;
SCVAL ( body , 0x03 , 0 ) ; /* reserved */
SIVAL ( body , 0x04 , 0 ) ; /* reserved */
2012-08-22 12:33:07 +04:00
SBVAL ( body , 0x08 , op - > global - > open_persistent_id ) ;
SBVAL ( body , 0x10 , op - > global - > open_volatile_id ) ;
state - > vector [ 0 ] . iov_base = ( void * ) state - > buf ;
state - > vector [ 0 ] . iov_len = NBT_HDR_SIZE ;
if ( do_encryption ) {
state - > vector [ 1 + SMBD_SMB2_TF_IOV_OFS ] . iov_base = tf ;
state - > vector [ 1 + SMBD_SMB2_TF_IOV_OFS ] . iov_len = tf_len ;
} else {
state - > vector [ 1 + SMBD_SMB2_TF_IOV_OFS ] . iov_base = NULL ;
state - > vector [ 1 + SMBD_SMB2_TF_IOV_OFS ] . iov_len = 0 ;
}
state - > vector [ 1 + SMBD_SMB2_HDR_IOV_OFS ] . iov_base = hdr ;
state - > vector [ 1 + SMBD_SMB2_HDR_IOV_OFS ] . iov_len = SMB2_HDR_BODY ;
state - > vector [ 1 + SMBD_SMB2_BODY_IOV_OFS ] . iov_base = body ;
state - > vector [ 1 + SMBD_SMB2_BODY_IOV_OFS ] . iov_len = body_len ;
state - > vector [ 1 + SMBD_SMB2_DYN_IOV_OFS ] . iov_base = dyn ;
state - > vector [ 1 + SMBD_SMB2_DYN_IOV_OFS ] . iov_len = dyn_len ;
smb2_setup_nbt_length ( state - > vector , 1 + SMBD_SMB2_NUM_IOV_PER_REQ ) ;
if ( do_encryption ) {
NTSTATUS status ;
DATA_BLOB encryption_key = session - > global - > encryption_key ;
status = smb2_signing_encrypt_pdu ( encryption_key ,
conn - > protocol ,
& state - > vector [ 1 + SMBD_SMB2_TF_IOV_OFS ] ,
SMBD_SMB2_NUM_IOV_PER_REQ ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
2009-06-09 22:44:13 +04:00
subreq = tstream_writev_queue_send ( state ,
2011-12-12 17:15:03 +04:00
sconn - > ev_ctx ,
2009-06-09 22:44:13 +04:00
sconn - > smb2 . stream ,
sconn - > smb2 . send_queue ,
2012-08-22 12:33:07 +04:00
state - > vector ,
ARRAY_SIZE ( state - > vector ) ) ;
2009-06-09 22:44:13 +04:00
if ( subreq = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
tevent_req_set_callback ( subreq ,
smbd_smb2_oplock_break_writev_done ,
state ) ;
return NT_STATUS_OK ;
}
static void smbd_smb2_oplock_break_writev_done ( struct tevent_req * subreq )
{
struct smbd_smb2_send_oplock_break_state * state =
tevent_req_callback_data ( subreq ,
struct smbd_smb2_send_oplock_break_state ) ;
struct smbd_server_connection * sconn = state - > sconn ;
int ret ;
int sys_errno ;
ret = tstream_writev_queue_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
NTSTATUS status = map_nt_error_from_unix ( sys_errno ) ;
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
return ;
}
TALLOC_FREE ( state ) ;
}
2009-05-14 16:17:28 +04:00
struct smbd_smb2_request_read_state {
2012-08-05 22:52:55 +04:00
struct tevent_context * ev ;
struct smbd_server_connection * sconn ;
2009-05-14 16:17:28 +04:00
struct smbd_smb2_request * smb2_req ;
2012-08-05 22:52:55 +04:00
struct {
uint8_t nbt [ NBT_HDR_SIZE ] ;
bool done ;
} hdr ;
size_t pktlen ;
uint8_t * pktbuf ;
2009-05-14 16:17:28 +04:00
} ;
static int smbd_smb2_request_next_vector ( struct tstream_context * stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count ) ;
static void smbd_smb2_request_read_done ( struct tevent_req * subreq ) ;
static struct tevent_req * smbd_smb2_request_read_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
2009-08-07 17:21:07 +04:00
struct smbd_server_connection * sconn )
2009-05-14 16:17:28 +04:00
{
struct tevent_req * req ;
struct smbd_smb2_request_read_state * state ;
struct tevent_req * subreq ;
req = tevent_req_create ( mem_ctx , & state ,
struct smbd_smb2_request_read_state ) ;
if ( req = = NULL ) {
return NULL ;
}
2012-08-05 22:52:55 +04:00
state - > ev = ev ;
state - > sconn = sconn ;
2009-05-14 16:17:28 +04:00
2009-06-09 23:29:40 +04:00
state - > smb2_req = smbd_smb2_request_allocate ( state ) ;
2009-05-14 16:17:28 +04:00
if ( tevent_req_nomem ( state - > smb2_req , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2009-08-07 17:21:07 +04:00
state - > smb2_req - > sconn = sconn ;
2009-05-14 16:17:28 +04:00
2012-08-05 22:52:55 +04:00
subreq = tstream_readv_pdu_queue_send ( state - > smb2_req ,
state - > ev ,
state - > sconn - > smb2 . stream ,
state - > sconn - > smb2 . recv_queue ,
smbd_smb2_request_next_vector ,
state ) ;
2009-05-14 16:17:28 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , smbd_smb2_request_read_done , req ) ;
return req ;
}
static int smbd_smb2_request_next_vector ( struct tstream_context * stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count )
{
struct smbd_smb2_request_read_state * state =
talloc_get_type_abort ( private_data ,
struct smbd_smb2_request_read_state ) ;
struct iovec * vector ;
2012-08-05 22:52:55 +04:00
if ( state - > pktlen > 0 ) {
2009-05-14 16:17:28 +04:00
/* if there're no remaining bytes, we're done */
* _vector = NULL ;
* _count = 0 ;
return 0 ;
}
2012-08-05 22:52:55 +04:00
if ( ! state - > hdr . done ) {
2009-05-14 16:17:28 +04:00
/*
2012-08-05 22:52:55 +04:00
* first we need to get the NBT header
2009-05-14 16:17:28 +04:00
*/
2012-08-05 22:52:55 +04:00
vector = talloc_array ( mem_ctx , struct iovec , 1 ) ;
2009-05-14 16:17:28 +04:00
if ( vector = = NULL ) {
return - 1 ;
}
2012-08-05 22:52:55 +04:00
vector [ 0 ] . iov_base = ( void * ) state - > hdr . nbt ;
vector [ 0 ] . iov_len = NBT_HDR_SIZE ;
2009-05-14 16:17:28 +04:00
* _vector = vector ;
2012-08-05 22:52:55 +04:00
* _count = 1 ;
2009-05-14 16:17:28 +04:00
2012-08-05 22:52:55 +04:00
state - > hdr . done = true ;
return 0 ;
2009-05-14 16:17:28 +04:00
}
/*
2012-08-05 22:52:55 +04:00
* Now we analyze the NBT header
2009-05-14 16:17:28 +04:00
*/
2012-08-05 22:52:55 +04:00
state - > pktlen = smb2_len ( state - > hdr . nbt ) ;
2009-05-14 16:17:28 +04:00
2012-08-05 22:52:55 +04:00
if ( state - > pktlen = = 0 ) {
/* if there're no remaining bytes, we're done */
* _vector = NULL ;
* _count = 0 ;
return 0 ;
2009-05-14 16:17:28 +04:00
}
2012-08-05 22:52:55 +04:00
state - > pktbuf = talloc_array ( state - > smb2_req , uint8_t , state - > pktlen ) ;
if ( state - > pktbuf = = NULL ) {
2009-05-14 16:17:28 +04:00
return - 1 ;
}
vector = talloc_array ( mem_ctx , struct iovec , 1 ) ;
if ( vector = = NULL ) {
return - 1 ;
}
2012-08-05 22:52:55 +04:00
vector [ 0 ] . iov_base = ( void * ) state - > pktbuf ;
vector [ 0 ] . iov_len = state - > pktlen ;
2009-05-14 16:17:28 +04:00
* _vector = vector ;
* _count = 1 ;
return 0 ;
}
static void smbd_smb2_request_read_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
2012-08-05 22:52:55 +04:00
struct smbd_smb2_request_read_state * state =
tevent_req_data ( req ,
struct smbd_smb2_request_read_state ) ;
2009-05-14 16:17:28 +04:00
int ret ;
int sys_errno ;
NTSTATUS status ;
2012-08-05 22:52:55 +04:00
NTTIME now ;
2009-05-14 16:17:28 +04:00
ret = tstream_readv_pdu_queue_recv ( subreq , & sys_errno ) ;
2012-08-05 22:52:55 +04:00
TALLOC_FREE ( subreq ) ;
2009-05-14 16:17:28 +04:00
if ( ret = = - 1 ) {
status = map_nt_error_from_unix ( sys_errno ) ;
tevent_req_nterror ( req , status ) ;
return ;
}
2012-08-05 22:52:55 +04:00
if ( state - > hdr . nbt [ 0 ] ! = 0x00 ) {
DEBUG ( 1 , ( " smbd_smb2_request_read_done: ignore NBT[0x%02X] msg \n " ,
state - > hdr . nbt [ 0 ] ) ) ;
ZERO_STRUCT ( state - > hdr ) ;
TALLOC_FREE ( state - > pktbuf ) ;
state - > pktlen = 0 ;
subreq = tstream_readv_pdu_queue_send ( state - > smb2_req ,
state - > ev ,
state - > sconn - > smb2 . stream ,
state - > sconn - > smb2 . recv_queue ,
smbd_smb2_request_next_vector ,
state ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , smbd_smb2_request_read_done , req ) ;
return ;
}
state - > smb2_req - > request_time = timeval_current ( ) ;
now = timeval_to_nttime ( & state - > smb2_req - > request_time ) ;
status = smbd_smb2_inbuf_parse_compound ( state - > smb2_req - > sconn - > conn ,
now ,
state - > pktbuf ,
state - > pktlen ,
state - > smb2_req ,
& state - > smb2_req - > in . vector ,
& state - > smb2_req - > in . vector_count ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
state - > smb2_req - > current_idx = 1 ;
2009-05-14 16:17:28 +04:00
tevent_req_done ( req ) ;
}
static NTSTATUS smbd_smb2_request_read_recv ( struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
struct smbd_smb2_request * * _smb2_req )
{
struct smbd_smb2_request_read_state * state =
tevent_req_data ( req ,
struct smbd_smb2_request_read_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
2012-07-23 20:22:59 +04:00
* _smb2_req = talloc_move ( mem_ctx , & state - > smb2_req ) ;
2009-05-14 16:17:28 +04:00
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
static void smbd_smb2_request_incoming ( struct tevent_req * subreq ) ;
2011-08-05 22:34:43 +04:00
static NTSTATUS smbd_smb2_request_next_incoming ( struct smbd_server_connection * sconn )
{
size_t max_send_queue_len ;
size_t cur_send_queue_len ;
struct tevent_req * subreq ;
2011-08-27 01:23:26 +04:00
if ( sconn - > smb2 . compound_related_in_progress ) {
/*
* Can ' t read another until the related
* compound is done .
*/
return NT_STATUS_OK ;
}
2011-08-05 22:34:43 +04:00
if ( tevent_queue_length ( sconn - > smb2 . recv_queue ) > 0 ) {
/*
* if there is already a smbd_smb2_request_read
* pending , we are done .
*/
return NT_STATUS_OK ;
}
max_send_queue_len = MAX ( 1 , sconn - > smb2 . max_credits / 16 ) ;
cur_send_queue_len = tevent_queue_length ( sconn - > smb2 . send_queue ) ;
if ( cur_send_queue_len > max_send_queue_len ) {
/*
* if we have a lot of requests to send ,
* we wait until they are on the wire until we
* ask for the next request .
*/
return NT_STATUS_OK ;
}
/* ask for the next request */
2011-12-12 17:15:03 +04:00
subreq = smbd_smb2_request_read_send ( sconn , sconn - > ev_ctx , sconn ) ;
2011-08-05 22:34:43 +04:00
if ( subreq = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
tevent_req_set_callback ( subreq , smbd_smb2_request_incoming , sconn ) ;
return NT_STATUS_OK ;
}
2009-08-07 17:21:07 +04:00
void smbd_smb2_first_negprot ( struct smbd_server_connection * sconn ,
2012-08-05 22:46:35 +04:00
uint8_t * inbuf , size_t size )
2009-05-14 16:17:28 +04:00
{
NTSTATUS status ;
2010-08-30 17:56:16 +04:00
struct smbd_smb2_request * req = NULL ;
2009-05-14 16:17:28 +04:00
DEBUG ( 10 , ( " smbd_smb2_first_negprot: packet length %u \n " ,
( unsigned int ) size ) ) ;
2009-08-07 17:21:07 +04:00
status = smbd_initialize_smb2 ( sconn ) ;
2009-05-14 16:17:28 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2009-08-07 17:21:07 +04:00
status = smbd_smb2_request_create ( sconn , inbuf , size , & req ) ;
2009-05-14 16:17:28 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2012-06-26 01:14:24 +04:00
status = smbd_smb2_request_validate ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
return ;
}
2010-12-14 00:17:49 +03:00
status = smbd_smb2_request_setup_out ( req ) ;
2009-05-14 16:17:28 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
status = smbd_smb2_request_dispatch ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2011-08-05 22:34:43 +04:00
status = smbd_smb2_request_next_incoming ( sconn ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2011-07-08 01:59:41 +04:00
sconn - > num_requests + + ;
2009-05-14 16:17:28 +04:00
}
static void smbd_smb2_request_incoming ( struct tevent_req * subreq )
{
2009-08-07 17:21:07 +04:00
struct smbd_server_connection * sconn = tevent_req_callback_data ( subreq ,
struct smbd_server_connection ) ;
2009-05-14 16:17:28 +04:00
NTSTATUS status ;
2009-07-24 18:21:07 +04:00
struct smbd_smb2_request * req = NULL ;
2009-05-14 16:17:28 +04:00
2009-08-07 17:21:07 +04:00
status = smbd_smb2_request_read_recv ( subreq , sconn , & req ) ;
2009-05-14 16:17:28 +04:00
TALLOC_FREE ( subreq ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-04-18 08:20:17 +04:00
DEBUG ( 2 , ( " smbd_smb2_request_incoming: client read error %s \n " ,
nt_errstr ( status ) ) ) ;
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
DEBUG ( 10 , ( " smbd_smb2_request_incoming: idx[%d] of %d vectors \n " ,
req - > current_idx , req - > in . vector_count ) ) ;
2010-12-11 02:46:41 +03:00
status = smbd_smb2_request_validate ( req ) ;
2009-06-08 15:30:32 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-06-08 15:30:32 +04:00
return ;
}
2010-12-14 00:17:49 +03:00
status = smbd_smb2_request_setup_out ( req ) ;
2009-05-14 16:17:28 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
status = smbd_smb2_request_dispatch ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2011-08-05 22:34:43 +04:00
status = smbd_smb2_request_next_incoming ( sconn ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2011-07-08 01:59:41 +04:00
sconn - > num_requests + + ;
/* The timeout_processing function isn't run nearly
often enough to implement ' max log size ' without
overrunning the size of the file by many megabytes .
This is especially true if we are running at debug
level 10. Checking every 50 SMB2s is a nice
tradeoff of performance vs log file size overrun . */
if ( ( sconn - > num_requests % 50 ) = = 0 & &
need_to_check_log_size ( ) ) {
change_to_root_user ( ) ;
check_log_size ( ) ;
}
2009-05-14 16:17:28 +04:00
}