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
2013-10-14 12:33:57 +04:00
static void smbd_smb2_connection_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags ,
void * private_data ) ;
2014-06-11 12:00:09 +04:00
static NTSTATUS smbd_smb2_flush_send_queue ( struct smbXsrv_connection * xconn ) ;
2013-10-14 12:33:57 +04:00
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 ;
}
2014-06-11 12:45:56 +04:00
static NTSTATUS smbd_initialize_smb2 ( struct smbXsrv_connection * xconn )
2009-05-14 16:17:28 +04:00
{
2014-05-23 10:45:01 +04:00
TALLOC_FREE ( xconn - > transport . fde ) ;
2009-05-14 16:17:28 +04:00
2014-05-23 14:10:23 +04:00
xconn - > smb2 . credits . seq_low = 0 ;
xconn - > smb2 . credits . seq_range = 1 ;
xconn - > smb2 . credits . granted = 1 ;
xconn - > smb2 . credits . max = lp_smb2_max_credits ( ) ;
xconn - > smb2 . credits . bitmap = bitmap_talloc ( xconn ,
xconn - > smb2 . credits . max ) ;
if ( xconn - > smb2 . credits . bitmap = = NULL ) {
2010-12-11 02:46:41 +03:00
return NT_STATUS_NO_MEMORY ;
}
2009-05-15 13:20:34 +04:00
2014-06-10 22:20:58 +04:00
xconn - > transport . fde = tevent_add_fd ( xconn - > ev_ctx ,
xconn ,
2014-05-22 01:23:34 +04:00
xconn - > transport . sock ,
2013-10-14 12:33:57 +04:00
TEVENT_FD_READ ,
smbd_smb2_connection_handler ,
2014-06-10 22:20:58 +04:00
xconn ) ;
2014-05-23 11:36:24 +04:00
if ( xconn - > transport . fde = = NULL ) {
2013-10-14 12:33:57 +04:00
return NT_STATUS_NO_MEMORY ;
2009-05-14 16:17:28 +04:00
}
/* Ensure child is set to non-blocking mode */
2014-05-22 01:23:34 +04:00
set_blocking ( xconn - > transport . 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 )
{
2013-11-20 12:56:48 +04:00
if ( req - > first_key . length > 0 ) {
data_blob_clear_free ( & req - > first_key ) ;
}
if ( req - > last_key . length > 0 ) {
data_blob_clear_free ( & req - > last_key ) ;
}
2012-08-07 16:24:28 +04:00
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
2013-10-16 11:15:12 +04:00
mem_pool = talloc_tos ( ) ;
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 ) ;
2013-10-16 11:15:12 +04:00
#if 0
2012-07-23 20:22:59 +04:00
TALLOC_FREE ( mem_pool ) ;
2013-10-16 11:15:12 +04:00
# endif
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 ;
}
2014-07-07 14:25:48 +04:00
static NTSTATUS smbd_smb2_inbuf_parse_compound ( struct smbXsrv_connection * xconn ,
2012-08-05 21:39:39 +04:00
NTTIME now ,
uint8_t * buf ,
size_t buflen ,
2013-11-19 10:42:57 +04:00
struct smbd_smb2_request * req ,
2012-08-05 21:39:39 +04:00
struct iovec * * piov ,
int * pnum_iov )
{
2013-11-19 10:42:57 +04:00
TALLOC_CTX * mem_ctx = req ;
2012-08-05 21:39:39 +04:00
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
*/
2013-11-19 10:42:57 +04:00
iov = req - > in . _vector ;
2012-08-05 21:39:39 +04:00
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 ;
2014-05-23 14:21:57 +04:00
struct iovec * iov_alloc = NULL ;
if ( iov ! = req - > in . _vector ) {
iov_alloc = iov ;
}
2012-08-05 21:39:39 +04:00
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 ;
2014-07-07 14:25:48 +04:00
if ( xconn - > protocol < PROTOCOL_SMB2_24 ) {
2012-08-08 09:07:03 +04:00
DEBUG ( 10 , ( " Got SMB2_TRANSFORM header, "
" but dialect[0x%04X] is used \n " ,
2014-07-07 14:25:48 +04:00
xconn - > smb2 . server . dialect ) ) ;
2012-08-08 09:07:03 +04:00
goto inval ;
}
2014-07-07 14:25:48 +04:00
if ( ! ( xconn - > smb2 . server . capabilities & SMB2_CAP_ENCRYPTION ) ) {
2012-08-08 09:07:03 +04:00
DEBUG ( 10 , ( " Got SMB2_TRANSFORM header, "
" but not negotiated "
" client[0x%08X] server[0x%08X] \n " ,
2014-07-07 14:25:48 +04:00
xconn - > smb2 . client . capabilities ,
xconn - > smb2 . server . capabilities ) ) ;
2012-08-08 09:07:03 +04:00
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 ;
}
2014-07-07 14:25:48 +04:00
status = smb2srv_session_lookup ( xconn , uid , now , & s ) ;
2012-08-08 09:07:03 +04:00
if ( s = = NULL ) {
DEBUG ( 1 , ( " invalid session[%llu] in "
" SMB2_TRANSFORM header \n " ,
( unsigned long long ) uid ) ) ;
2014-05-23 14:21:57 +04:00
TALLOC_FREE ( iov_alloc ) ;
2012-08-08 09:07:03 +04:00
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 ,
2014-07-07 14:25:48 +04:00
xconn - > protocol ,
2012-08-08 09:07:03 +04:00
tf_iov , 2 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2014-05-23 14:21:57 +04:00
TALLOC_FREE ( iov_alloc ) ;
2012-08-08 09:07:03 +04:00
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
2013-11-19 10:42:57 +04:00
if ( num_iov > = ARRAY_SIZE ( req - > in . _vector ) ) {
struct iovec * iov_tmp = NULL ;
iov_tmp = talloc_realloc ( mem_ctx , iov_alloc ,
struct iovec ,
num_iov +
SMBD_SMB2_NUM_IOV_PER_REQ ) ;
if ( iov_tmp = = NULL ) {
TALLOC_FREE ( iov_alloc ) ;
return NT_STATUS_NO_MEMORY ;
}
if ( iov_alloc = = NULL ) {
memcpy ( iov_tmp ,
req - > in . _vector ,
sizeof ( req - > in . _vector ) ) ;
}
iov = iov_tmp ;
2012-08-05 21:39:39 +04:00
}
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 :
2013-11-19 10:42:57 +04:00
if ( iov ! = req - > in . _vector ) {
TALLOC_FREE ( iov ) ;
}
2012-08-05 21:39:39 +04:00
return NT_STATUS_INVALID_PARAMETER ;
}
2014-06-11 12:51:04 +04:00
static NTSTATUS smbd_smb2_request_create ( struct smbXsrv_connection * xconn ,
2014-06-24 21:28:51 +04:00
const uint8_t * _inpdu , size_t size ,
2009-05-14 16:17:28 +04:00
struct smbd_smb2_request * * _req )
{
2014-06-11 12:51:04 +04:00
struct smbd_server_connection * sconn = xconn - > sconn ;
2009-05-14 16:17:28 +04:00
struct smbd_smb2_request * req ;
uint32_t protocol_version ;
2014-06-24 21:28:51 +04:00
uint8_t * inpdu = NULL ;
2009-05-14 16:17:28 +04:00
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
2014-06-24 21:28:51 +04:00
if ( size < ( SMB2_HDR_BODY + 2 ) ) {
2009-05-14 16:17:28 +04:00
DEBUG ( 0 , ( " Invalid SMB2 packet length count %ld \n " , ( long ) size ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
2014-06-24 21:28:51 +04:00
inhdr = _inpdu ;
2009-05-14 16:17:28 +04:00
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 ;
}
2014-06-10 15:34:55 +04:00
req = smbd_smb2_request_allocate ( xconn ) ;
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 ;
2014-06-10 15:34:55 +04:00
req - > xconn = xconn ;
2009-05-14 16:17:28 +04:00
2014-06-24 21:28:51 +04:00
inpdu = talloc_memdup ( req , _inpdu , size ) ;
if ( inpdu = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2009-05-14 16:17:28 +04:00
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
2014-06-11 12:51:04 +04:00
status = smbd_smb2_inbuf_parse_compound ( xconn ,
2012-08-05 22:48:51 +04:00
now ,
2014-06-24 21:28:51 +04:00
inpdu ,
size ,
2012-08-05 22:48:51 +04:00
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 ;
}
2014-06-11 12:03:27 +04:00
static bool smb2_validate_sequence_number ( struct smbXsrv_connection * xconn ,
2012-06-26 01:17:55 +04:00
uint64_t message_id , uint64_t seq_id )
{
2014-05-23 14:10:23 +04:00
struct bitmap * credits_bm = xconn - > smb2 . credits . bitmap ;
2012-06-26 01:17:55 +04:00
unsigned int offset ;
2014-05-23 14:10:23 +04:00
uint64_t seq_tmp ;
2012-06-26 01:17:55 +04:00
2014-05-23 14:10:23 +04:00
seq_tmp = xconn - > smb2 . credits . seq_low ;
if ( seq_id < seq_tmp ) {
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 ,
2014-05-23 14:10:23 +04:00
( unsigned int ) xconn - > smb2 . credits . granted ,
( unsigned long long ) xconn - > smb2 . credits . seq_low ,
( unsigned int ) xconn - > smb2 . credits . seq_range ) ) ;
2012-06-26 01:17:55 +04:00
return false ;
}
2014-05-23 14:10:23 +04:00
seq_tmp + = xconn - > smb2 . credits . seq_range ;
if ( seq_id > = seq_tmp ) {
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 ,
2014-05-23 14:10:23 +04:00
( unsigned int ) xconn - > smb2 . credits . granted ,
( unsigned long long ) xconn - > smb2 . credits . seq_low ,
( unsigned int ) xconn - > smb2 . credits . seq_range ) ) ;
2012-06-26 01:17:55 +04:00
return false ;
}
2014-05-23 14:10:23 +04:00
offset = seq_id % xconn - > smb2 . credits . max ;
2012-06-26 01:17:55 +04:00
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 ,
2014-05-23 14:10:23 +04:00
( unsigned int ) xconn - > smb2 . credits . granted ,
( unsigned long long ) xconn - > smb2 . credits . seq_low ,
( unsigned int ) xconn - > smb2 . credits . seq_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 ) ;
2014-05-23 14:10:23 +04:00
if ( seq_id ! = xconn - > smb2 . credits . seq_low ) {
2012-06-26 01:17:55 +04:00
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 " ,
2014-05-23 14:10:23 +04:00
( unsigned long long ) ( xconn - > smb2 . credits . seq_low ) ,
2012-06-26 01:17:55 +04:00
offset ) ) ;
bitmap_clear ( credits_bm , offset ) ;
2014-05-23 14:10:23 +04:00
xconn - > smb2 . credits . seq_low + = 1 ;
xconn - > smb2 . credits . seq_range - = 1 ;
offset = xconn - > smb2 . credits . seq_low % xconn - > smb2 . credits . max ;
2012-06-26 01:17:55 +04:00
}
return true ;
}
2014-06-11 12:05:43 +04:00
static bool smb2_validate_message_id ( struct smbXsrv_connection * xconn ,
const uint8_t * inhdr )
2010-12-11 02:46:41 +03:00
{
uint64_t message_id = BVAL ( inhdr , SMB2_HDR_MESSAGE_ID ) ;
2013-04-02 07:11:26 +04:00
uint16_t opcode = SVAL ( 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 ;
}
2014-05-23 14:10:23 +04:00
if ( xconn - > smb2 . credits . 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 ,
2014-05-23 14:10:23 +04:00
( unsigned long long ) xconn - > smb2 . credits . granted ,
( unsigned long long ) xconn - > smb2 . credits . seq_low ,
( unsigned long long ) xconn - > smb2 . credits . seq_range ) ) ;
2012-02-28 05:53:04 +04:00
2014-05-23 14:10:23 +04:00
if ( xconn - > smb2 . credits . granted < credit_charge ) {
2012-06-26 16:28:07 +04:00
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 ,
2014-05-23 14:10:23 +04:00
( unsigned long long ) xconn - > smb2 . credits . granted ,
( unsigned long long ) xconn - > smb2 . credits . seq_low ,
( unsigned long long ) xconn - > smb2 . credits . seq_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
2014-06-11 12:03:27 +04:00
ok = smb2_validate_sequence_number ( xconn , message_id , id ) ;
2012-06-26 01:17:55 +04:00
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 */
2014-05-23 14:10:23 +04:00
xconn - > smb2 . credits . granted - = credit_charge ;
2012-06-26 01:17:55 +04:00
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 ;
}
2014-06-11 12:05:43 +04:00
if ( ! smb2_validate_message_id ( req - > xconn , inhdr ) ) {
2010-12-11 02:46:41 +03:00
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 ;
}
2014-06-11 12:08:26 +04:00
static void smb2_set_operation_credit ( struct smbXsrv_connection * xconn ,
const struct iovec * in_vector ,
struct iovec * out_vector )
2010-12-11 02:46:41 +03:00
{
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 .
*/
2014-05-23 14:10:23 +04:00
current_max_credits = xconn - > smb2 . credits . max / 16 ;
2012-06-27 17:33:43 +04:00
current_max_credits = MAX ( current_max_credits , 1 ) ;
2014-05-23 14:10:23 +04:00
if ( xconn - > smb2 . credits . multicredit ) {
2012-06-25 23:45:54 +04:00
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
2014-05-23 14:10:23 +04:00
SMB_ASSERT ( xconn - > smb2 . credits . max > = xconn - > smb2 . credits . granted ) ;
2012-10-03 04:30:54 +04:00
2014-05-23 14:10:23 +04:00
if ( xconn - > smb2 . credits . max < credit_charge ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn ,
2012-10-03 04:30:54 +04:00
" 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 ;
2014-05-23 14:10:23 +04:00
} else if ( xconn - > smb2 . credits . granted = = 0 ) {
2012-06-27 17:33:43 +04:00
/*
* 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 .
*/
2014-05-23 14:10:23 +04:00
credits_possible = UINT64_MAX - xconn - > smb2 . credits . seq_low ;
2012-06-27 17:33:43 +04:00
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 ) ;
2014-05-23 14:10:23 +04:00
credits_possible - = xconn - > smb2 . credits . seq_range ;
2012-06-27 17:33:43 +04:00
credits_granted = MIN ( credits_granted , credits_possible ) ;
2010-12-11 02:46:41 +03:00
SSVAL ( outhdr , SMB2_HDR_CREDIT , credits_granted ) ;
2014-05-23 14:10:23 +04:00
xconn - > smb2 . credits . granted + = credits_granted ;
xconn - > smb2 . credits . seq_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 ,
2014-05-23 14:10:23 +04:00
( unsigned int ) xconn - > smb2 . credits . granted ,
( unsigned int ) xconn - > smb2 . credits . max ,
( unsigned long long ) xconn - > smb2 . credits . seq_low ,
( unsigned int ) xconn - > smb2 . credits . seq_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 ;
2014-06-11 12:08:26 +04:00
smb2_set_operation_credit ( outreq - > xconn , inhdr_v , outhdr_v ) ;
2012-08-07 14:31:36 +04:00
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
}
}
2013-12-04 15:52:21 +04:00
DATA_BLOB smbd_smb2_generate_outbody ( struct smbd_smb2_request * req , size_t size )
{
2013-12-04 18:05:34 +04:00
if ( req - > current_idx < = 1 ) {
if ( size < = sizeof ( req - > out . _body ) ) {
return data_blob_const ( req - > out . _body , size ) ;
}
}
2013-12-04 18:28:11 +04:00
return data_blob_talloc ( req , NULL , size ) ;
2013-12-04 15:52:21 +04: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
{
2014-06-11 13:19:24 +04:00
struct smbXsrv_connection * xconn = req - > xconn ;
2013-12-04 18:32:45 +04:00
TALLOC_CTX * mem_ctx ;
2009-05-14 16:17:28 +04:00
struct iovec * vector ;
int count ;
int idx ;
count = req - > in . vector_count ;
2013-12-04 18:32:45 +04:00
if ( count < = ARRAY_SIZE ( req - > out . _vector ) ) {
mem_ctx = req ;
vector = req - > out . _vector ;
} else {
vector = talloc_zero_array ( req , struct iovec , count ) ;
if ( vector = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
mem_ctx = vector ;
2009-05-14 16:17:28 +04:00
}
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
}
2013-11-20 12:56:19 +04:00
if ( idx = = 1 ) {
outhdr = req - > out . _hdr ;
} else {
2013-12-04 18:32:45 +04:00
outhdr = talloc_zero_array ( mem_ctx , uint8_t ,
2013-11-20 12:56:19 +04:00
OUTVEC_ALLOC_SIZE ) ;
if ( outhdr = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2009-05-14 16:17:28 +04:00
}
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 ) ;
2014-06-10 15:24:50 +04:00
DLIST_ADD_END ( xconn - > 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 ;
}
2014-06-11 14:15:48 +04:00
void smbd_server_connection_terminate_ex ( struct smbXsrv_connection * xconn ,
2009-06-02 18:07:08 +04:00
const char * reason ,
const char * location )
2009-05-14 16:17:28 +04:00
{
2013-05-23 07:05:17 +04:00
DEBUG ( 10 , ( " smbd_server_connection_terminate_ex: reason[%s] at %s \n " ,
2009-06-02 18:07:08 +04:00
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 ;
2014-06-10 15:34:55 +04:00
newreq = smbd_smb2_request_allocate ( req - > xconn ) ;
2010-04-18 08:20:17 +04:00
if ( ! newreq ) {
return NULL ;
}
newreq - > sconn = req - > sconn ;
2014-06-10 15:34:55 +04:00
newreq - > xconn = req - > xconn ;
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 ;
}
2010-04-18 09:42:23 +04:00
static NTSTATUS smb2_send_async_interim_response ( const struct smbd_smb2_request * req )
{
2014-06-11 12:00:09 +04:00
struct smbXsrv_connection * xconn = req - > xconn ;
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 ,
2014-07-07 14:25:48 +04:00
xconn - > protocol ,
2012-08-08 09:07:03 +04:00
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 ,
2014-07-07 14:25:48 +04:00
xconn - > 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
2013-10-14 12:33:57 +04:00
nreq - > queue_entry . mem_ctx = nreq ;
nreq - > queue_entry . vector = nreq - > out . vector ;
nreq - > queue_entry . count = nreq - > out . vector_count ;
2014-07-07 14:25:48 +04:00
DLIST_ADD_END ( xconn - > smb2 . send_queue , & nreq - > queue_entry , NULL ) ;
xconn - > smb2 . send_queue_len + + ;
2010-04-18 09:42:23 +04:00
2014-06-11 12:00:09 +04:00
status = smbd_smb2_flush_send_queue ( xconn ) ;
2013-10-14 12:33:57 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2010-04-18 09:42:23 +04:00
}
return NT_STATUS_OK ;
}
struct smbd_smb2_request_pending_state {
2013-10-14 12:33:57 +04:00
struct smbd_smb2_send_queue queue_entry ;
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
} ;
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 ;
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 ) ) {
2013-10-12 04:40:12 +04:00
/*
* This is a performance optimization ,
* it avoids one tevent_loop iteration ,
* which means we avoid one
* talloc_stackframe_pool / talloc_free pair .
*/
tevent_req_notify_callback ( subreq ) ;
2009-08-15 12:01:38 +04:00
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 ;
}
2013-05-03 00:08:16 +04:00
if ( req - > in . vector_count > req - > current_idx + SMBD_SMB2_NUM_IOV_PER_REQ ) {
2010-04-18 08:20:17 +04:00
/*
* We ' re trying to go async in a compound
2013-05-03 00:55:53 +04:00
* request chain .
* This is only allowed for opens that
* cause an oplock break , otherwise it
* is not allowed . See [ MS - SMB2 ] . pdf
* note < 194 > on Section 3.3 .5 .2 .7 .
2010-04-18 08:20:17 +04:00
*/
2013-05-03 00:55:53 +04:00
const uint8_t * inhdr = SMBD_SMB2_IN_HDR_PTR ( req ) ;
if ( SVAL ( inhdr , SMB2_HDR_OPCODE ) ! = SMB2_OP_CREATE ) {
/*
* Cancel the outstanding request .
*/
bool ok = tevent_req_cancel ( req - > subreq ) ;
if ( ok ) {
return NT_STATUS_OK ;
}
TALLOC_FREE ( req - > subreq ) ;
return smbd_smb2_request_error ( req ,
NT_STATUS_INTERNAL_ERROR ) ;
2012-09-20 18:04:01 +04:00
}
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 ) ;
}
2013-05-03 00:55:53 +04:00
if ( req - > current_idx > 1 ) {
2012-08-07 14:47:44 +04:00
/*
2013-05-03 00:55:53 +04:00
* We ' re going async in a compound
* chain after the first request has
* already been processed . Send an
* interim response containing the
* set of replies already generated .
2010-04-18 09:42:23 +04:00
*/
2013-05-03 00:55:53 +04:00
int idx = req - > current_idx ;
2010-04-18 09:42:23 +04:00
status = smb2_send_async_interim_response ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2013-11-20 12:56:48 +04:00
if ( req - > first_key . length > 0 ) {
data_blob_clear_free ( & req - > first_key ) ;
}
2011-08-27 01:23:26 +04:00
2012-08-07 15:00:50 +04:00
req - > current_idx = 1 ;
2013-05-03 00:55:53 +04:00
/*
* Re - arrange the in . vectors to remove what
* we just sent .
*/
memmove ( & req - > in . vector [ 1 ] ,
& req - > in . vector [ idx ] ,
sizeof ( req - > in . vector [ 0 ] ) * ( req - > in . vector_count - idx ) ) ;
req - > in . vector_count = 1 + ( req - > in . vector_count - idx ) ;
/* Re-arrange the out.vectors to match. */
memmove ( & req - > out . vector [ 1 ] ,
& req - > out . vector [ idx ] ,
sizeof ( req - > out . vector [ 0 ] ) * ( req - > out . vector_count - idx ) ) ;
req - > out . vector_count = 1 + ( req - > out . vector_count - idx ) ;
if ( req - > in . vector_count = = 1 + SMBD_SMB2_NUM_IOV_PER_REQ ) {
/*
* We only have one remaining request as
* we ' ve processed everything else .
* This is no longer a compound request .
*/
req - > compound_related = false ;
outhdr = SMBD_SMB2_OUT_HDR_PTR ( req ) ;
flags = ( IVAL ( outhdr , SMB2_HDR_FLAGS ) & ~ SMB2_HDR_FLAG_CHAINED ) ;
SIVAL ( outhdr , SMB2_HDR_FLAGS , flags ) ;
}
2010-04-18 08:20:17 +04:00
}
2013-11-20 12:56:48 +04:00
if ( req - > last_key . length > 0 ) {
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 ) ;
2014-06-11 13:22:16 +04:00
struct smbXsrv_connection * xconn = req - > xconn ;
2011-11-09 14:47:33 +04:00
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 ;
2013-10-14 12:33:57 +04:00
NTSTATUS status ;
2011-11-09 14:47:33 +04:00
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 " ,
2013-04-02 07:11:26 +04:00
smb2_opcode_name ( SVAL ( inhdr , SMB2_HDR_OPCODE ) ) ,
2011-11-09 14:47:33 +04:00
( 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 .
*/
2014-06-11 13:22:16 +04:00
state = talloc_zero ( req - > xconn , struct smbd_smb2_request_pending_state ) ;
2010-04-18 09:42:23 +04:00
if ( state = = NULL ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn ,
2011-11-09 14:47:33 +04:00
nt_errstr ( NT_STATUS_NO_MEMORY ) ) ;
return ;
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 . */
2014-06-11 12:08:26 +04:00
smb2_set_operation_credit ( req - > xconn ,
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 ) {
struct smbXsrv_session * x = req - > session ;
DATA_BLOB encryption_key = x - > global - > encryption_key ;
status = smb2_signing_encrypt_pdu ( encryption_key ,
2014-05-23 11:53:21 +04:00
xconn - > protocol ,
2012-08-08 09:07:03 +04:00
& state - > vector [ 1 + SMBD_SMB2_TF_IOV_OFS ] ,
SMBD_SMB2_NUM_IOV_PER_REQ ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn ,
2012-08-08 09:07:03 +04:00
nt_errstr ( status ) ) ;
return ;
}
} else if ( req - > do_signing ) {
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 ;
2011-11-09 14:47:33 +04:00
2012-03-16 18:01:27 +04:00
status = smb2_signing_sign_pdu ( signing_key ,
2014-05-23 11:53:21 +04:00
xconn - > protocol ,
2012-08-08 08:40:51 +04:00
& 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 ) ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn ,
2011-11-09 14:47:33 +04:00
nt_errstr ( status ) ) ;
return ;
2010-04-18 08:20:17 +04:00
}
}
2009-06-10 00:33:32 +04:00
2013-10-14 12:33:57 +04:00
state - > queue_entry . mem_ctx = state ;
state - > queue_entry . vector = state - > vector ;
state - > queue_entry . count = ARRAY_SIZE ( state - > vector ) ;
2014-05-23 11:53:21 +04:00
DLIST_ADD_END ( xconn - > smb2 . send_queue , & state - > queue_entry , NULL ) ;
xconn - > smb2 . send_queue_len + + ;
2013-10-14 12:33:57 +04:00
2014-06-11 12:00:09 +04:00
status = smbd_smb2_flush_send_queue ( xconn ) ;
2013-10-14 12:33:57 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn ,
2013-10-14 12:33:57 +04:00
nt_errstr ( status ) ) ;
2011-11-09 14:47:33 +04:00
return ;
2009-06-10 00:33:32 +04:00
}
}
static NTSTATUS smbd_smb2_request_process_cancel ( struct smbd_smb2_request * req )
{
2014-06-11 13:29:01 +04:00
struct smbXsrv_connection * xconn = req - > xconn ;
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
*/
2014-06-10 15:24:50 +04:00
DLIST_REMOVE ( xconn - > smb2 . requests , req ) ;
2009-06-10 00:33:32 +04:00
TALLOC_FREE ( req ) ;
2014-06-10 15:24:50 +04:00
for ( cur = xconn - > smb2 . requests ; cur ; cur = cur - > next ) {
2009-06-10 00:33:32 +04:00
const uint8_t * outhdr ;
uint64_t message_id ;
uint64_t async_id ;
2013-05-02 23:34:54 +04:00
if ( cur - > compound_related ) {
/*
* Never cancel anything in a compound request .
* Way too hard to deal with the result .
*/
continue ;
}
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 " ,
2013-04-02 07:11:26 +04:00
smb2_opcode_name ( SVAL ( inhdr , SMB2_HDR_OPCODE ) ) ,
2010-04-18 08:20:17 +04:00
( 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 ) ;
2013-04-02 07:11:26 +04:00
in_opcode = SVAL ( 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 */
2014-06-11 13:30:23 +04:00
status = smb2srv_session_lookup ( req - > xconn ,
2012-03-16 18:01:27 +04:00
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
}
2014-06-11 13:30:23 +04:00
if ( in_session_id ! = req - > xconn - > last_session_id ) {
req - > xconn - > last_session_id = in_session_id ;
2013-11-19 08:21: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 )
{
2014-06-11 13:31:04 +04:00
struct smbXsrv_connection * xconn = req - > xconn ;
2012-02-28 05:49:12 +04:00
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
2014-05-23 14:10:23 +04:00
if ( xconn - > smb2 . credits . multicredit ) {
2012-06-26 01:35:46 +04:00
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 ;
2013-11-18 16:46:10 +04:00
case SMB2_OP_WRITE :
if ( req - > smb1req ! = NULL & & req - > smb1req - > unread_bytes > 0 ) {
if ( req - > smb1req - > unread_bytes < min_dyn_size ) {
return NT_STATUS_INVALID_PARAMETER ;
}
min_dyn_size = 0 ;
}
break ;
2011-09-14 15:04:28 +04:00
}
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
{
2014-06-11 13:31:37 +04:00
struct smbXsrv_connection * xconn = req - > xconn ;
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 ) ;
2013-04-02 07:11:26 +04:00
opcode = SVAL ( 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
2014-07-07 14:25:48 +04:00
if ( xconn - > 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 & &
2014-06-12 17:10:11 +04:00
x - > global - > signing_key . length > 0 ) {
2012-05-14 16:24:08 +04:00
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 ;
2013-09-23 22:51:30 +04:00
} else if ( signing_required | | ( flags & SMB2_HDR_FLAG_SIGNED ) ) {
2012-06-29 15:30:44 +04:00
DATA_BLOB signing_key ;
if ( x = = NULL ) {
2014-06-17 01:05:18 +04:00
/*
* MS - SMB2 : 3.3 .5 .2 .4 Verifying the Signature .
* If the SMB2 header of the SMB2 NEGOTIATE
* request has the SMB2_FLAGS_SIGNED bit set in the
* Flags field , the server MUST fail the request
* with STATUS_INVALID_PARAMETER .
*
* Microsoft test tool checks this .
*/
if ( ( opcode = = SMB2_OP_NEGPROT ) & &
( flags & SMB2_HDR_FLAG_SIGNED ) ) {
status = NT_STATUS_INVALID_PARAMETER ;
} else {
status = NT_STATUS_USER_SESSION_DELETED ;
}
return smbd_smb2_request_error ( req , status ) ;
2012-06-29 15:30:44 +04:00
}
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 ,
2014-07-07 14:25:48 +04:00
xconn - > 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
}
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 )
{
2014-06-11 12:00:09 +04:00
struct smbXsrv_connection * xconn = req - > xconn ;
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 ) ;
2013-10-14 12:33:57 +04:00
NTSTATUS status ;
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 ;
}
2013-12-04 18:28:11 +04:00
tf = talloc_zero_array ( req , uint8_t ,
2012-08-08 09:07:03 +04:00
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 ) ;
/*
* 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 ,
2014-07-07 14:25:48 +04:00
xconn - > protocol ,
2012-08-08 06:35:15 +04:00
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
}
2013-11-20 12:56:48 +04:00
if ( req - > last_key . length > 0 ) {
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
}
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 ) {
status = smb2_signing_encrypt_pdu ( req - > first_key ,
2014-07-07 14:25:48 +04:00
xconn - > protocol ,
2012-08-08 09:07:03 +04:00
firsttf ,
req - > out . vector_count - first_idx ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
} else if ( req - > do_signing ) {
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 ,
2014-07-07 14:25:48 +04:00
xconn - > 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 ;
}
}
2013-11-20 12:56:48 +04:00
if ( req - > first_key . length > 0 ) {
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 ;
}
2010-06-03 03:43:31 +04:00
/*
* We ' re done with this request -
* move it off the " being processed " queue .
*/
2014-07-07 14:25:48 +04:00
DLIST_REMOVE ( xconn - > smb2 . requests , req ) ;
2009-05-14 16:17:28 +04:00
2013-10-14 12:33:57 +04:00
req - > queue_entry . mem_ctx = req ;
req - > queue_entry . vector = req - > out . vector ;
req - > queue_entry . count = req - > out . vector_count ;
2014-07-07 14:25:48 +04:00
DLIST_ADD_END ( xconn - > smb2 . send_queue , & req - > queue_entry , NULL ) ;
xconn - > smb2 . send_queue_len + + ;
2013-10-14 12:33:57 +04:00
2014-06-11 12:00:09 +04:00
status = smbd_smb2_flush_send_queue ( xconn ) ;
2013-10-14 12:33:57 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2009-05-14 16:17:28 +04:00
return NT_STATUS_OK ;
}
2014-06-11 12:59:00 +04:00
static NTSTATUS smbd_smb2_request_next_incoming ( struct smbXsrv_connection * xconn ) ;
2011-08-27 01:23:26 +04:00
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 ) ;
2014-06-11 12:59:00 +04:00
struct smbXsrv_connection * xconn = req - > xconn ;
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 ) ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn , nt_errstr ( status ) ) ;
2009-05-20 21:48:47 +04:00
return ;
}
2014-06-11 12:59:00 +04:00
status = smbd_smb2_request_next_incoming ( xconn ) ;
2011-08-27 01:23:26 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn , nt_errstr ( status ) ) ;
2011-08-27 01:23:26 +04:00
return ;
}
}
2011-08-05 22:34:43 +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 ;
2013-12-04 18:28:11 +04:00
pad = talloc_zero_array ( req ,
2009-06-09 21:21:26 +04:00
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 ;
2013-12-04 18:28:11 +04:00
new_dyn = talloc_zero_array ( req ,
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 )
{
2014-06-11 13:40:04 +04:00
struct smbXsrv_connection * xconn = req - > xconn ;
2010-04-20 01:32:08 +04:00
DATA_BLOB body ;
2013-12-04 18:24:29 +04:00
DATA_BLOB _dyn ;
2012-08-05 17:00:23 +04:00
uint8_t * outhdr = SMBD_SMB2_OUT_HDR_PTR ( req ) ;
2013-03-19 23:36:52 +04:00
size_t unread_bytes = smbd_smb2_unread_bytes ( 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 ) ) ;
2013-03-19 23:36:52 +04:00
if ( unread_bytes ) {
/* Recvfile error. Drain incoming socket. */
2013-10-14 18:42:55 +04:00
size_t ret ;
errno = 0 ;
2014-05-22 01:23:34 +04:00
ret = drain_socket ( xconn - > transport . sock , unread_bytes ) ;
2013-03-19 23:36:52 +04:00
if ( ret ! = unread_bytes ) {
2013-10-14 18:42:55 +04:00
NTSTATUS error ;
if ( errno = = 0 ) {
error = NT_STATUS_IO_DEVICE_ERROR ;
} else {
error = map_nt_error_from_unix_common ( errno ) ;
}
DEBUG ( 2 , ( " Failed to drain %u bytes from SMB2 socket: "
" ret[%u] errno[%d] => %s \n " ,
( unsigned ) unread_bytes ,
( unsigned ) ret , errno , nt_errstr ( error ) ) ) ;
return error ;
2013-03-19 23:36:52 +04:00
}
}
2010-04-20 01:32:08 +04:00
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 .
*/
2013-12-04 18:24:29 +04:00
info = & _dyn ;
2010-04-20 01:32:08 +04:00
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__ ) ;
}
2014-07-07 14:13:56 +04:00
struct smbd_smb2_send_break_state {
2013-10-14 12:33:57 +04:00
struct smbd_smb2_send_queue queue_entry ;
2014-07-07 13:35:40 +04:00
uint8_t nbt_hdr [ NBT_HDR_SIZE ] ;
2014-07-07 13:35:40 +04:00
uint8_t tf [ SMB2_TF_HDR_SIZE ] ;
2014-07-07 13:35:40 +04:00
uint8_t hdr [ SMB2_HDR_BODY ] ;
2012-08-22 12:33:07 +04:00
struct iovec vector [ 1 + SMBD_SMB2_NUM_IOV_PER_REQ ] ;
2014-07-07 14:13:56 +04:00
uint8_t body [ 1 ] ;
2009-06-09 22:44:13 +04:00
} ;
2014-06-12 10:43:26 +04:00
static NTSTATUS smbd_smb2_send_break ( struct smbXsrv_connection * xconn ,
2012-08-22 12:33:07 +04:00
struct smbXsrv_session * session ,
struct smbXsrv_tcon * tcon ,
2014-07-07 14:13:56 +04:00
const uint8_t * body ,
size_t body_len )
2009-06-09 22:44:13 +04:00
{
2014-07-07 14:13:56 +04:00
struct smbd_smb2_send_break_state * state ;
2012-08-22 12:33:07 +04:00
bool do_encryption = session - > global - > encryption_required ;
uint64_t nonce_high = 0 ;
uint64_t nonce_low = 0 ;
2013-10-14 12:33:57 +04:00
NTSTATUS status ;
2014-07-07 14:13:56 +04:00
size_t statelen ;
2012-08-22 12:33:07 +04:00
if ( tcon - > global - > encryption_required ) {
do_encryption = true ;
}
2009-06-09 22:44:13 +04:00
2014-07-07 14:13:56 +04:00
statelen = offsetof ( struct smbd_smb2_send_break_state , body ) +
body_len ;
2014-06-12 10:43:26 +04:00
state = talloc_zero_size ( xconn , statelen ) ;
2009-06-09 22:44:13 +04:00
if ( state = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2014-07-07 14:13:56 +04:00
talloc_set_name_const ( state , " struct smbd_smb2_send_break_state " ) ;
2009-06-09 22:44:13 +04:00
2012-08-22 12:33:07 +04:00
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 ;
}
}
2014-07-07 13:35:40 +04:00
SIVAL ( state - > tf , SMB2_TF_PROTOCOL_ID , SMB2_TF_MAGIC ) ;
SBVAL ( state - > tf , SMB2_TF_NONCE + 0 , nonce_low ) ;
SBVAL ( state - > tf , SMB2_TF_NONCE + 8 , nonce_high ) ;
SBVAL ( state - > tf , SMB2_TF_SESSION_ID , session - > global - > session_wire_id ) ;
2009-06-09 22:44:13 +04:00
2014-07-07 13:35:40 +04:00
SIVAL ( state - > hdr , 0 , SMB2_MAGIC ) ;
SSVAL ( state - > hdr , SMB2_HDR_LENGTH , SMB2_HDR_BODY ) ;
SSVAL ( state - > hdr , SMB2_HDR_EPOCH , 0 ) ;
SIVAL ( state - > hdr , SMB2_HDR_STATUS , 0 ) ;
SSVAL ( state - > hdr , SMB2_HDR_OPCODE , SMB2_OP_BREAK ) ;
SSVAL ( state - > hdr , SMB2_HDR_CREDIT , 0 ) ;
SIVAL ( state - > hdr , SMB2_HDR_FLAGS , SMB2_HDR_FLAG_REDIRECT ) ;
SIVAL ( state - > hdr , SMB2_HDR_NEXT_COMMAND , 0 ) ;
SBVAL ( state - > hdr , SMB2_HDR_MESSAGE_ID , UINT64_MAX ) ;
SIVAL ( state - > hdr , SMB2_HDR_PID , 0 ) ;
SIVAL ( state - > hdr , SMB2_HDR_TID , 0 ) ;
SBVAL ( state - > hdr , SMB2_HDR_SESSION_ID , 0 ) ;
memset ( state - > hdr + SMB2_HDR_SIGNATURE , 0 , 16 ) ;
2009-06-09 22:44:13 +04:00
2014-07-07 13:35:40 +04:00
state - > vector [ 0 ] = ( struct iovec ) {
. iov_base = state - > nbt_hdr ,
. iov_len = sizeof ( state - > nbt_hdr )
} ;
2012-08-22 12:33:07 +04:00
if ( do_encryption ) {
2014-07-07 13:35:40 +04:00
state - > vector [ 1 + SMBD_SMB2_TF_IOV_OFS ] = ( struct iovec ) {
. iov_base = state - > tf ,
. iov_len = sizeof ( state - > tf )
} ;
2012-08-22 12:33:07 +04:00
} else {
2014-07-07 13:35:40 +04:00
state - > vector [ 1 + SMBD_SMB2_TF_IOV_OFS ] = ( struct iovec ) {
. iov_base = NULL ,
. iov_len = 0
} ;
2012-08-22 12:33:07 +04:00
}
2014-07-07 13:35:40 +04:00
state - > vector [ 1 + SMBD_SMB2_HDR_IOV_OFS ] = ( struct iovec ) {
. iov_base = state - > hdr ,
. iov_len = sizeof ( state - > hdr )
} ;
2012-08-22 12:33:07 +04:00
2014-07-07 14:13:56 +04:00
memcpy ( state - > body , body , body_len ) ;
state - > vector [ 1 + SMBD_SMB2_BODY_IOV_OFS ] = ( struct iovec ) {
. iov_base = state - > body ,
. iov_len = body_len /* no sizeof(state->body) .. :-) */
} ;
2012-08-22 12:33:07 +04:00
2014-07-07 13:54:12 +04:00
/*
* state - > vector [ 1 + SMBD_SMB2_DYN_IOV_OFS ] is NULL by talloc_zero above
*/
2012-08-22 12:33:07 +04:00
smb2_setup_nbt_length ( state - > vector , 1 + SMBD_SMB2_NUM_IOV_PER_REQ ) ;
if ( do_encryption ) {
DATA_BLOB encryption_key = session - > global - > encryption_key ;
status = smb2_signing_encrypt_pdu ( encryption_key ,
2014-07-07 14:25:48 +04:00
xconn - > protocol ,
2012-08-22 12:33:07 +04:00
& 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
2013-10-14 12:33:57 +04:00
state - > queue_entry . mem_ctx = state ;
state - > queue_entry . vector = state - > vector ;
state - > queue_entry . count = ARRAY_SIZE ( state - > vector ) ;
2014-07-07 14:25:48 +04:00
DLIST_ADD_END ( xconn - > smb2 . send_queue , & state - > queue_entry , NULL ) ;
xconn - > smb2 . send_queue_len + + ;
2009-06-09 22:44:13 +04:00
2014-06-11 12:00:09 +04:00
status = smbd_smb2_flush_send_queue ( xconn ) ;
2013-10-14 12:33:57 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2009-06-09 22:44:13 +04:00
}
2013-10-14 12:33:57 +04:00
return NT_STATUS_OK ;
2009-06-09 22:44:13 +04:00
}
2014-07-07 14:13:56 +04:00
NTSTATUS smbd_smb2_send_oplock_break ( struct smbd_server_connection * sconn ,
struct smbXsrv_session * session ,
struct smbXsrv_tcon * tcon ,
struct smbXsrv_open * op ,
uint8_t oplock_level )
{
2014-06-12 10:43:26 +04:00
struct smbXsrv_connection * xconn = sconn - > conn ;
2014-07-07 14:13:56 +04:00
uint8_t body [ 0x18 ] ;
SSVAL ( body , 0x00 , sizeof ( body ) ) ;
SCVAL ( body , 0x02 , oplock_level ) ;
SCVAL ( body , 0x03 , 0 ) ; /* reserved */
SIVAL ( body , 0x04 , 0 ) ; /* reserved */
SBVAL ( body , 0x08 , op - > global - > open_persistent_id ) ;
SBVAL ( body , 0x10 , op - > global - > open_volatile_id ) ;
2014-06-12 10:43:26 +04:00
return smbd_smb2_send_break ( xconn , session , tcon , body , sizeof ( body ) ) ;
2014-07-07 14:13:56 +04:00
}
2013-04-02 00:19:01 +04:00
static bool is_smb2_recvfile_write ( struct smbd_smb2_request_read_state * state )
{
2014-04-11 02:43:46 +04:00
NTSTATUS status ;
2013-04-01 22:16:01 +04:00
uint32_t flags ;
2014-04-11 02:43:46 +04:00
uint64_t file_id_persistent ;
uint64_t file_id_volatile ;
struct smbXsrv_open * op = NULL ;
struct files_struct * fsp = NULL ;
const uint8_t * body = NULL ;
/*
* This is only called with a pktbuf
* of at least SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN
* bytes
*/
2013-04-01 22:16:01 +04:00
if ( IVAL ( state - > pktbuf , 0 ) = = SMB2_TF_MAGIC ) {
/* Transform header. Cannot recvfile. */
return false ;
}
if ( IVAL ( state - > pktbuf , 0 ) ! = SMB2_MAGIC ) {
/* Not SMB2. Normal error path will cope. */
return false ;
}
if ( SVAL ( state - > pktbuf , 4 ) ! = SMB2_HDR_BODY ) {
/* Not SMB2. Normal error path will cope. */
return false ;
}
if ( SVAL ( state - > pktbuf , SMB2_HDR_OPCODE ) ! = SMB2_OP_WRITE ) {
/* Needs to be a WRITE. */
return false ;
}
if ( IVAL ( state - > pktbuf , SMB2_HDR_NEXT_COMMAND ) ! = 0 ) {
/* Chained. Cannot recvfile. */
return false ;
}
flags = IVAL ( state - > pktbuf , SMB2_HDR_FLAGS ) ;
if ( flags & SMB2_HDR_FLAG_CHAINED ) {
/* Chained. Cannot recvfile. */
return false ;
}
if ( flags & SMB2_HDR_FLAG_SIGNED ) {
/* Signed. Cannot recvfile. */
return false ;
}
2014-04-11 02:43:46 +04:00
body = & state - > pktbuf [ SMB2_HDR_BODY ] ;
file_id_persistent = BVAL ( body , 0x10 ) ;
file_id_volatile = BVAL ( body , 0x18 ) ;
2014-06-11 13:41:27 +04:00
status = smb2srv_open_lookup ( state - > req - > xconn ,
2014-04-11 02:43:46 +04:00
file_id_persistent ,
file_id_volatile ,
0 , /* now */
& op ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return false ;
}
fsp = op - > compat ;
if ( fsp = = NULL ) {
return false ;
}
if ( fsp - > conn = = NULL ) {
return false ;
}
if ( IS_IPC ( fsp - > conn ) ) {
return false ;
}
if ( IS_PRINT ( fsp - > conn ) ) {
return false ;
}
2013-04-01 22:16:01 +04:00
DEBUG ( 10 , ( " Doing recvfile write len = %u \n " ,
2013-11-15 12:12:40 +04:00
( unsigned int ) ( state - > pktfull - state - > pktlen ) ) ) ;
2013-04-01 22:16:01 +04:00
return true ;
2013-04-02 00:19:01 +04:00
}
2014-06-11 12:59:00 +04:00
static NTSTATUS smbd_smb2_request_next_incoming ( struct smbXsrv_connection * xconn )
2011-08-05 22:34:43 +04:00
{
2014-06-11 12:59:00 +04:00
struct smbd_server_connection * sconn = xconn - > sconn ;
2014-05-23 11:41:57 +04:00
struct smbd_smb2_request_read_state * state = & xconn - > smb2 . request_read_state ;
2011-08-05 22:34:43 +04:00
size_t max_send_queue_len ;
size_t cur_send_queue_len ;
2014-05-23 11:07:47 +04:00
if ( ! NT_STATUS_IS_OK ( xconn - > transport . status ) ) {
2014-02-17 15:01:12 +04:00
/*
* we ' re not supposed to do any io
*/
return NT_STATUS_OK ;
}
2013-10-14 12:33:57 +04:00
if ( state - > req ! = NULL ) {
2011-08-05 22:34:43 +04:00
/*
2013-10-14 12:33:57 +04:00
* if there is already a tstream_readv_pdu
2011-08-05 22:34:43 +04:00
* pending , we are done .
*/
return NT_STATUS_OK ;
}
2014-05-23 14:10:23 +04:00
max_send_queue_len = MAX ( 1 , xconn - > smb2 . credits . max / 16 ) ;
2014-05-23 11:53:21 +04:00
cur_send_queue_len = xconn - > smb2 . send_queue_len ;
2011-08-05 22:34:43 +04:00
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 */
2013-10-14 12:33:57 +04:00
ZERO_STRUCTP ( state ) ;
2014-06-10 15:34:55 +04:00
state - > req = smbd_smb2_request_allocate ( xconn ) ;
2013-10-14 12:33:57 +04:00
if ( state - > req = = NULL ) {
2011-08-05 22:34:43 +04:00
return NT_STATUS_NO_MEMORY ;
}
2013-10-14 12:33:57 +04:00
state - > req - > sconn = sconn ;
2014-06-10 15:34:55 +04:00
state - > req - > xconn = xconn ;
2014-04-11 03:37:42 +04:00
state - > min_recv_size = lp_min_receive_file_size ( ) ;
2013-10-14 12:33:57 +04:00
2014-05-23 11:36:24 +04:00
TEVENT_FD_READABLE ( xconn - > transport . fde ) ;
2011-08-05 22:34:43 +04:00
return NT_STATUS_OK ;
}
2014-06-11 12:42:01 +04:00
void smbd_smb2_first_negprot ( struct smbXsrv_connection * xconn ,
2014-06-24 21:28:51 +04:00
const uint8_t * inpdu , size_t size )
2009-05-14 16:17:28 +04:00
{
2014-06-11 12:42:01 +04:00
struct smbd_server_connection * sconn = xconn - > client - > sconn ;
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 ) ) ;
2014-06-11 12:45:56 +04:00
status = smbd_initialize_smb2 ( xconn ) ;
2009-05-14 16:17:28 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2014-06-24 21:28:51 +04:00
status = smbd_smb2_request_create ( xconn , inpdu , size , & req ) ;
2009-05-14 16:17:28 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn , 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 ) ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn , nt_errstr ( status ) ) ;
2012-06-26 01:14:24 +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 ) ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
status = smbd_smb2_request_dispatch ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2014-06-11 12:59:00 +04:00
status = smbd_smb2_request_next_incoming ( xconn ) ;
2011-08-05 22:34:43 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn , 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
}
2013-10-14 12:33:57 +04:00
static int socket_error_from_errno ( int ret ,
int sys_errno ,
bool * retry )
2009-05-14 16:17:28 +04:00
{
2013-10-14 12:33:57 +04:00
* retry = false ;
if ( ret > = 0 ) {
return 0 ;
}
if ( ret ! = - 1 ) {
return EIO ;
}
if ( sys_errno = = 0 ) {
return EIO ;
}
if ( sys_errno = = EINTR ) {
* retry = true ;
return sys_errno ;
}
if ( sys_errno = = EINPROGRESS ) {
* retry = true ;
return sys_errno ;
}
if ( sys_errno = = EAGAIN ) {
* retry = true ;
return sys_errno ;
}
/* ENOMEM is retryable on Solaris/illumos, and possibly other systems. */
if ( sys_errno = = ENOMEM ) {
* retry = true ;
return sys_errno ;
}
# ifdef EWOULDBLOCK
2013-12-16 00:18:07 +04:00
# if EWOULDBLOCK != EAGAIN
2013-10-14 12:33:57 +04:00
if ( sys_errno = = EWOULDBLOCK ) {
* retry = true ;
return sys_errno ;
}
2013-12-16 00:18:07 +04:00
# endif
2013-10-14 12:33:57 +04:00
# endif
return sys_errno ;
}
2014-06-11 12:00:09 +04:00
static NTSTATUS smbd_smb2_flush_send_queue ( struct smbXsrv_connection * xconn )
2013-10-14 12:33:57 +04:00
{
int ret ;
int err ;
bool retry ;
2014-05-23 11:53:21 +04:00
if ( xconn - > smb2 . send_queue = = NULL ) {
2014-05-23 11:36:24 +04:00
TEVENT_FD_NOT_WRITEABLE ( xconn - > transport . fde ) ;
2013-10-14 12:33:57 +04:00
return NT_STATUS_OK ;
}
2014-05-23 11:53:21 +04:00
while ( xconn - > smb2 . send_queue ! = NULL ) {
struct smbd_smb2_send_queue * e = xconn - > smb2 . send_queue ;
2013-10-14 12:33:57 +04:00
2013-10-14 16:18:26 +04:00
if ( e - > sendfile_header ! = NULL ) {
2014-07-11 04:25:00 +04:00
NTSTATUS status = NT_STATUS_INTERNAL_ERROR ;
2013-10-14 16:18:26 +04:00
size_t size = 0 ;
size_t i = 0 ;
uint8_t * buf ;
for ( i = 0 ; i < e - > count ; i + + ) {
size + = e - > vector [ i ] . iov_len ;
}
2013-12-04 15:32:36 +04:00
if ( size < = e - > sendfile_header - > length ) {
buf = e - > sendfile_header - > data ;
} else {
buf = talloc_array ( e - > mem_ctx , uint8_t , size ) ;
if ( buf = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2013-10-14 16:18:26 +04:00
}
size = 0 ;
for ( i = 0 ; i < e - > count ; i + + ) {
memcpy ( buf + size ,
e - > vector [ i ] . iov_base ,
e - > vector [ i ] . iov_len ) ;
size + = e - > vector [ i ] . iov_len ;
}
e - > sendfile_header - > data = buf ;
e - > sendfile_header - > length = size ;
2014-07-11 04:25:00 +04:00
e - > sendfile_status = & status ;
2013-10-14 16:18:26 +04:00
e - > count = 0 ;
2014-05-23 11:53:21 +04:00
xconn - > smb2 . send_queue_len - - ;
DLIST_REMOVE ( xconn - > smb2 . send_queue , e ) ;
2013-12-04 15:32:36 +04:00
/*
* This triggers the sendfile path via
* the destructor .
*/
2013-10-14 16:18:26 +04:00
talloc_free ( e - > mem_ctx ) ;
2014-07-11 04:25:00 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2013-10-14 16:18:26 +04:00
continue ;
}
2014-05-22 01:23:34 +04:00
ret = writev ( xconn - > transport . sock , e - > vector , e - > count ) ;
2013-10-14 12:33:57 +04:00
if ( ret = = 0 ) {
/* propagate end of file */
return NT_STATUS_INTERNAL_ERROR ;
}
err = socket_error_from_errno ( ret , errno , & retry ) ;
if ( retry ) {
/* retry later */
2014-05-23 11:36:24 +04:00
TEVENT_FD_WRITEABLE ( xconn - > transport . fde ) ;
2013-10-14 12:33:57 +04:00
return NT_STATUS_OK ;
}
if ( err ! = 0 ) {
return map_nt_error_from_unix_common ( err ) ;
}
while ( ret > 0 ) {
if ( ret < e - > vector [ 0 ] . iov_len ) {
uint8_t * base ;
base = ( uint8_t * ) e - > vector [ 0 ] . iov_base ;
base + = ret ;
e - > vector [ 0 ] . iov_base = ( void * ) base ;
e - > vector [ 0 ] . iov_len - = ret ;
break ;
}
ret - = e - > vector [ 0 ] . iov_len ;
e - > vector + = 1 ;
e - > count - = 1 ;
}
/*
* there ' re maybe some empty vectors at the end
* which we need to skip , otherwise we would get
* ret = = 0 from the readv ( ) call and return EPIPE
*/
while ( e - > count > 0 ) {
if ( e - > vector [ 0 ] . iov_len > 0 ) {
break ;
}
e - > vector + = 1 ;
e - > count - = 1 ;
}
if ( e - > count > 0 ) {
/* we have more to write */
2014-05-23 11:36:24 +04:00
TEVENT_FD_WRITEABLE ( xconn - > transport . fde ) ;
2013-10-14 12:33:57 +04:00
return NT_STATUS_OK ;
}
2014-05-23 11:53:21 +04:00
xconn - > smb2 . send_queue_len - - ;
DLIST_REMOVE ( xconn - > smb2 . send_queue , e ) ;
2013-10-14 12:33:57 +04:00
talloc_free ( e - > mem_ctx ) ;
}
return NT_STATUS_OK ;
}
2014-06-10 22:20:58 +04:00
static NTSTATUS smbd_smb2_io_handler ( struct smbXsrv_connection * xconn ,
2013-10-14 12:33:57 +04:00
uint16_t fde_flags )
{
2014-06-10 22:20:58 +04:00
struct smbd_server_connection * sconn = xconn - > client - > sconn ;
2014-05-23 11:41:57 +04:00
struct smbd_smb2_request_read_state * state = & xconn - > smb2 . request_read_state ;
2009-07-24 18:21:07 +04:00
struct smbd_smb2_request * req = NULL ;
2013-10-14 12:33:57 +04:00
size_t min_recvfile_size = UINT32_MAX ;
int ret ;
int err ;
bool retry ;
NTSTATUS status ;
NTTIME now ;
2014-05-23 11:07:47 +04:00
if ( ! NT_STATUS_IS_OK ( xconn - > transport . status ) ) {
2014-02-17 15:01:12 +04:00
/*
* we ' re not supposed to do any io
*/
2014-05-23 11:36:24 +04:00
TEVENT_FD_NOT_READABLE ( xconn - > transport . fde ) ;
TEVENT_FD_NOT_WRITEABLE ( xconn - > transport . fde ) ;
2014-02-17 15:01:12 +04:00
return NT_STATUS_OK ;
}
2013-10-14 12:33:57 +04:00
if ( fde_flags & TEVENT_FD_WRITE ) {
2014-06-11 12:00:09 +04:00
status = smbd_smb2_flush_send_queue ( xconn ) ;
2013-10-14 12:33:57 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
if ( ! ( fde_flags & TEVENT_FD_READ ) ) {
return NT_STATUS_OK ;
}
if ( state - > req = = NULL ) {
2014-05-23 11:36:24 +04:00
TEVENT_FD_NOT_READABLE ( xconn - > transport . fde ) ;
2013-10-14 12:33:57 +04:00
return NT_STATUS_OK ;
}
again :
if ( ! state - > hdr . done ) {
state - > hdr . done = true ;
state - > vector . iov_base = ( void * ) state - > hdr . nbt ;
state - > vector . iov_len = NBT_HDR_SIZE ;
}
2014-05-22 01:23:34 +04:00
ret = readv ( xconn - > transport . sock , & state - > vector , 1 ) ;
2013-10-14 12:33:57 +04:00
if ( ret = = 0 ) {
/* propagate end of file */
return NT_STATUS_END_OF_FILE ;
}
err = socket_error_from_errno ( ret , errno , & retry ) ;
if ( retry ) {
/* retry later */
2014-05-23 11:36:24 +04:00
TEVENT_FD_READABLE ( xconn - > transport . fde ) ;
2013-10-14 12:33:57 +04:00
return NT_STATUS_OK ;
}
if ( err ! = 0 ) {
return map_nt_error_from_unix_common ( err ) ;
}
if ( ret < state - > vector . iov_len ) {
uint8_t * base ;
base = ( uint8_t * ) state - > vector . iov_base ;
base + = ret ;
state - > vector . iov_base = ( void * ) base ;
state - > vector . iov_len - = ret ;
/* we have more to read */
2014-05-23 11:36:24 +04:00
TEVENT_FD_READABLE ( xconn - > transport . fde ) ;
2013-10-14 12:33:57 +04:00
return NT_STATUS_OK ;
}
if ( state - > pktlen > 0 ) {
if ( state - > doing_receivefile & & ! is_smb2_recvfile_write ( state ) ) {
/*
* Not a possible receivefile write .
* Read the rest of the data .
*/
state - > doing_receivefile = false ;
2013-11-15 12:12:40 +04:00
state - > pktbuf = talloc_realloc ( state - > req ,
state - > pktbuf ,
uint8_t ,
state - > pktfull ) ;
if ( state - > pktbuf = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2013-10-14 12:33:57 +04:00
state - > vector . iov_base = ( void * ) ( state - > pktbuf +
2013-11-15 12:12:40 +04:00
state - > pktlen ) ;
state - > vector . iov_len = ( state - > pktfull -
state - > pktlen ) ;
state - > pktlen = state - > pktfull ;
2013-10-14 12:33:57 +04:00
goto again ;
}
/*
* Either this is a receivefile write so we ' ve
* done a short read , or if not we have all the data .
*/
goto got_full ;
}
/*
* Now we analyze the NBT header
*/
2014-04-11 03:05:21 +04:00
if ( state - > hdr . nbt [ 0 ] ! = 0x00 ) {
state - > min_recv_size = 0 ;
}
2013-11-15 12:12:40 +04:00
state - > pktfull = smb2_len ( state - > hdr . nbt ) ;
if ( state - > pktfull = = 0 ) {
2013-10-14 12:33:57 +04:00
goto got_full ;
}
if ( state - > min_recv_size ! = 0 ) {
min_recvfile_size = SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN ;
min_recvfile_size + = state - > min_recv_size ;
}
2013-11-15 12:12:40 +04:00
if ( state - > pktfull > min_recvfile_size ) {
2013-10-14 12:33:57 +04:00
/*
* Might be a receivefile write . Read the SMB2 HEADER +
* SMB2_WRITE header first . Set ' doing_receivefile '
* as we ' re * attempting * receivefile write . If this
* turns out not to be a SMB2_WRITE request or otherwise
* not suitable then we ' ll just read the rest of the data
* the next time this function is called .
*/
2013-11-15 12:12:40 +04:00
state - > pktlen = SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN ;
2013-10-14 12:33:57 +04:00
state - > doing_receivefile = true ;
} else {
2013-11-15 12:12:40 +04:00
state - > pktlen = state - > pktfull ;
2013-10-14 12:33:57 +04:00
}
2013-11-15 12:12:40 +04:00
state - > pktbuf = talloc_array ( state - > req , uint8_t , state - > pktlen ) ;
if ( state - > pktbuf = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
state - > vector . iov_base = ( void * ) state - > pktbuf ;
state - > vector . iov_len = state - > pktlen ;
2013-10-14 12:33:57 +04:00
goto again ;
got_full :
if ( state - > hdr . nbt [ 0 ] ! = 0x00 ) {
DEBUG ( 1 , ( " ignore NBT[0x%02X] msg \n " ,
state - > hdr . nbt [ 0 ] ) ) ;
req = state - > req ;
ZERO_STRUCTP ( state ) ;
state - > req = req ;
2014-04-11 03:37:42 +04:00
state - > min_recv_size = lp_min_receive_file_size ( ) ;
2013-10-14 12:33:57 +04:00
req = NULL ;
goto again ;
}
req = state - > req ;
state - > req = NULL ;
req - > request_time = timeval_current ( ) ;
now = timeval_to_nttime ( & req - > request_time ) ;
2009-05-14 16:17:28 +04:00
2014-06-10 22:20:58 +04:00
status = smbd_smb2_inbuf_parse_compound ( xconn ,
2013-10-14 12:33:57 +04:00
now ,
state - > pktbuf ,
state - > pktlen ,
req ,
& req - > in . vector ,
& req - > in . vector_count ) ;
2009-05-14 16:17:28 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2013-10-14 12:33:57 +04:00
return status ;
}
if ( state - > doing_receivefile ) {
req - > smb1req = talloc_zero ( req , struct smb_request ) ;
if ( req - > smb1req = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2013-11-15 12:12:40 +04:00
req - > smb1req - > unread_bytes = state - > pktfull - state - > pktlen ;
2009-05-14 16:17:28 +04:00
}
2013-10-14 12:33:57 +04:00
ZERO_STRUCTP ( state ) ;
req - > current_idx = 1 ;
DEBUG ( 10 , ( " smbd_smb2_request idx[%d] of %d vectors \n " ,
2009-05-14 16:17:28 +04:00
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 ) ) {
2013-10-14 12:33:57 +04:00
return status ;
2009-06-08 15:30:32 +04:00
}
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 ) ) {
2013-10-14 12:33:57 +04:00
return status ;
2009-05-14 16:17:28 +04:00
}
status = smbd_smb2_request_dispatch ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2013-10-14 12:33:57 +04:00
return status ;
2009-05-14 16:17:28 +04:00
}
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 ( ) ;
}
2013-10-14 12:33:57 +04:00
2014-06-11 12:59:00 +04:00
status = smbd_smb2_request_next_incoming ( xconn ) ;
2013-10-14 12:33:57 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
return NT_STATUS_OK ;
}
static void smbd_smb2_connection_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags ,
void * private_data )
{
2014-06-10 22:20:58 +04:00
struct smbXsrv_connection * xconn =
2013-10-14 12:33:57 +04:00
talloc_get_type_abort ( private_data ,
2014-06-10 22:20:58 +04:00
struct smbXsrv_connection ) ;
2013-10-14 12:33:57 +04:00
NTSTATUS status ;
2014-06-10 22:20:58 +04:00
status = smbd_smb2_io_handler ( xconn , flags ) ;
2013-10-14 12:33:57 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2014-06-11 14:15:48 +04:00
smbd_server_connection_terminate ( xconn , nt_errstr ( status ) ) ;
2013-10-14 12:33:57 +04:00
return ;
}
2009-05-14 16:17:28 +04:00
}