2022-03-21 20:51:41 +03:00
/*
Unix SMB / CIFS implementation .
SMB transaction2 handling
Copyright ( C ) Jeremy Allison 1994 - 2007
Copyright ( C ) Stefan ( metze ) Metzmacher 2003
Copyright ( C ) Volker Lendecke 2005 - 2007
Copyright ( C ) Steve French 2005
Copyright ( C ) James Peach 2006 - 2007
Extensively modified by Andrew Tridgell , 1995
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"
# include "ntioctl.h"
# include "system/filesys.h"
# include "lib/util/time_basic.h"
# include "version.h"
# include "smbd/smbd.h"
# include "smbd/globals.h"
# include "../libcli/auth/libcli_auth.h"
# include "../librpc/gen_ndr/xattr.h"
# include "../librpc/gen_ndr/ndr_security.h"
# include "libcli/security/security.h"
# include "trans2.h"
# include "auth.h"
# include "smbprofile.h"
# include "rpc_server/srv_pipe_hnd.h"
# include "printing.h"
# include "lib/util_ea.h"
# include "lib/readdir_attr.h"
# include "messages.h"
# include "libcli/smb/smb2_posix.h"
# include "lib/util/string_wrappers.h"
# include "source3/lib/substitute.h"
# include "source3/lib/adouble.h"
2022-03-21 21:29:26 +03:00
# define DIR_ENTRY_SAFETY_MARGIN 4096
2022-03-21 20:51:41 +03:00
/****************************************************************************
Send the required number of replies back .
We assume all fields other than the data fields are
set correctly for the type of call .
HACK ! Always assumes smb_setup field is zero .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void send_trans2_replies ( connection_struct * conn ,
struct smb_request * req ,
NTSTATUS status ,
const char * params ,
int paramsize ,
const char * pdata ,
int datasize ,
int max_data_bytes )
{
/* As we are using a protocol > LANMAN1 then the max_send
variable must have been set in the sessetupX call .
This takes precedence over the max_xmit field in the
global struct . These different max_xmit variables should
be merged as this is now too confusing */
int data_to_send = datasize ;
int params_to_send = paramsize ;
int useable_space ;
const char * pp = params ;
const char * pd = pdata ;
int params_sent_thistime , data_sent_thistime , total_sent_thistime ;
int alignment_offset = 1 ; /* JRA. This used to be 3. Set to 1 to make netmon parse ok. */
int data_alignment_offset = 0 ;
bool overflow = False ;
struct smbXsrv_connection * xconn = req - > xconn ;
int max_send = xconn - > smb1 . sessions . max_send ;
/* Modify the data_to_send and datasize and set the error if
we ' re trying to send more than max_data_bytes . We still send
the part of the packet ( s ) that fit . Strange , but needed
for OS / 2. */
if ( max_data_bytes > 0 & & datasize > max_data_bytes ) {
DEBUG ( 5 , ( " send_trans2_replies: max_data_bytes %d exceeded by data %d \n " ,
max_data_bytes , datasize ) ) ;
datasize = data_to_send = max_data_bytes ;
overflow = True ;
}
/* If there genuinely are no parameters or data to send just send the empty packet */
if ( params_to_send = = 0 & & data_to_send = = 0 ) {
2022-04-05 05:53:20 +03:00
reply_smb1_outbuf ( req , 10 , 0 ) ;
2022-03-21 20:51:41 +03:00
if ( NT_STATUS_V ( status ) ) {
uint8_t eclass ;
uint32_t ecode ;
ntstatus_to_dos ( status , & eclass , & ecode ) ;
error_packet_set ( ( char * ) req - > outbuf ,
eclass , ecode , status ,
__LINE__ , __FILE__ ) ;
}
show_msg ( ( char * ) req - > outbuf ) ;
2022-03-31 22:09:13 +03:00
if ( ! smb1_srv_send ( xconn ,
2022-03-21 20:51:41 +03:00
( char * ) req - > outbuf ,
true , req - > seqnum + 1 ,
IS_CONN_ENCRYPTED ( conn ) ,
& req - > pcd ) ) {
2022-03-31 22:09:13 +03:00
exit_server_cleanly ( " send_trans2_replies: smb1_srv_send failed. " ) ;
2022-03-21 20:51:41 +03:00
}
TALLOC_FREE ( req - > outbuf ) ;
return ;
}
/* When sending params and data ensure that both are nicely aligned */
/* Only do this alignment when there is also data to send - else
can cause NT redirector problems . */
if ( ( ( params_to_send % 4 ) ! = 0 ) & & ( data_to_send ! = 0 ) )
data_alignment_offset = 4 - ( params_to_send % 4 ) ;
/* Space is bufsize minus Netbios over TCP header minus SMB header */
/* The alignment_offset is to align the param bytes on an even byte
boundary . NT 4.0 Beta needs this to work correctly . */
useable_space = max_send - ( smb_size
+ 2 * 10 /* wct */
+ alignment_offset
+ data_alignment_offset ) ;
if ( useable_space < 0 ) {
DEBUG ( 0 , ( " send_trans2_replies failed sanity useable_space "
" = %d!!! " , useable_space ) ) ;
exit_server_cleanly ( " send_trans2_replies: Not enough space " ) ;
}
while ( params_to_send | | data_to_send ) {
/* Calculate whether we will totally or partially fill this packet */
total_sent_thistime = params_to_send + data_to_send ;
/* We can never send more than useable_space */
/*
* Note that ' useable_space ' does not include the alignment offsets ,
* but we must include the alignment offsets in the calculation of
* the length of the data we send over the wire , as the alignment offsets
* are sent here . Fix from Marc_Jacobsen @ hp . com .
*/
total_sent_thistime = MIN ( total_sent_thistime , useable_space ) ;
2022-04-05 05:53:20 +03:00
reply_smb1_outbuf ( req , 10 , total_sent_thistime + alignment_offset
2022-03-21 20:51:41 +03:00
+ data_alignment_offset ) ;
/* Set total params and data to be sent */
SSVAL ( req - > outbuf , smb_tprcnt , paramsize ) ;
SSVAL ( req - > outbuf , smb_tdrcnt , datasize ) ;
/* Calculate how many parameters and data we can fit into
* this packet . Parameters get precedence
*/
params_sent_thistime = MIN ( params_to_send , useable_space ) ;
data_sent_thistime = useable_space - params_sent_thistime ;
data_sent_thistime = MIN ( data_sent_thistime , data_to_send ) ;
SSVAL ( req - > outbuf , smb_prcnt , params_sent_thistime ) ;
/* smb_proff is the offset from the start of the SMB header to the
parameter bytes , however the first 4 bytes of outbuf are
the Netbios over TCP header . Thus use smb_base ( ) to subtract
them from the calculation */
SSVAL ( req - > outbuf , smb_proff ,
( ( smb_buf ( req - > outbuf ) + alignment_offset )
- smb_base ( req - > outbuf ) ) ) ;
if ( params_sent_thistime = = 0 )
SSVAL ( req - > outbuf , smb_prdisp , 0 ) ;
else
/* Absolute displacement of param bytes sent in this packet */
SSVAL ( req - > outbuf , smb_prdisp , pp - params ) ;
SSVAL ( req - > outbuf , smb_drcnt , data_sent_thistime ) ;
if ( data_sent_thistime = = 0 ) {
SSVAL ( req - > outbuf , smb_droff , 0 ) ;
SSVAL ( req - > outbuf , smb_drdisp , 0 ) ;
} else {
/* The offset of the data bytes is the offset of the
parameter bytes plus the number of parameters being sent this time */
SSVAL ( req - > outbuf , smb_droff ,
( ( smb_buf ( req - > outbuf ) + alignment_offset )
- smb_base ( req - > outbuf ) )
+ params_sent_thistime + data_alignment_offset ) ;
SSVAL ( req - > outbuf , smb_drdisp , pd - pdata ) ;
}
/* Initialize the padding for alignment */
if ( alignment_offset ! = 0 ) {
memset ( smb_buf ( req - > outbuf ) , 0 , alignment_offset ) ;
}
/* Copy the param bytes into the packet */
if ( params_sent_thistime ) {
memcpy ( ( smb_buf ( req - > outbuf ) + alignment_offset ) , pp ,
params_sent_thistime ) ;
}
/* Copy in the data bytes */
if ( data_sent_thistime ) {
if ( data_alignment_offset ! = 0 ) {
memset ( ( smb_buf ( req - > outbuf ) + alignment_offset +
params_sent_thistime ) , 0 ,
data_alignment_offset ) ;
}
memcpy ( smb_buf ( req - > outbuf ) + alignment_offset
+ params_sent_thistime + data_alignment_offset ,
pd , data_sent_thistime ) ;
}
DEBUG ( 9 , ( " t2_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d \n " ,
params_sent_thistime , data_sent_thistime , useable_space ) ) ;
DEBUG ( 9 , ( " t2_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d \n " ,
params_to_send , data_to_send , paramsize , datasize ) ) ;
if ( overflow ) {
error_packet_set ( ( char * ) req - > outbuf ,
ERRDOS , ERRbufferoverflow ,
STATUS_BUFFER_OVERFLOW ,
__LINE__ , __FILE__ ) ;
} else if ( NT_STATUS_V ( status ) ) {
uint8_t eclass ;
uint32_t ecode ;
ntstatus_to_dos ( status , & eclass , & ecode ) ;
error_packet_set ( ( char * ) req - > outbuf ,
eclass , ecode , status ,
__LINE__ , __FILE__ ) ;
}
/* Send the packet */
show_msg ( ( char * ) req - > outbuf ) ;
2022-03-31 22:09:13 +03:00
if ( ! smb1_srv_send ( xconn ,
2022-03-21 20:51:41 +03:00
( char * ) req - > outbuf ,
true , req - > seqnum + 1 ,
IS_CONN_ENCRYPTED ( conn ) ,
& req - > pcd ) )
2022-03-31 22:09:13 +03:00
exit_server_cleanly ( " send_trans2_replies: smb1_srv_send failed. " ) ;
2022-03-21 20:51:41 +03:00
TALLOC_FREE ( req - > outbuf ) ;
pp + = params_sent_thistime ;
pd + = data_sent_thistime ;
params_to_send - = params_sent_thistime ;
data_to_send - = data_sent_thistime ;
/* Sanity check */
if ( params_to_send < 0 | | data_to_send < 0 ) {
DEBUG ( 0 , ( " send_trans2_replies failed sanity check pts = %d, dts = %d \n !!! " ,
params_to_send , data_to_send ) ) ;
return ;
}
}
return ;
}
2022-03-21 20:59:15 +03:00
/****************************************************************************
Deal with SMB_SET_POSIX_LOCK .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void smb_set_posix_lock_done ( struct tevent_req * subreq ) ;
NTSTATUS smb_set_posix_lock ( connection_struct * conn ,
struct smb_request * req ,
const char * pdata ,
int total_data ,
files_struct * fsp )
{
struct tevent_req * subreq = NULL ;
struct smbd_lock_element * lck = NULL ;
uint64_t count ;
uint64_t offset ;
uint64_t smblctx ;
bool blocking_lock = False ;
enum brl_type lock_type ;
NTSTATUS status = NT_STATUS_OK ;
if ( fsp = = NULL | |
fsp - > fsp_flags . is_pathref | |
fsp_get_io_fd ( fsp ) = = - 1 )
{
return NT_STATUS_INVALID_HANDLE ;
}
if ( total_data ! = POSIX_LOCK_DATA_SIZE ) {
return NT_STATUS_INVALID_PARAMETER ;
}
switch ( SVAL ( pdata , POSIX_LOCK_TYPE_OFFSET ) ) {
case POSIX_LOCK_TYPE_READ :
lock_type = READ_LOCK ;
break ;
case POSIX_LOCK_TYPE_WRITE :
/* Return the right POSIX-mappable error code for files opened read-only. */
if ( ! fsp - > fsp_flags . can_write ) {
return NT_STATUS_INVALID_HANDLE ;
}
lock_type = WRITE_LOCK ;
break ;
case POSIX_LOCK_TYPE_UNLOCK :
lock_type = UNLOCK_LOCK ;
break ;
default :
return NT_STATUS_INVALID_PARAMETER ;
}
switch ( SVAL ( pdata , POSIX_LOCK_FLAGS_OFFSET ) ) {
case POSIX_LOCK_FLAG_NOWAIT :
blocking_lock = false ;
break ;
case POSIX_LOCK_FLAG_WAIT :
blocking_lock = true ;
break ;
default :
return NT_STATUS_INVALID_PARAMETER ;
}
if ( ! lp_blocking_locks ( SNUM ( conn ) ) ) {
blocking_lock = False ;
}
smblctx = ( uint64_t ) IVAL ( pdata , POSIX_LOCK_PID_OFFSET ) ;
offset = ( ( ( uint64_t ) IVAL ( pdata , ( POSIX_LOCK_START_OFFSET + 4 ) ) ) < < 32 ) |
( ( uint64_t ) IVAL ( pdata , POSIX_LOCK_START_OFFSET ) ) ;
count = ( ( ( uint64_t ) IVAL ( pdata , ( POSIX_LOCK_LEN_OFFSET + 4 ) ) ) < < 32 ) |
( ( uint64_t ) IVAL ( pdata , POSIX_LOCK_LEN_OFFSET ) ) ;
DBG_DEBUG ( " file %s, lock_type = %u, smblctx = % " PRIu64 " , "
" count = % " PRIu64 " , offset = % " PRIu64 " \n " ,
fsp_str_dbg ( fsp ) ,
( unsigned int ) lock_type ,
smblctx ,
count ,
offset ) ;
if ( lock_type = = UNLOCK_LOCK ) {
struct smbd_lock_element l = {
. req_guid = smbd_request_guid ( req , 0 ) ,
. smblctx = smblctx ,
. brltype = UNLOCK_LOCK ,
. lock_flav = POSIX_LOCK ,
. offset = offset ,
. count = count ,
} ;
status = smbd_do_unlocking ( req , fsp , 1 , & l ) ;
return status ;
}
lck = talloc ( req , struct smbd_lock_element ) ;
if ( lck = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
* lck = ( struct smbd_lock_element ) {
. req_guid = smbd_request_guid ( req , 0 ) ,
. smblctx = smblctx ,
. brltype = lock_type ,
. lock_flav = POSIX_LOCK ,
. count = count ,
. offset = offset ,
} ;
subreq = smbd_smb1_do_locks_send (
fsp ,
req - > sconn - > ev_ctx ,
& req ,
fsp ,
blocking_lock ? UINT32_MAX : 0 ,
true , /* large_offset */
1 ,
lck ) ;
if ( subreq = = NULL ) {
TALLOC_FREE ( lck ) ;
return NT_STATUS_NO_MEMORY ;
}
tevent_req_set_callback ( subreq , smb_set_posix_lock_done , req ) ;
return NT_STATUS_EVENT_PENDING ;
}
static void smb_set_posix_lock_done ( struct tevent_req * subreq )
{
struct smb_request * req = NULL ;
NTSTATUS status ;
bool ok ;
ok = smbd_smb1_do_locks_extract_smbreq ( subreq , talloc_tos ( ) , & req ) ;
SMB_ASSERT ( ok ) ;
status = smbd_smb1_do_locks_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
char params [ 2 ] = { 0 } ;
/* Fake up max_data_bytes here - we know it fits. */
send_trans2_replies (
req - > conn ,
req ,
NT_STATUS_OK ,
params ,
2 ,
NULL ,
0 ,
0xffff ) ;
} else {
reply_nterror ( req , status ) ;
2022-03-31 22:09:13 +03:00
ok = smb1_srv_send (
2022-03-21 20:59:15 +03:00
req - > xconn ,
( char * ) req - > outbuf ,
true ,
req - > seqnum + 1 ,
IS_CONN_ENCRYPTED ( req - > conn ) ,
NULL ) ;
if ( ! ok ) {
exit_server_cleanly ( " smb_set_posix_lock_done: "
2022-03-31 22:09:13 +03:00
" smb1_srv_send failed. " ) ;
2022-03-21 20:59:15 +03:00
}
}
TALLOC_FREE ( req ) ;
return ;
}
2022-03-21 21:29:26 +03:00
/****************************************************************************
Read a list of EA names from an incoming data buffer . Create an ea_list with them .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static struct ea_list * read_ea_name_list ( TALLOC_CTX * ctx , const char * pdata , size_t data_size )
{
struct ea_list * ea_list_head = NULL ;
size_t converted_size , offset = 0 ;
while ( offset + 2 < data_size ) {
struct ea_list * eal = talloc_zero ( ctx , struct ea_list ) ;
unsigned int namelen = CVAL ( pdata , offset ) ;
offset + + ; /* Go past the namelen byte. */
/* integer wrap paranioa. */
if ( ( offset + namelen < offset ) | | ( offset + namelen < namelen ) | |
( offset > data_size ) | | ( namelen > data_size ) | |
( offset + namelen > = data_size ) ) {
break ;
}
/* Ensure the name is null terminated. */
if ( pdata [ offset + namelen ] ! = ' \0 ' ) {
return NULL ;
}
if ( ! pull_ascii_talloc ( ctx , & eal - > ea . name , & pdata [ offset ] ,
& converted_size ) ) {
DEBUG ( 0 , ( " read_ea_name_list: pull_ascii_talloc "
" failed: %s " , strerror ( errno ) ) ) ;
}
if ( ! eal - > ea . name ) {
return NULL ;
}
offset + = ( namelen + 1 ) ; /* Go past the name + terminating zero. */
DLIST_ADD_END ( ea_list_head , eal ) ;
DEBUG ( 10 , ( " read_ea_name_list: read ea name %s \n " , eal - > ea . name ) ) ;
}
return ea_list_head ;
}
/****************************************************************************
Reply to a TRANSACT2_OPEN .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void call_trans2open ( connection_struct * conn ,
struct smb_request * req ,
char * * pparams , int total_params ,
char * * ppdata , int total_data ,
unsigned int max_data_bytes )
{
struct smb_filename * smb_fname = NULL ;
char * params = * pparams ;
char * pdata = * ppdata ;
int deny_mode ;
int32_t open_attr ;
bool oplock_request ;
#if 0
bool return_additional_info ;
int16 open_sattr ;
time_t open_time ;
# endif
int open_ofun ;
uint32_t open_size ;
char * pname ;
char * fname = NULL ;
off_t size = 0 ;
int fattr = 0 , mtime = 0 ;
SMB_INO_T inode = 0 ;
int smb_action = 0 ;
2022-07-27 22:36:23 +03:00
struct files_struct * dirfsp = NULL ;
2022-03-21 21:29:26 +03:00
files_struct * fsp ;
struct ea_list * ea_list = NULL ;
uint16_t flags = 0 ;
NTSTATUS status ;
uint32_t access_mask ;
uint32_t share_mode ;
uint32_t create_disposition ;
uint32_t create_options = 0 ;
uint32_t private_flags = 0 ;
2022-07-27 22:36:23 +03:00
NTTIME twrp = 0 ;
2022-03-21 21:29:26 +03:00
uint32_t ucf_flags = ucf_flags_from_smb_request ( req ) ;
TALLOC_CTX * ctx = talloc_tos ( ) ;
/*
* Ensure we have enough parameters to perform the operation .
*/
if ( total_params < 29 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
goto out ;
}
flags = SVAL ( params , 0 ) ;
deny_mode = SVAL ( params , 2 ) ;
open_attr = SVAL ( params , 6 ) ;
oplock_request = ( flags & REQUEST_OPLOCK ) ? EXCLUSIVE_OPLOCK : 0 ;
if ( oplock_request ) {
oplock_request | = ( flags & REQUEST_BATCH_OPLOCK ) ? BATCH_OPLOCK : 0 ;
}
#if 0
return_additional_info = BITSETW ( params , 0 ) ;
open_sattr = SVAL ( params , 4 ) ;
open_time = make_unix_date3 ( params + 8 ) ;
# endif
open_ofun = SVAL ( params , 12 ) ;
open_size = IVAL ( params , 14 ) ;
pname = & params [ 28 ] ;
if ( IS_IPC ( conn ) ) {
reply_nterror ( req , NT_STATUS_NETWORK_ACCESS_DENIED ) ;
goto out ;
}
if ( req - > posix_pathnames ) {
srvstr_get_path_posix ( ctx ,
params ,
req - > flags2 ,
& fname ,
pname ,
total_params - 28 ,
STR_TERMINATE ,
& status ) ;
} else {
srvstr_get_path ( ctx ,
params ,
req - > flags2 ,
& fname ,
pname ,
total_params - 28 ,
STR_TERMINATE ,
& status ) ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
reply_nterror ( req , status ) ;
goto out ;
}
DEBUG ( 3 , ( " call_trans2open %s deny_mode=0x%x attr=%d ofun=0x%x size=%d \n " ,
fname , ( unsigned int ) deny_mode , ( unsigned int ) open_attr ,
( unsigned int ) open_ofun , open_size ) ) ;
2022-07-27 22:36:23 +03:00
if ( ucf_flags & UCF_GMT_PATHNAME ) {
2022-08-04 20:02:15 +03:00
extract_snapshot_token ( fname , & twrp ) ;
2022-07-27 22:36:23 +03:00
}
status = filename_convert_dirfsp ( ctx ,
conn ,
fname ,
ucf_flags ,
twrp ,
& dirfsp ,
& smb_fname ) ;
2022-03-21 21:29:26 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
if ( NT_STATUS_EQUAL ( status , NT_STATUS_PATH_NOT_COVERED ) ) {
reply_botherror ( req ,
NT_STATUS_PATH_NOT_COVERED ,
ERRSRV , ERRbadpath ) ;
goto out ;
}
reply_nterror ( req , status ) ;
goto out ;
}
if ( open_ofun = = 0 ) {
reply_nterror ( req , NT_STATUS_OBJECT_NAME_COLLISION ) ;
goto out ;
}
if ( ! map_open_params_to_ntcreate ( smb_fname - > base_name , deny_mode ,
open_ofun ,
& access_mask , & share_mode ,
& create_disposition ,
& create_options ,
& private_flags ) ) {
reply_nterror ( req , NT_STATUS_ACCESS_DENIED ) ;
goto out ;
}
/* Any data in this call is an EA list. */
if ( total_data & & ( total_data ! = 4 ) ) {
if ( total_data < 10 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
goto out ;
}
if ( IVAL ( pdata , 0 ) > total_data ) {
DEBUG ( 10 , ( " call_trans2open: bad total data size (%u) > %u \n " ,
IVAL ( pdata , 0 ) , ( unsigned int ) total_data ) ) ;
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
goto out ;
}
ea_list = read_ea_list ( talloc_tos ( ) , pdata + 4 ,
total_data - 4 ) ;
if ( ! ea_list ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
goto out ;
}
if ( ! lp_ea_support ( SNUM ( conn ) ) ) {
reply_nterror ( req , NT_STATUS_EAS_NOT_SUPPORTED ) ;
goto out ;
}
if ( ! req - > posix_pathnames & &
ea_list_has_invalid_name ( ea_list ) ) {
int param_len = 30 ;
* pparams = ( char * ) SMB_REALLOC ( * pparams , param_len ) ;
if ( * pparams = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
goto out ;
}
params = * pparams ;
memset ( params , ' \0 ' , param_len ) ;
send_trans2_replies ( conn , req , STATUS_INVALID_EA_NAME ,
params , param_len , NULL , 0 , max_data_bytes ) ;
goto out ;
}
}
status = SMB_VFS_CREATE_FILE (
conn , /* conn */
req , /* req */
2022-07-27 22:36:23 +03:00
dirfsp , /* dirfsp */
2022-03-21 21:29:26 +03:00
smb_fname , /* fname */
access_mask , /* access_mask */
share_mode , /* share_access */
create_disposition , /* create_disposition*/
create_options , /* create_options */
open_attr , /* file_attributes */
oplock_request , /* oplock_request */
NULL , /* lease */
open_size , /* allocation_size */
private_flags ,
NULL , /* sd */
ea_list , /* ea_list */
& fsp , /* result */
& smb_action , /* psbuf */
NULL , NULL ) ; /* create context */
if ( ! NT_STATUS_IS_OK ( status ) ) {
if ( open_was_deferred ( req - > xconn , req - > mid ) ) {
/* We have re-scheduled this call. */
goto out ;
}
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_SHARING_VIOLATION ) ) {
reply_openerror ( req , status ) ;
goto out ;
}
fsp = fcb_or_dos_open (
req ,
smb_fname ,
access_mask ,
create_options ,
private_flags ) ;
if ( fsp = = NULL ) {
bool ok = defer_smb1_sharing_violation ( req ) ;
if ( ok ) {
goto out ;
}
reply_openerror ( req , status ) ;
goto out ;
}
smb_action = FILE_WAS_OPENED ;
}
size = get_file_size_stat ( & smb_fname - > st ) ;
fattr = fdos_mode ( fsp ) ;
mtime = convert_timespec_to_time_t ( smb_fname - > st . st_ex_mtime ) ;
inode = smb_fname - > st . st_ex_ino ;
if ( fattr & FILE_ATTRIBUTE_DIRECTORY ) {
close_file_free ( req , & fsp , ERROR_CLOSE ) ;
reply_nterror ( req , NT_STATUS_ACCESS_DENIED ) ;
goto out ;
}
/* Realloc the size of parameters and data we will return */
* pparams = ( char * ) SMB_REALLOC ( * pparams , 30 ) ;
if ( * pparams = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
goto out ;
}
params = * pparams ;
SSVAL ( params , 0 , fsp - > fnum ) ;
SSVAL ( params , 2 , fattr ) ;
srv_put_dos_date2 ( params , 4 , mtime ) ;
SIVAL ( params , 8 , ( uint32_t ) size ) ;
SSVAL ( params , 12 , deny_mode ) ;
SSVAL ( params , 14 , 0 ) ; /* open_type - file or directory. */
SSVAL ( params , 16 , 0 ) ; /* open_state - only valid for IPC device. */
if ( oplock_request & & lp_fake_oplocks ( SNUM ( conn ) ) ) {
smb_action | = EXTENDED_OPLOCK_GRANTED ;
}
SSVAL ( params , 18 , smb_action ) ;
/*
* WARNING - this may need to be changed if SMB_INO_T < > 4 bytes .
*/
SIVAL ( params , 20 , inode ) ;
SSVAL ( params , 24 , 0 ) ; /* Padding. */
if ( flags & 8 ) {
uint32_t ea_size = estimate_ea_size ( smb_fname - > fsp ) ;
SIVAL ( params , 26 , ea_size ) ;
} else {
SIVAL ( params , 26 , 0 ) ;
}
/* Send the required number of replies */
send_trans2_replies ( conn , req , NT_STATUS_OK , params , 30 , * ppdata , 0 , max_data_bytes ) ;
out :
TALLOC_FREE ( smb_fname ) ;
}
static NTSTATUS get_lanman2_dir_entry ( TALLOC_CTX * ctx ,
connection_struct * conn ,
struct dptr_struct * dirptr ,
uint16_t flags2 ,
const char * path_mask ,
uint32_t dirtype ,
int info_level ,
bool requires_resume_key ,
bool dont_descend ,
bool ask_sharemode ,
char * * ppdata ,
char * base_data ,
char * end_data ,
int space_remaining ,
bool * got_exact_match ,
int * last_entry_off ,
struct ea_list * name_list )
{
uint8_t align = 4 ;
const bool do_pad = true ;
if ( info_level > = 1 & & info_level < = 3 ) {
/* No alignment on earlier info levels. */
align = 1 ;
}
return smbd_dirptr_lanman2_entry ( ctx , conn , dirptr , flags2 ,
path_mask , dirtype , info_level ,
requires_resume_key , dont_descend , ask_sharemode ,
true , align , do_pad ,
ppdata , base_data , end_data ,
space_remaining ,
NULL ,
got_exact_match ,
last_entry_off , name_list , NULL ) ;
}
/****************************************************************************
Reply to a TRANS2_FINDFIRST .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void call_trans2findfirst ( connection_struct * conn ,
struct smb_request * req ,
char * * pparams , int total_params ,
char * * ppdata , int total_data ,
unsigned int max_data_bytes )
{
/* We must be careful here that we don't return more than the
allowed number of data bytes . If this means returning fewer than
maxentries then so be it . We assume that the redirector has
enough room for the fixed number of parameter bytes it has
requested . */
struct smb_filename * smb_dname = NULL ;
char * params = * pparams ;
char * pdata = * ppdata ;
char * data_end ;
uint32_t dirtype ;
int maxentries ;
uint16_t findfirst_flags ;
bool close_after_first ;
bool close_if_end ;
bool requires_resume_key ;
int info_level ;
char * directory = NULL ;
char * mask = NULL ;
char * p ;
int last_entry_off = 0 ;
int dptr_num = - 1 ;
int numentries = 0 ;
int i ;
bool finished = False ;
bool dont_descend = False ;
bool out_of_space = False ;
int space_remaining ;
struct ea_list * ea_list = NULL ;
NTSTATUS ntstatus = NT_STATUS_OK ;
bool ask_sharemode = lp_smbd_search_ask_sharemode ( SNUM ( conn ) ) ;
struct smbd_server_connection * sconn = req - > sconn ;
uint32_t ucf_flags = ucf_flags_from_smb_request ( req ) ;
bool backup_priv = false ;
bool as_root = false ;
files_struct * fsp = NULL ;
2022-08-03 19:58:54 +03:00
struct files_struct * dirfsp = NULL ;
2022-03-21 21:29:26 +03:00
const struct loadparm_substitution * lp_sub =
loadparm_s3_global_substitution ( ) ;
if ( total_params < 13 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
goto out ;
}
dirtype = SVAL ( params , 0 ) ;
maxentries = SVAL ( params , 2 ) ;
findfirst_flags = SVAL ( params , 4 ) ;
close_after_first = ( findfirst_flags & FLAG_TRANS2_FIND_CLOSE ) ;
close_if_end = ( findfirst_flags & FLAG_TRANS2_FIND_CLOSE_IF_END ) ;
requires_resume_key = ( findfirst_flags & FLAG_TRANS2_FIND_REQUIRE_RESUME ) ;
backup_priv = ( ( findfirst_flags & FLAG_TRANS2_FIND_BACKUP_INTENT ) & &
security_token_has_privilege ( get_current_nttok ( conn ) ,
SEC_PRIV_BACKUP ) ) ;
info_level = SVAL ( params , 6 ) ;
DBG_NOTICE ( " dirtype = % " PRIx32 " , maxentries = %d, "
" close_after_first=%d, close_if_end = %d "
" requires_resume_key = %d backup_priv = %d level = 0x%x, "
" max_data_bytes = %d \n " ,
dirtype ,
maxentries ,
close_after_first ,
close_if_end ,
requires_resume_key ,
backup_priv ,
info_level ,
max_data_bytes ) ;
if ( ! maxentries ) {
/* W2K3 seems to treat zero as 1. */
maxentries = 1 ;
}
switch ( info_level ) {
case SMB_FIND_INFO_STANDARD :
case SMB_FIND_EA_SIZE :
case SMB_FIND_EA_LIST :
case SMB_FIND_FILE_DIRECTORY_INFO :
case SMB_FIND_FILE_FULL_DIRECTORY_INFO :
case SMB_FIND_FILE_NAMES_INFO :
case SMB_FIND_FILE_BOTH_DIRECTORY_INFO :
case SMB_FIND_ID_FULL_DIRECTORY_INFO :
case SMB_FIND_ID_BOTH_DIRECTORY_INFO :
break ;
case SMB_FIND_FILE_UNIX :
case SMB_FIND_FILE_UNIX_INFO2 :
if ( ! lp_smb1_unix_extensions ( ) ) {
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
goto out ;
}
if ( ! req - > posix_pathnames ) {
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
goto out ;
}
break ;
default :
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
goto out ;
}
if ( req - > posix_pathnames ) {
/* Always use filesystem for UNIX mtime query. */
ask_sharemode = false ;
}
if ( req - > posix_pathnames ) {
srvstr_get_path_posix ( talloc_tos ( ) ,
params ,
req - > flags2 ,
& directory ,
params + 12 ,
total_params - 12 ,
STR_TERMINATE ,
& ntstatus ) ;
} else {
srvstr_get_path ( talloc_tos ( ) ,
params ,
req - > flags2 ,
& directory ,
params + 12 ,
total_params - 12 ,
STR_TERMINATE ,
& ntstatus ) ;
}
if ( ! NT_STATUS_IS_OK ( ntstatus ) ) {
reply_nterror ( req , ntstatus ) ;
goto out ;
}
if ( backup_priv ) {
become_root ( ) ;
as_root = true ;
}
ntstatus = filename_convert_smb1_search_path ( talloc_tos ( ) ,
conn ,
directory ,
ucf_flags ,
2022-08-03 19:58:54 +03:00
& dirfsp ,
2022-03-21 21:29:26 +03:00
& smb_dname ,
& mask ) ;
if ( ! NT_STATUS_IS_OK ( ntstatus ) ) {
if ( NT_STATUS_EQUAL ( ntstatus , NT_STATUS_PATH_NOT_COVERED ) ) {
reply_botherror ( req , NT_STATUS_PATH_NOT_COVERED ,
ERRSRV , ERRbadpath ) ;
goto out ;
}
reply_nterror ( req , ntstatus ) ;
goto out ;
}
TALLOC_FREE ( directory ) ;
directory = smb_dname - > base_name ;
DEBUG ( 5 , ( " dir=%s, mask = %s \n " , directory , mask ) ) ;
if ( info_level = = SMB_FIND_EA_LIST ) {
uint32_t ea_size ;
if ( total_data < 4 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
goto out ;
}
ea_size = IVAL ( pdata , 0 ) ;
if ( ea_size ! = total_data ) {
DEBUG ( 4 , ( " call_trans2findfirst: Rejecting EA request with incorrect \
total_data = % u ( should be % u ) \ n " , (unsigned int)total_data, (unsigned int)IVAL(pdata,0) ));
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
goto out ;
}
if ( ! lp_ea_support ( SNUM ( conn ) ) ) {
reply_nterror ( req , NT_STATUS_EAS_NOT_SUPPORTED ) ;
goto out ;
}
/* Pull out the list of names. */
ea_list = read_ea_name_list ( talloc_tos ( ) , pdata + 4 , ea_size - 4 ) ;
if ( ! ea_list ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
goto out ;
}
}
if ( max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
goto out ;
}
* ppdata = ( char * ) SMB_REALLOC (
* ppdata , max_data_bytes + DIR_ENTRY_SAFETY_MARGIN ) ;
if ( * ppdata = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
goto out ;
}
pdata = * ppdata ;
data_end = pdata + max_data_bytes + DIR_ENTRY_SAFETY_MARGIN - 1 ;
/*
* squash valgrind " writev(vector[...]) points to uninitialised byte(s) "
* error .
*/
memset ( pdata + total_data , 0 , ( ( max_data_bytes + DIR_ENTRY_SAFETY_MARGIN ) - total_data ) ) ;
/* Realloc the params space */
* pparams = ( char * ) SMB_REALLOC ( * pparams , 10 ) ;
if ( * pparams = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
goto out ;
}
params = * pparams ;
/*
* Open an fsp on this directory for the dptr .
*/
ntstatus = SMB_VFS_CREATE_FILE (
conn , /* conn */
req , /* req */
2022-08-03 19:58:54 +03:00
dirfsp , /* dirfsp */
2022-03-21 21:29:26 +03:00
smb_dname , /* dname */
FILE_LIST_DIRECTORY , /* access_mask */
FILE_SHARE_READ |
FILE_SHARE_WRITE , /* share_access */
FILE_OPEN , /* create_disposition*/
FILE_DIRECTORY_FILE , /* create_options */
FILE_ATTRIBUTE_DIRECTORY , /* file_attributes */
NO_OPLOCK , /* oplock_request */
NULL , /* lease */
0 , /* allocation_size */
0 , /* private_flags */
NULL , /* sd */
NULL , /* ea_list */
& fsp , /* result */
NULL , /* pinfo */
NULL , /* in_context */
NULL ) ; /* out_context */
if ( ! NT_STATUS_IS_OK ( ntstatus ) ) {
DBG_ERR ( " failed to open directory %s \n " ,
smb_fname_str_dbg ( smb_dname ) ) ;
reply_nterror ( req , ntstatus ) ;
goto out ;
}
/* Save the wildcard match and attribs we are using on this directory -
needed as lanman2 assumes these are being saved between calls */
ntstatus = dptr_create ( conn ,
req ,
fsp , /* fsp */
False ,
True ,
req - > smbpid ,
mask ,
dirtype ,
& fsp - > dptr ) ;
if ( ! NT_STATUS_IS_OK ( ntstatus ) ) {
/*
* Use NULL here for the first parameter ( req )
* as this is not a client visible handle so
* can ' tbe part of an SMB1 chain .
*/
close_file_free ( NULL , & fsp , NORMAL_CLOSE ) ;
reply_nterror ( req , ntstatus ) ;
goto out ;
}
if ( backup_priv ) {
/* Remember this in case we have
to do a findnext . */
dptr_set_priv ( fsp - > dptr ) ;
}
dptr_num = dptr_dnum ( fsp - > dptr ) ;
DEBUG ( 4 , ( " dptr_num is %d, wcard = %s, attr = %d \n " , dptr_num , mask , dirtype ) ) ;
/* We don't need to check for VOL here as this is returned by
a different TRANS2 call . */
DEBUG ( 8 , ( " dirpath=<%s> dontdescend=<%s> \n " ,
directory , lp_dont_descend ( talloc_tos ( ) , lp_sub , SNUM ( conn ) ) ) ) ;
if ( in_list ( directory ,
lp_dont_descend ( talloc_tos ( ) , lp_sub , SNUM ( conn ) ) ,
dptr_case_sensitive ( fsp - > dptr ) ) ) {
dont_descend = True ;
}
p = pdata ;
space_remaining = max_data_bytes ;
out_of_space = False ;
for ( i = 0 ; ( i < maxentries ) & & ! finished & & ! out_of_space ; i + + ) {
bool got_exact_match = False ;
/* this is a heuristic to avoid seeking the dirptr except when
absolutely necessary . It allows for a filename of about 40 chars */
if ( space_remaining < DIRLEN_GUESS & & numentries > 0 ) {
out_of_space = True ;
finished = False ;
} else {
ntstatus = get_lanman2_dir_entry ( talloc_tos ( ) ,
conn ,
fsp - > dptr ,
req - > flags2 ,
mask , dirtype , info_level ,
requires_resume_key , dont_descend ,
ask_sharemode ,
& p , pdata , data_end ,
space_remaining ,
& got_exact_match ,
& last_entry_off , ea_list ) ;
if ( NT_STATUS_EQUAL ( ntstatus ,
NT_STATUS_ILLEGAL_CHARACTER ) ) {
/*
* Bad character conversion on name . Ignore this
* entry .
*/
continue ;
}
if ( NT_STATUS_EQUAL ( ntstatus , STATUS_MORE_ENTRIES ) ) {
out_of_space = true ;
} else {
finished = ! NT_STATUS_IS_OK ( ntstatus ) ;
}
}
if ( ! finished & & ! out_of_space )
numentries + + ;
/*
* As an optimisation if we know we aren ' t looking
* for a wildcard name ( ie . the name matches the wildcard exactly )
* then we can finish on any ( first ) match .
* This speeds up large directory searches . JRA .
*/
if ( got_exact_match )
finished = True ;
/* Ensure space_remaining never goes -ve. */
if ( PTR_DIFF ( p , pdata ) > max_data_bytes ) {
space_remaining = 0 ;
out_of_space = true ;
} else {
space_remaining = max_data_bytes - PTR_DIFF ( p , pdata ) ;
}
}
/* Check if we can close the dirptr */
if ( close_after_first | | ( finished & & close_if_end ) ) {
DEBUG ( 5 , ( " call_trans2findfirst - (2) closing dptr_num %d \n " , dptr_num ) ) ;
dptr_num = - 1 ;
close_file_free ( NULL , & fsp , NORMAL_CLOSE ) ;
}
/*
* If there are no matching entries we must return ERRDOS / ERRbadfile -
* from observation of NT . NB . This changes to ERRDOS , ERRnofiles if
* the protocol level is less than NT1 . Tested with smbclient . JRA .
* This should fix the OS / 2 client bug # 2335.
*/
if ( numentries = = 0 ) {
dptr_num = - 1 ;
/*
* We may have already closed the file in the
* close_after_first or finished case above .
*/
if ( fsp ! = NULL ) {
close_file_free ( NULL , & fsp , NORMAL_CLOSE ) ;
}
if ( get_Protocol ( ) < PROTOCOL_NT1 ) {
reply_force_doserror ( req , ERRDOS , ERRnofiles ) ;
goto out ;
} else {
reply_botherror ( req , NT_STATUS_NO_SUCH_FILE ,
ERRDOS , ERRbadfile ) ;
goto out ;
}
}
/* At this point pdata points to numentries directory entries. */
/* Set up the return parameter block */
SSVAL ( params , 0 , dptr_num ) ;
SSVAL ( params , 2 , numentries ) ;
SSVAL ( params , 4 , finished ) ;
SSVAL ( params , 6 , 0 ) ; /* Never an EA error */
SSVAL ( params , 8 , last_entry_off ) ;
send_trans2_replies ( conn , req , NT_STATUS_OK , params , 10 , pdata , PTR_DIFF ( p , pdata ) ,
max_data_bytes ) ;
if ( ( ! * directory ) & & dptr_path ( sconn , dptr_num ) ) {
directory = talloc_strdup ( talloc_tos ( ) , dptr_path ( sconn , dptr_num ) ) ;
if ( ! directory ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
}
}
DEBUG ( 4 , ( " %s mask=%s directory=%s dirtype=%d numentries=%d \n " ,
smb_fn_name ( req - > cmd ) ,
mask , directory , dirtype , numentries ) ) ;
/*
* Force a name mangle here to ensure that the
* mask as an 8.3 name is top of the mangled cache .
* The reasons for this are subtle . Don ' t remove
* this code unless you know what you are doing
* ( see PR # 13758 ) . JRA .
*/
if ( ! mangle_is_8_3_wildcards ( mask , False , conn - > params ) ) {
char mangled_name [ 13 ] ;
name_to_8_3 ( mask , mangled_name , True , conn - > params ) ;
}
out :
if ( as_root ) {
unbecome_root ( ) ;
}
TALLOC_FREE ( smb_dname ) ;
return ;
}
/****************************************************************************
Reply to a TRANS2_FINDNEXT .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void call_trans2findnext ( connection_struct * conn ,
struct smb_request * req ,
char * * pparams , int total_params ,
char * * ppdata , int total_data ,
unsigned int max_data_bytes )
{
/* We must be careful here that we don't return more than the
allowed number of data bytes . If this means returning fewer than
maxentries then so be it . We assume that the redirector has
enough room for the fixed number of parameter bytes it has
requested . */
char * params = * pparams ;
char * pdata = * ppdata ;
char * data_end ;
int dptr_num ;
int maxentries ;
uint16_t info_level ;
uint32_t resume_key ;
uint16_t findnext_flags ;
bool close_after_request ;
bool close_if_end ;
bool requires_resume_key ;
bool continue_bit ;
char * resume_name = NULL ;
const char * mask = NULL ;
const char * directory = NULL ;
char * p = NULL ;
uint16_t dirtype ;
int numentries = 0 ;
int i , last_entry_off = 0 ;
bool finished = False ;
bool dont_descend = False ;
bool out_of_space = False ;
int space_remaining ;
struct ea_list * ea_list = NULL ;
NTSTATUS ntstatus = NT_STATUS_OK ;
bool ask_sharemode = lp_smbd_search_ask_sharemode ( SNUM ( conn ) ) ;
TALLOC_CTX * ctx = talloc_tos ( ) ;
struct smbd_server_connection * sconn = req - > sconn ;
bool backup_priv = false ;
bool as_root = false ;
files_struct * fsp = NULL ;
const struct loadparm_substitution * lp_sub =
loadparm_s3_global_substitution ( ) ;
if ( total_params < 13 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
dptr_num = SVAL ( params , 0 ) ;
maxentries = SVAL ( params , 2 ) ;
info_level = SVAL ( params , 4 ) ;
resume_key = IVAL ( params , 6 ) ;
findnext_flags = SVAL ( params , 10 ) ;
close_after_request = ( findnext_flags & FLAG_TRANS2_FIND_CLOSE ) ;
close_if_end = ( findnext_flags & FLAG_TRANS2_FIND_CLOSE_IF_END ) ;
requires_resume_key = ( findnext_flags & FLAG_TRANS2_FIND_REQUIRE_RESUME ) ;
continue_bit = ( findnext_flags & FLAG_TRANS2_FIND_CONTINUE ) ;
if ( ! continue_bit ) {
/* We only need resume_name if continue_bit is zero. */
if ( req - > posix_pathnames ) {
srvstr_get_path_posix ( ctx ,
params ,
req - > flags2 ,
& resume_name ,
params + 12 ,
total_params - 12 ,
STR_TERMINATE ,
& ntstatus ) ;
} else {
srvstr_get_path ( ctx ,
params ,
req - > flags2 ,
& resume_name ,
params + 12 ,
total_params - 12 ,
STR_TERMINATE ,
& ntstatus ) ;
}
if ( ! NT_STATUS_IS_OK ( ntstatus ) ) {
/* Win9x or OS/2 can send a resume name of ".." or ".". This will cause the parser to
complain ( it thinks we ' re asking for the directory above the shared
path or an invalid name ) . Catch this as the resume name is only compared , never used in
a file access . JRA . */
srvstr_pull_talloc ( ctx , params , req - > flags2 ,
& resume_name , params + 12 ,
total_params - 12 ,
STR_TERMINATE ) ;
if ( ! resume_name | | ! ( ISDOT ( resume_name ) | | ISDOTDOT ( resume_name ) ) ) {
reply_nterror ( req , ntstatus ) ;
return ;
}
}
}
DEBUG ( 3 , ( " call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, \
close_after_request = % d , close_if_end = % d requires_resume_key = % d \
resume_key = % d resume name = % s continue = % d level = % d \ n " ,
dptr_num , max_data_bytes , maxentries , close_after_request , close_if_end ,
requires_resume_key , resume_key ,
resume_name ? resume_name : " (NULL) " , continue_bit , info_level ) ) ;
if ( ! maxentries ) {
/* W2K3 seems to treat zero as 1. */
maxentries = 1 ;
}
switch ( info_level ) {
case SMB_FIND_INFO_STANDARD :
case SMB_FIND_EA_SIZE :
case SMB_FIND_EA_LIST :
case SMB_FIND_FILE_DIRECTORY_INFO :
case SMB_FIND_FILE_FULL_DIRECTORY_INFO :
case SMB_FIND_FILE_NAMES_INFO :
case SMB_FIND_FILE_BOTH_DIRECTORY_INFO :
case SMB_FIND_ID_FULL_DIRECTORY_INFO :
case SMB_FIND_ID_BOTH_DIRECTORY_INFO :
break ;
case SMB_FIND_FILE_UNIX :
case SMB_FIND_FILE_UNIX_INFO2 :
/* Always use filesystem for UNIX mtime query. */
ask_sharemode = false ;
if ( ! lp_smb1_unix_extensions ( ) ) {
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
if ( ! req - > posix_pathnames ) {
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
break ;
default :
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
if ( info_level = = SMB_FIND_EA_LIST ) {
uint32_t ea_size ;
if ( total_data < 4 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
ea_size = IVAL ( pdata , 0 ) ;
if ( ea_size ! = total_data ) {
DEBUG ( 4 , ( " call_trans2findnext: Rejecting EA request with incorrect \
total_data = % u ( should be % u ) \ n " , (unsigned int)total_data, (unsigned int)IVAL(pdata,0) ));
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
if ( ! lp_ea_support ( SNUM ( conn ) ) ) {
reply_nterror ( req , NT_STATUS_EAS_NOT_SUPPORTED ) ;
return ;
}
/* Pull out the list of names. */
ea_list = read_ea_name_list ( ctx , pdata + 4 , ea_size - 4 ) ;
if ( ! ea_list ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
}
if ( max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
* ppdata = ( char * ) SMB_REALLOC (
* ppdata , max_data_bytes + DIR_ENTRY_SAFETY_MARGIN ) ;
if ( * ppdata = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
pdata = * ppdata ;
data_end = pdata + max_data_bytes + DIR_ENTRY_SAFETY_MARGIN - 1 ;
/*
* squash valgrind " writev(vector[...]) points to uninitialised byte(s) "
* error .
*/
memset ( pdata + total_data , 0 , ( max_data_bytes + DIR_ENTRY_SAFETY_MARGIN ) - total_data ) ;
/* Realloc the params space */
* pparams = ( char * ) SMB_REALLOC ( * pparams , 6 * SIZEOFWORD ) ;
if ( * pparams = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
params = * pparams ;
/* Check that the dptr is valid */
fsp = dptr_fetch_lanman2_fsp ( sconn , dptr_num ) ;
if ( fsp = = NULL ) {
reply_nterror ( req , STATUS_NO_MORE_FILES ) ;
return ;
}
directory = dptr_path ( sconn , dptr_num ) ;
/* Get the wildcard mask from the dptr */
if ( ( mask = dptr_wcard ( sconn , dptr_num ) ) = = NULL ) {
DEBUG ( 2 , ( " dptr_num %d has no wildcard \n " , dptr_num ) ) ;
reply_nterror ( req , STATUS_NO_MORE_FILES ) ;
return ;
}
/* Get the attr mask from the dptr */
dirtype = dptr_attr ( sconn , dptr_num ) ;
backup_priv = dptr_get_priv ( fsp - > dptr ) ;
DEBUG ( 3 , ( " dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%lX,%ld) "
" backup_priv = %d \n " ,
dptr_num , mask , dirtype ,
( long ) fsp - > dptr ,
dptr_TellDir ( fsp - > dptr ) ,
( int ) backup_priv ) ) ;
/* We don't need to check for VOL here as this is returned by
a different TRANS2 call . */
DEBUG ( 8 , ( " dirpath=<%s> dontdescend=<%s> \n " ,
directory , lp_dont_descend ( ctx , lp_sub , SNUM ( conn ) ) ) ) ;
if ( in_list ( directory , lp_dont_descend ( ctx , lp_sub , SNUM ( conn ) ) ,
dptr_case_sensitive ( fsp - > dptr ) ) )
dont_descend = True ;
p = pdata ;
space_remaining = max_data_bytes ;
out_of_space = False ;
if ( backup_priv ) {
become_root ( ) ;
as_root = true ;
}
/*
* Seek to the correct position . We no longer use the resume key but
* depend on the last file name instead .
*/
if ( ! continue_bit & & resume_name & & * resume_name ) {
SMB_STRUCT_STAT st ;
bool posix_open = ( fsp - > posix_flags & FSP_POSIX_FLAGS_OPEN ) ;
long current_pos = 0 ;
/*
* Remember , name_to_8_3 is called by
* get_lanman2_dir_entry ( ) , so the resume name
* could be mangled . Ensure we check the unmangled name .
*/
if ( ! posix_open & &
mangle_is_mangled ( resume_name , conn - > params ) ) {
char * new_resume_name = NULL ;
mangle_lookup_name_from_8_3 ( ctx ,
resume_name ,
& new_resume_name ,
conn - > params ) ;
if ( new_resume_name ) {
resume_name = new_resume_name ;
}
}
/*
* Fix for NT redirector problem triggered by resume key indexes
* changing between directory scans . We now return a resume key of 0
* and instead look for the filename to continue from ( also given
* to us by NT / 95 / smbfs / smbclient ) . If no other scans have been done between the
* findfirst / findnext ( as is usual ) then the directory pointer
* should already be at the correct place .
*/
finished = ! dptr_SearchDir ( fsp - > dptr , resume_name , & current_pos , & st ) ;
} /* end if resume_name && !continue_bit */
for ( i = 0 ; ( i < ( int ) maxentries ) & & ! finished & & ! out_of_space ; i + + ) {
bool got_exact_match = False ;
/* this is a heuristic to avoid seeking the fsp->dptr except when
absolutely necessary . It allows for a filename of about 40 chars */
if ( space_remaining < DIRLEN_GUESS & & numentries > 0 ) {
out_of_space = True ;
finished = False ;
} else {
ntstatus = get_lanman2_dir_entry ( ctx ,
conn ,
fsp - > dptr ,
req - > flags2 ,
mask , dirtype , info_level ,
requires_resume_key , dont_descend ,
ask_sharemode ,
& p , pdata , data_end ,
space_remaining ,
& got_exact_match ,
& last_entry_off , ea_list ) ;
if ( NT_STATUS_EQUAL ( ntstatus ,
NT_STATUS_ILLEGAL_CHARACTER ) ) {
/*
* Bad character conversion on name . Ignore this
* entry .
*/
continue ;
}
if ( NT_STATUS_EQUAL ( ntstatus , STATUS_MORE_ENTRIES ) ) {
out_of_space = true ;
} else {
finished = ! NT_STATUS_IS_OK ( ntstatus ) ;
}
}
if ( ! finished & & ! out_of_space )
numentries + + ;
/*
* As an optimisation if we know we aren ' t looking
* for a wildcard name ( ie . the name matches the wildcard exactly )
* then we can finish on any ( first ) match .
* This speeds up large directory searches . JRA .
*/
if ( got_exact_match )
finished = True ;
space_remaining = max_data_bytes - PTR_DIFF ( p , pdata ) ;
}
DEBUG ( 3 , ( " %s mask=%s directory=%s dirtype=%d numentries=%d \n " ,
smb_fn_name ( req - > cmd ) ,
mask , directory , dirtype , numentries ) ) ;
/* Check if we can close the fsp->dptr */
if ( close_after_request | | ( finished & & close_if_end ) ) {
DEBUG ( 5 , ( " call_trans2findnext: closing dptr_num = %d \n " , dptr_num ) ) ;
dptr_num = - 1 ;
close_file_free ( NULL , & fsp , NORMAL_CLOSE ) ;
}
if ( as_root ) {
unbecome_root ( ) ;
}
/* Set up the return parameter block */
SSVAL ( params , 0 , numentries ) ;
SSVAL ( params , 2 , finished ) ;
SSVAL ( params , 4 , 0 ) ; /* Never an EA error */
SSVAL ( params , 6 , last_entry_off ) ;
send_trans2_replies ( conn , req , NT_STATUS_OK , params , 8 , pdata , PTR_DIFF ( p , pdata ) ,
max_data_bytes ) ;
return ;
}
/****************************************************************************
Reply to a TRANS2_QFSINFO ( query filesystem info ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void call_trans2qfsinfo ( connection_struct * conn ,
struct smb_request * req ,
char * * pparams , int total_params ,
char * * ppdata , int total_data ,
unsigned int max_data_bytes )
{
char * params = * pparams ;
uint16_t info_level ;
int data_len = 0 ;
size_t fixed_portion ;
NTSTATUS status ;
if ( total_params < 2 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
info_level = SVAL ( params , 0 ) ;
if ( ENCRYPTION_REQUIRED ( conn ) & & ! req - > encrypted ) {
if ( info_level ! = SMB_QUERY_CIFS_UNIX_INFO ) {
DEBUG ( 0 , ( " call_trans2qfsinfo: encryption required "
" and info level 0x%x sent. \n " ,
( unsigned int ) info_level ) ) ;
reply_nterror ( req , NT_STATUS_ACCESS_DENIED ) ;
return ;
}
}
DEBUG ( 3 , ( " call_trans2qfsinfo: level = %d \n " , info_level ) ) ;
status = smbd_do_qfsinfo ( req - > xconn , conn , req ,
info_level ,
req - > flags2 ,
max_data_bytes ,
& fixed_portion ,
NULL ,
ppdata , & data_len ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
reply_nterror ( req , status ) ;
return ;
}
send_trans2_replies ( conn , req , NT_STATUS_OK , params , 0 , * ppdata , data_len ,
max_data_bytes ) ;
DEBUG ( 4 , ( " %s info_level = %d \n " ,
smb_fn_name ( req - > cmd ) , info_level ) ) ;
return ;
}
/****************************************************************************
Reply to a TRANS2_SETFSINFO ( set filesystem info ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void call_trans2setfsinfo ( connection_struct * conn ,
struct smb_request * req ,
char * * pparams , int total_params ,
char * * ppdata , int total_data ,
unsigned int max_data_bytes )
{
const struct loadparm_substitution * lp_sub =
loadparm_s3_global_substitution ( ) ;
struct smbXsrv_connection * xconn = req - > xconn ;
char * pdata = * ppdata ;
char * params = * pparams ;
uint16_t info_level ;
DEBUG ( 10 , ( " call_trans2setfsinfo: for service [%s] \n " ,
lp_servicename ( talloc_tos ( ) , lp_sub , SNUM ( conn ) ) ) ) ;
/* */
if ( total_params < 4 ) {
DEBUG ( 0 , ( " call_trans2setfsinfo: requires total_params(%d) >= 4 bytes! \n " ,
total_params ) ) ;
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
info_level = SVAL ( params , 2 ) ;
if ( IS_IPC ( conn ) ) {
if ( info_level ! = SMB_REQUEST_TRANSPORT_ENCRYPTION & &
info_level ! = SMB_SET_CIFS_UNIX_INFO ) {
DEBUG ( 0 , ( " call_trans2setfsinfo: not an allowed "
" info level (0x%x) on IPC$. \n " ,
( unsigned int ) info_level ) ) ;
reply_nterror ( req , NT_STATUS_ACCESS_DENIED ) ;
return ;
}
}
if ( ENCRYPTION_REQUIRED ( conn ) & & ! req - > encrypted ) {
if ( info_level ! = SMB_REQUEST_TRANSPORT_ENCRYPTION ) {
DEBUG ( 0 , ( " call_trans2setfsinfo: encryption required "
" and info level 0x%x sent. \n " ,
( unsigned int ) info_level ) ) ;
reply_nterror ( req , NT_STATUS_ACCESS_DENIED ) ;
return ;
}
}
switch ( info_level ) {
case SMB_SET_CIFS_UNIX_INFO :
if ( ! lp_smb1_unix_extensions ( ) ) {
DEBUG ( 2 , ( " call_trans2setfsinfo: "
" SMB_SET_CIFS_UNIX_INFO is invalid with "
" unix extensions off \n " ) ) ;
reply_nterror ( req ,
NT_STATUS_INVALID_LEVEL ) ;
return ;
}
/* There should be 12 bytes of capabilities set. */
if ( total_data < 12 ) {
reply_nterror (
req ,
NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
xconn - > smb1 . unix_info . client_major = SVAL ( pdata , 0 ) ;
xconn - > smb1 . unix_info . client_minor = SVAL ( pdata , 2 ) ;
xconn - > smb1 . unix_info . client_cap_low = IVAL ( pdata , 4 ) ;
xconn - > smb1 . unix_info . client_cap_high = IVAL ( pdata , 8 ) ;
/* Just print these values for now. */
DEBUG ( 10 , ( " call_trans2setfsinfo: set unix_info info. "
" major = %u, minor = %u cap_low = 0x%x, "
" cap_high = 0x%xn " ,
( unsigned int ) xconn - >
smb1 . unix_info . client_major ,
( unsigned int ) xconn - >
smb1 . unix_info . client_minor ,
( unsigned int ) xconn - >
smb1 . unix_info . client_cap_low ,
( unsigned int ) xconn - >
smb1 . unix_info . client_cap_high ) ) ;
/* Here is where we must switch to posix pathname processing... */
if ( xconn - > smb1 . unix_info . client_cap_low & CIFS_UNIX_POSIX_PATHNAMES_CAP ) {
lp_set_posix_pathnames ( ) ;
mangle_change_to_posix ( ) ;
}
if ( ( xconn - > smb1 . unix_info . client_cap_low & CIFS_UNIX_FCNTL_LOCKS_CAP ) & &
! ( xconn - > smb1 . unix_info . client_cap_low & CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP ) ) {
/* Client that knows how to do posix locks,
* but not posix open / mkdir operations . Set a
* default type for read / write checks . */
lp_set_posix_default_cifsx_readwrite_locktype ( POSIX_LOCK ) ;
}
break ;
case SMB_REQUEST_TRANSPORT_ENCRYPTION :
{
NTSTATUS status ;
size_t param_len = 0 ;
size_t data_len = total_data ;
if ( ! lp_smb1_unix_extensions ( ) ) {
reply_nterror (
req ,
NT_STATUS_INVALID_LEVEL ) ;
return ;
}
if ( lp_server_smb_encrypt ( SNUM ( conn ) ) = =
SMB_ENCRYPTION_OFF ) {
reply_nterror (
req ,
NT_STATUS_NOT_SUPPORTED ) ;
return ;
}
if ( xconn - > smb1 . echo_handler . trusted_fde ) {
DEBUG ( 2 , ( " call_trans2setfsinfo: "
" request transport encryption disabled "
" with 'fork echo handler = yes' \n " ) ) ;
reply_nterror (
req ,
NT_STATUS_NOT_SUPPORTED ) ;
return ;
}
DEBUG ( 4 , ( " call_trans2setfsinfo: "
" request transport encryption. \n " ) ) ;
status = srv_request_encryption_setup ( conn ,
( unsigned char * * ) ppdata ,
& data_len ,
( unsigned char * * ) pparams ,
& param_len ) ;
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) & &
! NT_STATUS_IS_OK ( status ) ) {
reply_nterror ( req , status ) ;
return ;
}
send_trans2_replies ( conn , req ,
NT_STATUS_OK ,
* pparams ,
param_len ,
* ppdata ,
data_len ,
max_data_bytes ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
/* Server-side transport
* encryption is now * on * . */
status = srv_encryption_start ( conn ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
char * reason = talloc_asprintf ( talloc_tos ( ) ,
" Failure in setting "
" up encrypted transport: %s " ,
nt_errstr ( status ) ) ;
exit_server_cleanly ( reason ) ;
}
}
return ;
}
case SMB_FS_QUOTA_INFORMATION :
{
NTSTATUS status ;
DATA_BLOB qdata = {
. data = ( uint8_t * ) pdata ,
. length = total_data
} ;
files_struct * fsp = NULL ;
fsp = file_fsp ( req , SVAL ( params , 0 ) ) ;
status = smb_set_fsquota ( conn ,
req ,
fsp ,
& qdata ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
reply_nterror ( req , status ) ;
return ;
}
break ;
}
default :
DEBUG ( 3 , ( " call_trans2setfsinfo: unknown level (0x%X) not implemented yet. \n " ,
info_level ) ) ;
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
break ;
}
/*
* sending this reply works fine ,
* but I ' m not sure it ' s the same
* like windows do . . .
* - - metze
*/
2022-04-05 05:53:20 +03:00
reply_smb1_outbuf ( req , 10 , 0 ) ;
2022-03-21 21:29:26 +03:00
}
/****************************************************************************
Reply to a TRANSACT2_QFILEINFO on a PIPE !
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void call_trans2qpipeinfo ( connection_struct * conn ,
struct smb_request * req ,
2022-04-21 16:48:09 +03:00
files_struct * fsp ,
uint16_t info_level ,
2022-03-21 21:29:26 +03:00
unsigned int tran_call ,
char * * pparams , int total_params ,
char * * ppdata , int total_data ,
unsigned int max_data_bytes )
{
char * params = * pparams ;
char * pdata = * ppdata ;
unsigned int data_size = 0 ;
unsigned int param_size = 2 ;
if ( ! fsp_is_np ( fsp ) ) {
reply_nterror ( req , NT_STATUS_INVALID_HANDLE ) ;
return ;
}
* pparams = ( char * ) SMB_REALLOC ( * pparams , 2 ) ;
if ( * pparams = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
params = * pparams ;
SSVAL ( params , 0 , 0 ) ;
if ( max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
data_size = max_data_bytes + DIR_ENTRY_SAFETY_MARGIN ;
* ppdata = ( char * ) SMB_REALLOC ( * ppdata , data_size ) ;
if ( * ppdata = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
pdata = * ppdata ;
switch ( info_level ) {
case SMB_FILE_STANDARD_INFORMATION :
memset ( pdata , 0 , 24 ) ;
SOFF_T ( pdata , 0 , 4096LL ) ;
SIVAL ( pdata , 16 , 1 ) ;
SIVAL ( pdata , 20 , 1 ) ;
data_size = 24 ;
break ;
default :
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
send_trans2_replies ( conn , req , NT_STATUS_OK , params , param_size , * ppdata , data_size ,
max_data_bytes ) ;
return ;
}
/****************************************************************************
Reply to a TRANS2_QFILEPATHINFO or TRANSACT2_QFILEINFO ( query file info by
file name or file id ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void call_trans2qfilepathinfo ( connection_struct * conn ,
struct smb_request * req ,
unsigned int tran_call ,
char * * pparams , int total_params ,
char * * ppdata , int total_data ,
unsigned int max_data_bytes )
{
char * params = * pparams ;
char * pdata = * ppdata ;
uint16_t info_level ;
unsigned int data_size = 0 ;
unsigned int param_size = 2 ;
struct smb_filename * smb_fname = NULL ;
bool delete_pending = False ;
struct timespec write_time_ts ;
2022-07-28 20:39:12 +03:00
struct files_struct * dirfsp = NULL ;
2022-03-21 21:29:26 +03:00
files_struct * fsp = NULL ;
struct file_id fileid ;
struct ea_list * ea_list = NULL ;
int lock_data_count = 0 ;
char * lock_data = NULL ;
size_t fixed_portion ;
NTSTATUS status = NT_STATUS_OK ;
int ret ;
if ( ! params ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
ZERO_STRUCT ( write_time_ts ) ;
if ( tran_call = = TRANSACT2_QFILEINFO ) {
if ( total_params < 4 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
2022-04-21 16:48:09 +03:00
fsp = file_fsp ( req , SVAL ( params , 0 ) ) ;
info_level = SVAL ( params , 2 ) ;
2022-03-21 21:29:26 +03:00
if ( IS_IPC ( conn ) ) {
2022-04-21 16:48:09 +03:00
call_trans2qpipeinfo (
conn ,
req ,
fsp ,
info_level ,
tran_call ,
pparams ,
total_params ,
ppdata ,
total_data ,
max_data_bytes ) ;
2022-03-21 21:29:26 +03:00
return ;
}
DEBUG ( 3 , ( " call_trans2qfilepathinfo: TRANSACT2_QFILEINFO: level = %d \n " , info_level ) ) ;
if ( INFO_LEVEL_IS_UNIX ( info_level ) ) {
if ( ! lp_smb1_unix_extensions ( ) ) {
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
if ( ! req - > posix_pathnames ) {
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
}
/* Initial check for valid fsp ptr. */
if ( ! check_fsp_open ( conn , req , fsp ) ) {
return ;
}
smb_fname = fsp - > fsp_name ;
if ( fsp - > fake_file_handle ) {
/*
* This is actually for the QUOTA_FAKE_FILE - - metze
*/
/* We know this name is ok, it's already passed the checks. */
} else if ( fsp_get_pathref_fd ( fsp ) = = - 1 ) {
/*
* This is actually a QFILEINFO on a directory
* handle ( returned from an NT SMB ) . NT5 .0 seems
* to do this call . JRA .
*/
ret = vfs_stat ( conn , smb_fname ) ;
if ( ret ! = 0 ) {
DBG_NOTICE ( " vfs_stat of %s failed (%s) \n " ,
smb_fname_str_dbg ( smb_fname ) ,
strerror ( errno ) ) ;
reply_nterror ( req ,
map_nt_error_from_unix ( errno ) ) ;
return ;
}
if ( lp_smbd_getinfo_ask_sharemode ( SNUM ( conn ) ) ) {
fileid = vfs_file_id_from_sbuf (
conn , & smb_fname - > st ) ;
get_file_infos ( fileid , fsp - > name_hash ,
& delete_pending ,
& write_time_ts ) ;
}
} else {
/*
* Original code - this is an open file .
*/
status = vfs_stat_fsp ( fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 3 , ( " fstat of %s failed (%s) \n " ,
fsp_fnum_dbg ( fsp ) , nt_errstr ( status ) ) ) ;
reply_nterror ( req , status ) ;
return ;
}
if ( lp_smbd_getinfo_ask_sharemode ( SNUM ( conn ) ) ) {
fileid = vfs_file_id_from_sbuf (
conn , & smb_fname - > st ) ;
get_file_infos ( fileid , fsp - > name_hash ,
& delete_pending ,
& write_time_ts ) ;
}
}
} else {
uint32_t name_hash ;
char * fname = NULL ;
uint32_t ucf_flags = ucf_flags_from_smb_request ( req ) ;
2022-07-28 20:39:12 +03:00
NTTIME twrp = 0 ;
2022-03-21 21:29:26 +03:00
/* qpathinfo */
if ( total_params < 7 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
info_level = SVAL ( params , 0 ) ;
DEBUG ( 3 , ( " call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d \n " , info_level ) ) ;
if ( INFO_LEVEL_IS_UNIX ( info_level ) ) {
if ( ! lp_smb1_unix_extensions ( ) ) {
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
if ( ! req - > posix_pathnames ) {
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
}
if ( req - > posix_pathnames ) {
srvstr_get_path_posix ( req ,
params ,
req - > flags2 ,
& fname ,
& params [ 6 ] ,
total_params - 6 ,
STR_TERMINATE ,
& status ) ;
} else {
srvstr_get_path ( req ,
params ,
req - > flags2 ,
& fname ,
& params [ 6 ] ,
total_params - 6 ,
STR_TERMINATE ,
& status ) ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
reply_nterror ( req , status ) ;
return ;
}
2022-07-28 20:39:12 +03:00
if ( ucf_flags & UCF_GMT_PATHNAME ) {
2022-08-04 20:02:15 +03:00
extract_snapshot_token ( fname , & twrp ) ;
2022-07-28 20:39:12 +03:00
}
status = filename_convert_dirfsp ( req ,
conn ,
fname ,
ucf_flags ,
twrp ,
& dirfsp ,
& smb_fname ) ;
2022-03-21 21:29:26 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
if ( NT_STATUS_EQUAL ( status , NT_STATUS_PATH_NOT_COVERED ) ) {
reply_botherror ( req ,
NT_STATUS_PATH_NOT_COVERED ,
ERRSRV , ERRbadpath ) ;
return ;
}
reply_nterror ( req , status ) ;
return ;
}
/*
* qpathinfo must operate on an existing file , so we
2022-07-28 20:39:12 +03:00
* can exit early if filename_convert_dirfsp ( ) returned the
* " new file " NT_STATUS_OK , ! VALID_STAT case .
2022-03-21 21:29:26 +03:00
*/
if ( ! VALID_STAT ( smb_fname - > st ) ) {
reply_nterror ( req , NT_STATUS_OBJECT_NAME_NOT_FOUND ) ;
return ;
}
/*
* smb_fname - > fsp may be NULL if smb_fname points at a symlink
* and we ' re in POSIX context , so be careful when using fsp
* below , it can still be NULL .
*/
fsp = smb_fname - > fsp ;
/* If this is a stream, check if there is a delete_pending. */
if ( ( conn - > fs_capabilities & FILE_NAMED_STREAMS )
& & is_ntfs_stream_smb_fname ( smb_fname ) ) {
struct smb_filename * smb_fname_base ;
/* Create an smb_filename with stream_name == NULL. */
smb_fname_base = synthetic_smb_fname (
talloc_tos ( ) ,
smb_fname - > base_name ,
NULL ,
NULL ,
smb_fname - > twrp ,
smb_fname - > flags ) ;
if ( smb_fname_base = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
ret = vfs_stat ( conn , smb_fname_base ) ;
if ( ret ! = 0 ) {
DBG_NOTICE ( " vfs_stat of %s failed "
" (%s) \n " ,
smb_fname_str_dbg ( smb_fname_base ) ,
strerror ( errno ) ) ;
TALLOC_FREE ( smb_fname_base ) ;
reply_nterror ( req ,
map_nt_error_from_unix ( errno ) ) ;
return ;
}
status = file_name_hash ( conn ,
smb_fname_str_dbg ( smb_fname_base ) ,
& name_hash ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( smb_fname_base ) ;
reply_nterror ( req , status ) ;
return ;
}
fileid = vfs_file_id_from_sbuf ( conn ,
& smb_fname_base - > st ) ;
TALLOC_FREE ( smb_fname_base ) ;
get_file_infos ( fileid , name_hash , & delete_pending , NULL ) ;
if ( delete_pending ) {
reply_nterror ( req , NT_STATUS_DELETE_PENDING ) ;
return ;
}
}
status = file_name_hash ( conn ,
smb_fname_str_dbg ( smb_fname ) ,
& name_hash ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
reply_nterror ( req , status ) ;
return ;
}
if ( lp_smbd_getinfo_ask_sharemode ( SNUM ( conn ) ) ) {
fileid = vfs_file_id_from_sbuf ( conn , & smb_fname - > st ) ;
get_file_infos ( fileid , name_hash , & delete_pending ,
& write_time_ts ) ;
}
if ( delete_pending ) {
reply_nterror ( req , NT_STATUS_DELETE_PENDING ) ;
return ;
}
}
DEBUG ( 3 , ( " call_trans2qfilepathinfo %s (%s) level=%d call=%d "
" total_data=%d \n " , smb_fname_str_dbg ( smb_fname ) ,
fsp_fnum_dbg ( fsp ) ,
info_level , tran_call , total_data ) ) ;
/* Pull out any data sent here before we realloc. */
switch ( info_level ) {
case SMB_INFO_QUERY_EAS_FROM_LIST :
{
/* Pull any EA list from the data portion. */
uint32_t ea_size ;
if ( total_data < 4 ) {
reply_nterror (
req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
ea_size = IVAL ( pdata , 0 ) ;
if ( total_data > 0 & & ea_size ! = total_data ) {
DEBUG ( 4 , ( " call_trans2qfilepathinfo: Rejecting EA request with incorrect \
total_data = % u ( should be % u ) \ n " , (unsigned int)total_data, (unsigned int)IVAL(pdata,0) ));
reply_nterror (
req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
if ( ! lp_ea_support ( SNUM ( conn ) ) ) {
reply_nterror ( req , NT_STATUS_EAS_NOT_SUPPORTED ) ;
return ;
}
/* Pull out the list of names. */
ea_list = read_ea_name_list ( req , pdata + 4 , ea_size - 4 ) ;
if ( ! ea_list ) {
reply_nterror (
req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
break ;
}
case SMB_QUERY_POSIX_LOCK :
{
if ( fsp = = NULL | |
fsp - > fsp_flags . is_pathref | |
fsp_get_io_fd ( fsp ) = = - 1 )
{
reply_nterror ( req , NT_STATUS_INVALID_HANDLE ) ;
return ;
}
if ( total_data ! = POSIX_LOCK_DATA_SIZE ) {
reply_nterror (
req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
/* Copy the lock range data. */
lock_data = ( char * ) talloc_memdup (
req , pdata , total_data ) ;
if ( ! lock_data ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
lock_data_count = total_data ;
break ;
}
default :
break ;
}
* pparams = ( char * ) SMB_REALLOC ( * pparams , 2 ) ;
if ( * pparams = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
params = * pparams ;
SSVAL ( params , 0 , 0 ) ;
/*
* draft - leach - cifs - v1 - spec - 02. txt
* 4.2 .14 TRANS2_QUERY_PATH_INFORMATION : Get File Attributes given Path
* says :
*
* The requested information is placed in the Data portion of the
* transaction response . For the information levels greater than 0x100 ,
* the transaction response has 1 parameter word which should be
* ignored by the client .
*
* However Windows only follows this rule for the IS_NAME_VALID call .
*/
switch ( info_level ) {
case SMB_INFO_IS_NAME_VALID :
param_size = 0 ;
break ;
}
if ( ( info_level & SMB2_INFO_SPECIAL ) = = SMB2_INFO_SPECIAL ) {
/*
* We use levels that start with 0xFF00
* internally to represent SMB2 specific levels
*/
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
status = smbd_do_qfilepathinfo ( conn , req , req , info_level ,
fsp , smb_fname ,
delete_pending , write_time_ts ,
ea_list ,
lock_data_count , lock_data ,
req - > flags2 , max_data_bytes ,
& fixed_portion ,
ppdata , & data_size ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
if ( open_was_deferred ( req - > xconn , req - > mid ) ) {
/* We have re-scheduled this call. */
return ;
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_SHARING_VIOLATION ) ) {
bool ok = defer_smb1_sharing_violation ( req ) ;
if ( ok ) {
return ;
}
}
reply_nterror ( req , status ) ;
return ;
}
if ( fixed_portion > max_data_bytes ) {
reply_nterror ( req , NT_STATUS_INFO_LENGTH_MISMATCH ) ;
return ;
}
send_trans2_replies ( conn , req , NT_STATUS_OK , params , param_size , * ppdata , data_size ,
max_data_bytes ) ;
return ;
}
/****************************************************************************
Reply to a TRANS2_SETFILEINFO ( set file info by fileid or pathname ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void call_trans2setfilepathinfo ( connection_struct * conn ,
struct smb_request * req ,
unsigned int tran_call ,
char * * pparams , int total_params ,
char * * ppdata , int total_data ,
unsigned int max_data_bytes )
{
char * params = * pparams ;
char * pdata = * ppdata ;
uint16_t info_level ;
struct smb_filename * smb_fname = NULL ;
2022-07-28 20:41:25 +03:00
struct files_struct * dirfsp = NULL ;
2022-03-21 21:29:26 +03:00
files_struct * fsp = NULL ;
NTSTATUS status = NT_STATUS_OK ;
int data_return_size = 0 ;
int ret ;
if ( ! params ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
if ( tran_call = = TRANSACT2_SETFILEINFO ) {
if ( total_params < 4 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
fsp = file_fsp ( req , SVAL ( params , 0 ) ) ;
/* Basic check for non-null fsp. */
if ( ! check_fsp_open ( conn , req , fsp ) ) {
return ;
}
info_level = SVAL ( params , 2 ) ;
if ( INFO_LEVEL_IS_UNIX ( info_level ) ) {
if ( ! lp_smb1_unix_extensions ( ) ) {
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
if ( ! req - > posix_pathnames ) {
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
}
smb_fname = fsp - > fsp_name ;
if ( fsp_get_pathref_fd ( fsp ) = = - 1 ) {
/*
* This is actually a SETFILEINFO on a directory
* handle ( returned from an NT SMB ) . NT5 .0 seems
* to do this call . JRA .
*/
ret = vfs_stat ( conn , smb_fname ) ;
if ( ret ! = 0 ) {
DBG_NOTICE ( " vfs_stat of %s failed (%s) \n " ,
smb_fname_str_dbg ( smb_fname ) ,
strerror ( errno ) ) ;
reply_nterror ( req ,
map_nt_error_from_unix ( errno ) ) ;
return ;
}
} else if ( fsp - > print_file ) {
/*
* Doing a DELETE_ON_CLOSE should cancel a print job .
*/
if ( ( info_level = = SMB_SET_FILE_DISPOSITION_INFO ) & & CVAL ( pdata , 0 ) ) {
2022-05-19 21:05:52 +03:00
fsp - > fsp_flags . delete_on_close = true ;
2022-03-21 21:29:26 +03:00
DEBUG ( 3 , ( " call_trans2setfilepathinfo: "
" Cancelling print job (%s) \n " ,
fsp_str_dbg ( fsp ) ) ) ;
SSVAL ( params , 0 , 0 ) ;
send_trans2_replies ( conn , req , NT_STATUS_OK , params , 2 ,
* ppdata , 0 ,
max_data_bytes ) ;
return ;
} else {
reply_nterror ( req ,
NT_STATUS_OBJECT_PATH_NOT_FOUND ) ;
return ;
}
} else {
/*
* Original code - this is an open file .
*/
status = vfs_stat_fsp ( fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 3 , ( " call_trans2setfilepathinfo: fstat "
" of %s failed (%s) \n " , fsp_fnum_dbg ( fsp ) ,
nt_errstr ( status ) ) ) ;
reply_nterror ( req , status ) ;
return ;
}
}
} else {
char * fname = NULL ;
uint32_t ucf_flags = ucf_flags_from_smb_request ( req ) ;
bool require_existing_object = true ;
2022-07-28 20:41:25 +03:00
NTTIME twrp = 0 ;
2022-03-21 21:29:26 +03:00
/* set path info */
if ( total_params < 7 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
info_level = SVAL ( params , 0 ) ;
if ( INFO_LEVEL_IS_UNIX ( info_level ) ) {
if ( ! lp_smb1_unix_extensions ( ) ) {
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
if ( ! req - > posix_pathnames ) {
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
}
if ( req - > posix_pathnames ) {
srvstr_get_path_posix ( req ,
params ,
req - > flags2 ,
& fname ,
& params [ 6 ] ,
total_params - 6 ,
STR_TERMINATE ,
& status ) ;
} else {
srvstr_get_path ( req ,
params ,
req - > flags2 ,
& fname ,
& params [ 6 ] ,
total_params - 6 ,
STR_TERMINATE ,
& status ) ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
reply_nterror ( req , status ) ;
return ;
}
2022-07-28 20:41:25 +03:00
if ( ucf_flags & UCF_GMT_PATHNAME ) {
2022-08-04 20:02:15 +03:00
extract_snapshot_token ( fname , & twrp ) ;
2022-07-28 20:41:25 +03:00
}
status = filename_convert_dirfsp ( req ,
conn ,
fname ,
ucf_flags ,
twrp ,
& dirfsp ,
& smb_fname ) ;
2022-03-21 21:29:26 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
if ( NT_STATUS_EQUAL ( status , NT_STATUS_PATH_NOT_COVERED ) ) {
reply_botherror ( req ,
NT_STATUS_PATH_NOT_COVERED ,
ERRSRV , ERRbadpath ) ;
return ;
}
reply_nterror ( req , status ) ;
return ;
}
/*
* smb_fname - > fsp may be NULL if smb_fname points at a symlink
* and we ' re in POSIX context , so be careful when using fsp
* below , it can still be NULL .
*/
fsp = smb_fname - > fsp ;
/*
* There are 4 info levels which can
* create a new object in the filesystem .
* They are :
* SMB_SET_FILE_UNIX_LINK - > creates POSIX symlink .
* SMB_POSIX_PATH_OPEN - > creates POSIX file or directory .
* SMB_SET_FILE_UNIX_BASIC :
* SMB_SET_FILE_UNIX_INFO2 : can create a POSIX special file .
*
* These info levels do not require an existing object .
*/
switch ( info_level ) {
case SMB_SET_FILE_UNIX_LINK :
case SMB_POSIX_PATH_OPEN :
case SMB_SET_FILE_UNIX_BASIC :
case SMB_SET_FILE_UNIX_INFO2 :
require_existing_object = false ;
break ;
default :
break ;
}
if ( ! VALID_STAT ( smb_fname - > st ) & & require_existing_object ) {
reply_nterror ( req , NT_STATUS_OBJECT_NAME_NOT_FOUND ) ;
}
}
DEBUG ( 3 , ( " call_trans2setfilepathinfo(%d) %s (%s) info_level=%d "
" totdata=%d \n " , tran_call , smb_fname_str_dbg ( smb_fname ) ,
fsp_fnum_dbg ( fsp ) ,
info_level , total_data ) ) ;
/* Realloc the parameter size */
* pparams = ( char * ) SMB_REALLOC ( * pparams , 2 ) ;
if ( * pparams = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
params = * pparams ;
SSVAL ( params , 0 , 0 ) ;
status = smbd_do_setfilepathinfo ( conn , req , req ,
info_level ,
fsp ,
smb_fname ,
ppdata , total_data ,
& data_return_size ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
if ( open_was_deferred ( req - > xconn , req - > mid ) ) {
/* We have re-scheduled this call. */
return ;
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_SHARING_VIOLATION ) ) {
bool ok = defer_smb1_sharing_violation ( req ) ;
if ( ok ) {
return ;
}
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_EVENT_PENDING ) ) {
/* We have re-scheduled this call. */
return ;
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_PATH_NOT_COVERED ) ) {
reply_botherror ( req , NT_STATUS_PATH_NOT_COVERED ,
ERRSRV , ERRbadpath ) ;
return ;
}
if ( info_level = = SMB_POSIX_PATH_OPEN ) {
reply_openerror ( req , status ) ;
return ;
}
/*
* Invalid EA name needs to return 2 param bytes ,
* not a zero - length error packet .
*/
if ( NT_STATUS_EQUAL ( status , STATUS_INVALID_EA_NAME ) ) {
send_trans2_replies ( conn , req , status , params , 2 , NULL , 0 ,
max_data_bytes ) ;
} else {
reply_nterror ( req , status ) ;
}
return ;
}
send_trans2_replies ( conn , req , NT_STATUS_OK , params , 2 , * ppdata , data_return_size ,
max_data_bytes ) ;
return ;
}
/****************************************************************************
Reply to a TRANS2_MKDIR ( make directory with extended attributes ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void call_trans2mkdir ( connection_struct * conn , struct smb_request * req ,
char * * pparams , int total_params ,
char * * ppdata , int total_data ,
unsigned int max_data_bytes )
{
2022-07-27 22:39:11 +03:00
struct files_struct * dirfsp = NULL ;
2022-03-21 21:29:26 +03:00
struct files_struct * fsp = NULL ;
struct smb_filename * smb_dname = NULL ;
char * params = * pparams ;
char * pdata = * ppdata ;
char * directory = NULL ;
NTSTATUS status = NT_STATUS_OK ;
struct ea_list * ea_list = NULL ;
uint32_t ucf_flags = ucf_flags_from_smb_request ( req ) ;
2022-07-27 22:39:11 +03:00
NTTIME twrp = 0 ;
2022-03-21 21:29:26 +03:00
TALLOC_CTX * ctx = talloc_tos ( ) ;
if ( ! CAN_WRITE ( conn ) ) {
reply_nterror ( req , NT_STATUS_ACCESS_DENIED ) ;
return ;
}
if ( total_params < 5 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
if ( req - > posix_pathnames ) {
srvstr_get_path_posix ( ctx ,
params ,
req - > flags2 ,
& directory ,
& params [ 4 ] ,
total_params - 4 ,
STR_TERMINATE ,
& status ) ;
} else {
srvstr_get_path ( ctx ,
params ,
req - > flags2 ,
& directory ,
& params [ 4 ] ,
total_params - 4 ,
STR_TERMINATE ,
& status ) ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
reply_nterror ( req , status ) ;
return ;
}
DEBUG ( 3 , ( " call_trans2mkdir : name = %s \n " , directory ) ) ;
2022-07-27 22:39:11 +03:00
if ( ucf_flags & UCF_GMT_PATHNAME ) {
2022-08-04 20:02:15 +03:00
extract_snapshot_token ( directory , & twrp ) ;
2022-07-27 22:39:11 +03:00
}
status = filename_convert_dirfsp ( ctx ,
conn ,
directory ,
ucf_flags ,
twrp ,
& dirfsp ,
& smb_dname ) ;
2022-03-21 21:29:26 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
if ( NT_STATUS_EQUAL ( status , NT_STATUS_PATH_NOT_COVERED ) ) {
reply_botherror ( req ,
NT_STATUS_PATH_NOT_COVERED ,
ERRSRV , ERRbadpath ) ;
return ;
}
reply_nterror ( req , status ) ;
return ;
}
/*
* OS / 2 workplace shell seems to send SET_EA requests of " null "
* length ( 4 bytes containing IVAL 4 ) .
* They seem to have no effect . Bug # 3212. JRA .
*/
if ( total_data & & ( total_data ! = 4 ) ) {
/* Any data in this call is an EA list. */
if ( total_data < 10 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
goto out ;
}
if ( IVAL ( pdata , 0 ) > total_data ) {
DEBUG ( 10 , ( " call_trans2mkdir: bad total data size (%u) > %u \n " ,
IVAL ( pdata , 0 ) , ( unsigned int ) total_data ) ) ;
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
goto out ;
}
ea_list = read_ea_list ( talloc_tos ( ) , pdata + 4 ,
total_data - 4 ) ;
if ( ! ea_list ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
goto out ;
}
if ( ! lp_ea_support ( SNUM ( conn ) ) ) {
reply_nterror ( req , NT_STATUS_EAS_NOT_SUPPORTED ) ;
goto out ;
}
}
/* If total_data == 4 Windows doesn't care what values
* are placed in that field , it just ignores them .
* The System i QNTC IBM SMB client puts bad values here ,
* so ignore them . */
status = SMB_VFS_CREATE_FILE (
conn , /* conn */
req , /* req */
2022-07-27 22:39:11 +03:00
dirfsp , /* dirfsp */
2022-03-21 21:29:26 +03:00
smb_dname , /* fname */
MAXIMUM_ALLOWED_ACCESS , /* access_mask */
FILE_SHARE_NONE , /* share_access */
FILE_CREATE , /* create_disposition*/
FILE_DIRECTORY_FILE , /* create_options */
FILE_ATTRIBUTE_DIRECTORY , /* file_attributes */
0 , /* oplock_request */
NULL , /* lease */
0 , /* allocation_size */
0 , /* private_flags */
NULL , /* sd */
NULL , /* ea_list */
& fsp , /* result */
NULL , /* pinfo */
NULL , NULL ) ; /* create context */
if ( ! NT_STATUS_IS_OK ( status ) ) {
reply_nterror ( req , status ) ;
goto out ;
}
/* Try and set any given EA. */
if ( ea_list ) {
status = set_ea ( conn , fsp , ea_list ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
reply_nterror ( req , status ) ;
goto out ;
}
}
/* Realloc the parameter and data sizes */
* pparams = ( char * ) SMB_REALLOC ( * pparams , 2 ) ;
if ( * pparams = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
goto out ;
}
params = * pparams ;
SSVAL ( params , 0 , 0 ) ;
send_trans2_replies ( conn , req , NT_STATUS_OK , params , 2 , * ppdata , 0 , max_data_bytes ) ;
out :
if ( fsp ! = NULL ) {
close_file_free ( NULL , & fsp , NORMAL_CLOSE ) ;
}
TALLOC_FREE ( smb_dname ) ;
return ;
}
/****************************************************************************
Reply to a TRANS2_FINDNOTIFYFIRST ( start monitoring a directory for changes ) .
We don ' t actually do this - we just send a null response .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void call_trans2findnotifyfirst ( connection_struct * conn ,
struct smb_request * req ,
char * * pparams , int total_params ,
char * * ppdata , int total_data ,
unsigned int max_data_bytes )
{
char * params = * pparams ;
uint16_t info_level ;
if ( total_params < 6 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
info_level = SVAL ( params , 4 ) ;
DEBUG ( 3 , ( " call_trans2findnotifyfirst - info_level %d \n " , info_level ) ) ;
switch ( info_level ) {
case 1 :
case 2 :
break ;
default :
reply_nterror ( req , NT_STATUS_INVALID_LEVEL ) ;
return ;
}
/* Realloc the parameter and data sizes */
* pparams = ( char * ) SMB_REALLOC ( * pparams , 6 ) ;
if ( * pparams = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
params = * pparams ;
SSVAL ( params , 0 , fnf_handle ) ;
SSVAL ( params , 2 , 0 ) ; /* No changes */
SSVAL ( params , 4 , 0 ) ; /* No EA errors */
fnf_handle + + ;
if ( fnf_handle = = 0 )
fnf_handle = 257 ;
send_trans2_replies ( conn , req , NT_STATUS_OK , params , 6 , * ppdata , 0 , max_data_bytes ) ;
return ;
}
/****************************************************************************
Reply to a TRANS2_FINDNOTIFYNEXT ( continue monitoring a directory for
changes ) . Currently this does nothing .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void call_trans2findnotifynext ( connection_struct * conn ,
struct smb_request * req ,
char * * pparams , int total_params ,
char * * ppdata , int total_data ,
unsigned int max_data_bytes )
{
char * params = * pparams ;
DEBUG ( 3 , ( " call_trans2findnotifynext \n " ) ) ;
/* Realloc the parameter and data sizes */
* pparams = ( char * ) SMB_REALLOC ( * pparams , 4 ) ;
if ( * pparams = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
params = * pparams ;
SSVAL ( params , 0 , 0 ) ; /* No changes */
SSVAL ( params , 2 , 0 ) ; /* No EA errors */
send_trans2_replies ( conn , req , NT_STATUS_OK , params , 4 , * ppdata , 0 , max_data_bytes ) ;
return ;
}
/****************************************************************************
Reply to a TRANS2_GET_DFS_REFERRAL - Shirish Kalele < kalele @ veritas . com > .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void call_trans2getdfsreferral ( connection_struct * conn ,
struct smb_request * req ,
char * * pparams , int total_params ,
char * * ppdata , int total_data ,
unsigned int max_data_bytes )
{
char * params = * pparams ;
char * pathname = NULL ;
int reply_size = 0 ;
int max_referral_level ;
NTSTATUS status = NT_STATUS_OK ;
TALLOC_CTX * ctx = talloc_tos ( ) ;
DEBUG ( 10 , ( " call_trans2getdfsreferral \n " ) ) ;
2022-08-28 12:12:52 +03:00
if ( ! IS_IPC ( conn ) ) {
reply_nterror ( req , NT_STATUS_ACCESS_DENIED ) ;
return ;
}
2022-03-21 21:29:26 +03:00
if ( total_params < 3 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return ;
}
max_referral_level = SVAL ( params , 0 ) ;
if ( ! lp_host_msdfs ( ) ) {
reply_nterror ( req , NT_STATUS_NOT_IMPLEMENTED ) ;
return ;
}
srvstr_pull_talloc ( ctx , params , req - > flags2 , & pathname , & params [ 2 ] ,
total_params - 2 , STR_TERMINATE ) ;
if ( ! pathname ) {
reply_nterror ( req , NT_STATUS_NOT_FOUND ) ;
return ;
}
2022-08-09 15:51:10 +03:00
reply_size = setup_dfs_referral (
conn , pathname , max_referral_level , ppdata , & status ) ;
if ( reply_size < 0 ) {
2022-03-21 21:29:26 +03:00
reply_nterror ( req , status ) ;
return ;
}
SSVAL ( ( discard_const_p ( uint8_t , req - > inbuf ) ) , smb_flg2 ,
SVAL ( req - > inbuf , smb_flg2 ) | FLAGS2_DFS_PATHNAMES ) ;
send_trans2_replies ( conn , req , NT_STATUS_OK , 0 , 0 , * ppdata , reply_size , max_data_bytes ) ;
return ;
}
# define LMCAT_SPL 0x53
# define LMFUNC_GETJOBID 0x60
/****************************************************************************
Reply to a TRANS2_IOCTL - used for OS / 2 printing .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void call_trans2ioctl ( connection_struct * conn ,
struct smb_request * req ,
char * * pparams , int total_params ,
char * * ppdata , int total_data ,
unsigned int max_data_bytes )
{
const struct loadparm_substitution * lp_sub =
loadparm_s3_global_substitution ( ) ;
char * pdata = * ppdata ;
files_struct * fsp = file_fsp ( req , SVAL ( req - > vwv + 15 , 0 ) ) ;
NTSTATUS status ;
size_t len = 0 ;
/* check for an invalid fid before proceeding */
if ( ! fsp ) {
reply_nterror ( req , NT_STATUS_INVALID_HANDLE ) ;
return ;
}
if ( ( SVAL ( req - > vwv + 16 , 0 ) = = LMCAT_SPL )
& & ( SVAL ( req - > vwv + 17 , 0 ) = = LMFUNC_GETJOBID ) ) {
* ppdata = ( char * ) SMB_REALLOC ( * ppdata , 32 ) ;
if ( * ppdata = = NULL ) {
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
pdata = * ppdata ;
/* NOTE - THIS IS ASCII ONLY AT THE MOMENT - NOT SURE IF OS/2
CAN ACCEPT THIS IN UNICODE . JRA . */
/* Job number */
SSVAL ( pdata , 0 , print_spool_rap_jobid ( fsp - > print_file ) ) ;
status = srvstr_push ( pdata , req - > flags2 , pdata + 2 ,
lp_netbios_name ( ) , 15 ,
STR_ASCII | STR_TERMINATE , & len ) ; /* Our NetBIOS name */
if ( ! NT_STATUS_IS_OK ( status ) ) {
reply_nterror ( req , status ) ;
return ;
}
status = srvstr_push ( pdata , req - > flags2 , pdata + 18 ,
lp_servicename ( talloc_tos ( ) , lp_sub , SNUM ( conn ) ) , 13 ,
STR_ASCII | STR_TERMINATE , & len ) ; /* Service name */
if ( ! NT_STATUS_IS_OK ( status ) ) {
reply_nterror ( req , status ) ;
return ;
}
send_trans2_replies ( conn , req , NT_STATUS_OK , * pparams , 0 , * ppdata , 32 ,
max_data_bytes ) ;
return ;
}
DEBUG ( 2 , ( " Unknown TRANS2_IOCTL \n " ) ) ;
reply_nterror ( req , NT_STATUS_NOT_IMPLEMENTED ) ;
}
2022-03-21 22:15:19 +03:00
static void handle_trans2 ( connection_struct * conn , struct smb_request * req ,
struct trans_state * state )
2022-03-21 21:29:26 +03:00
{
if ( get_Protocol ( ) > = PROTOCOL_NT1 ) {
req - > flags2 | = 0x40 ; /* IS_LONG_NAME */
SSVAL ( ( discard_const_p ( uint8_t , req - > inbuf ) ) , smb_flg2 , req - > flags2 ) ;
}
if ( ENCRYPTION_REQUIRED ( conn ) & & ! req - > encrypted ) {
if ( state - > call ! = TRANSACT2_QFSINFO & &
state - > call ! = TRANSACT2_SETFSINFO ) {
DEBUG ( 0 , ( " handle_trans2: encryption required "
" with call 0x%x \n " ,
( unsigned int ) state - > call ) ) ;
reply_nterror ( req , NT_STATUS_ACCESS_DENIED ) ;
return ;
}
}
SMB_PERFCOUNT_SET_SUBOP ( & req - > pcd , state - > call ) ;
/* Now we must call the relevant TRANS2 function */
switch ( state - > call ) {
case TRANSACT2_OPEN :
{
START_PROFILE ( Trans2_open ) ;
call_trans2open ( conn , req ,
& state - > param , state - > total_param ,
& state - > data , state - > total_data ,
state - > max_data_return ) ;
END_PROFILE ( Trans2_open ) ;
break ;
}
case TRANSACT2_FINDFIRST :
{
START_PROFILE ( Trans2_findfirst ) ;
call_trans2findfirst ( conn , req ,
& state - > param , state - > total_param ,
& state - > data , state - > total_data ,
state - > max_data_return ) ;
END_PROFILE ( Trans2_findfirst ) ;
break ;
}
case TRANSACT2_FINDNEXT :
{
START_PROFILE ( Trans2_findnext ) ;
call_trans2findnext ( conn , req ,
& state - > param , state - > total_param ,
& state - > data , state - > total_data ,
state - > max_data_return ) ;
END_PROFILE ( Trans2_findnext ) ;
break ;
}
case TRANSACT2_QFSINFO :
{
START_PROFILE ( Trans2_qfsinfo ) ;
call_trans2qfsinfo ( conn , req ,
& state - > param , state - > total_param ,
& state - > data , state - > total_data ,
state - > max_data_return ) ;
END_PROFILE ( Trans2_qfsinfo ) ;
break ;
}
case TRANSACT2_SETFSINFO :
{
START_PROFILE ( Trans2_setfsinfo ) ;
call_trans2setfsinfo ( conn , req ,
& state - > param , state - > total_param ,
& state - > data , state - > total_data ,
state - > max_data_return ) ;
END_PROFILE ( Trans2_setfsinfo ) ;
break ;
}
case TRANSACT2_QPATHINFO :
case TRANSACT2_QFILEINFO :
{
START_PROFILE ( Trans2_qpathinfo ) ;
call_trans2qfilepathinfo ( conn , req , state - > call ,
& state - > param , state - > total_param ,
& state - > data , state - > total_data ,
state - > max_data_return ) ;
END_PROFILE ( Trans2_qpathinfo ) ;
break ;
}
case TRANSACT2_SETPATHINFO :
case TRANSACT2_SETFILEINFO :
{
START_PROFILE ( Trans2_setpathinfo ) ;
call_trans2setfilepathinfo ( conn , req , state - > call ,
& state - > param , state - > total_param ,
& state - > data , state - > total_data ,
state - > max_data_return ) ;
END_PROFILE ( Trans2_setpathinfo ) ;
break ;
}
case TRANSACT2_FINDNOTIFYFIRST :
{
START_PROFILE ( Trans2_findnotifyfirst ) ;
call_trans2findnotifyfirst ( conn , req ,
& state - > param , state - > total_param ,
& state - > data , state - > total_data ,
state - > max_data_return ) ;
END_PROFILE ( Trans2_findnotifyfirst ) ;
break ;
}
case TRANSACT2_FINDNOTIFYNEXT :
{
START_PROFILE ( Trans2_findnotifynext ) ;
call_trans2findnotifynext ( conn , req ,
& state - > param , state - > total_param ,
& state - > data , state - > total_data ,
state - > max_data_return ) ;
END_PROFILE ( Trans2_findnotifynext ) ;
break ;
}
case TRANSACT2_MKDIR :
{
START_PROFILE ( Trans2_mkdir ) ;
call_trans2mkdir ( conn , req ,
& state - > param , state - > total_param ,
& state - > data , state - > total_data ,
state - > max_data_return ) ;
END_PROFILE ( Trans2_mkdir ) ;
break ;
}
case TRANSACT2_GET_DFS_REFERRAL :
{
START_PROFILE ( Trans2_get_dfs_referral ) ;
call_trans2getdfsreferral ( conn , req ,
& state - > param , state - > total_param ,
& state - > data , state - > total_data ,
state - > max_data_return ) ;
END_PROFILE ( Trans2_get_dfs_referral ) ;
break ;
}
case TRANSACT2_IOCTL :
{
START_PROFILE ( Trans2_ioctl ) ;
call_trans2ioctl ( conn , req ,
& state - > param , state - > total_param ,
& state - > data , state - > total_data ,
state - > max_data_return ) ;
END_PROFILE ( Trans2_ioctl ) ;
break ;
}
default :
/* Error in request */
DEBUG ( 2 , ( " Unknown request %d in trans2 call \n " , state - > call ) ) ;
reply_nterror ( req , NT_STATUS_NOT_IMPLEMENTED ) ;
}
}
/****************************************************************************
Reply to a SMBtrans2 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void reply_trans2 ( struct smb_request * req )
{
connection_struct * conn = req - > conn ;
unsigned int dsoff ;
unsigned int dscnt ;
unsigned int psoff ;
unsigned int pscnt ;
unsigned int tran_call ;
struct trans_state * state ;
NTSTATUS result ;
START_PROFILE ( SMBtrans2 ) ;
if ( req - > wct < 14 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
END_PROFILE ( SMBtrans2 ) ;
return ;
}
dsoff = SVAL ( req - > vwv + 12 , 0 ) ;
dscnt = SVAL ( req - > vwv + 11 , 0 ) ;
psoff = SVAL ( req - > vwv + 10 , 0 ) ;
pscnt = SVAL ( req - > vwv + 9 , 0 ) ;
tran_call = SVAL ( req - > vwv + 14 , 0 ) ;
result = allow_new_trans ( conn - > pending_trans , req - > mid ) ;
if ( ! NT_STATUS_IS_OK ( result ) ) {
DEBUG ( 2 , ( " Got invalid trans2 request: %s \n " ,
nt_errstr ( result ) ) ) ;
reply_nterror ( req , result ) ;
END_PROFILE ( SMBtrans2 ) ;
return ;
}
if ( IS_IPC ( conn ) ) {
switch ( tran_call ) {
/* List the allowed trans2 calls on IPC$ */
case TRANSACT2_OPEN :
case TRANSACT2_GET_DFS_REFERRAL :
case TRANSACT2_QFILEINFO :
case TRANSACT2_QFSINFO :
case TRANSACT2_SETFSINFO :
break ;
default :
reply_nterror ( req , NT_STATUS_ACCESS_DENIED ) ;
END_PROFILE ( SMBtrans2 ) ;
return ;
}
}
if ( ( state = talloc ( conn , struct trans_state ) ) = = NULL ) {
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
END_PROFILE ( SMBtrans2 ) ;
return ;
}
state - > cmd = SMBtrans2 ;
state - > mid = req - > mid ;
state - > vuid = req - > vuid ;
state - > setup_count = SVAL ( req - > vwv + 13 , 0 ) ;
state - > setup = NULL ;
state - > total_param = SVAL ( req - > vwv + 0 , 0 ) ;
state - > param = NULL ;
state - > total_data = SVAL ( req - > vwv + 1 , 0 ) ;
state - > data = NULL ;
state - > max_param_return = SVAL ( req - > vwv + 2 , 0 ) ;
state - > max_data_return = SVAL ( req - > vwv + 3 , 0 ) ;
state - > max_setup_return = SVAL ( req - > vwv + 4 , 0 ) ;
state - > close_on_completion = BITSETW ( req - > vwv + 5 , 0 ) ;
state - > one_way = BITSETW ( req - > vwv + 5 , 1 ) ;
state - > call = tran_call ;
/* All trans2 messages we handle have smb_sucnt == 1 - ensure this
is so as a sanity check */
if ( state - > setup_count ! = 1 ) {
/*
* Need to have rc = 0 for ioctl to get job id for OS / 2.
* Network printing will fail if function is not successful .
* Similar function in reply . c will be used if protocol
* is LANMAN1 .0 instead of LM1 .2 X002 .
* Until DosPrintSetJobInfo with PRJINFO3 is supported ,
* outbuf doesn ' t have to be set ( only job id is used ) .
*/
if ( ( state - > setup_count = = 4 )
& & ( tran_call = = TRANSACT2_IOCTL )
& & ( SVAL ( req - > vwv + 16 , 0 ) = = LMCAT_SPL )
& & ( SVAL ( req - > vwv + 17 , 0 ) = = LMFUNC_GETJOBID ) ) {
DEBUG ( 2 , ( " Got Trans2 DevIOctl jobid \n " ) ) ;
} else {
DEBUG ( 2 , ( " Invalid smb_sucnt in trans2 call(%u) \n " , state - > setup_count ) ) ;
DEBUG ( 2 , ( " Transaction is %d \n " , tran_call ) ) ;
TALLOC_FREE ( state ) ;
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
END_PROFILE ( SMBtrans2 ) ;
return ;
}
}
if ( ( dscnt > state - > total_data ) | | ( pscnt > state - > total_param ) )
goto bad_param ;
if ( state - > total_data ) {
if ( smb_buffer_oob ( state - > total_data , 0 , dscnt )
| | smb_buffer_oob ( smb_len ( req - > inbuf ) , dsoff , dscnt ) ) {
goto bad_param ;
}
/* Can't use talloc here, the core routines do realloc on the
* params and data . */
state - > data = ( char * ) SMB_MALLOC ( state - > total_data ) ;
if ( state - > data = = NULL ) {
DEBUG ( 0 , ( " reply_trans2: data malloc fail for %u "
" bytes ! \n " , ( unsigned int ) state - > total_data ) ) ;
TALLOC_FREE ( state ) ;
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
END_PROFILE ( SMBtrans2 ) ;
return ;
}
memcpy ( state - > data , smb_base ( req - > inbuf ) + dsoff , dscnt ) ;
}
if ( state - > total_param ) {
if ( smb_buffer_oob ( state - > total_param , 0 , pscnt )
| | smb_buffer_oob ( smb_len ( req - > inbuf ) , psoff , pscnt ) ) {
goto bad_param ;
}
/* Can't use talloc here, the core routines do realloc on the
* params and data . */
state - > param = ( char * ) SMB_MALLOC ( state - > total_param ) ;
if ( state - > param = = NULL ) {
DEBUG ( 0 , ( " reply_trans: param malloc fail for %u "
" bytes ! \n " , ( unsigned int ) state - > total_param ) ) ;
SAFE_FREE ( state - > data ) ;
TALLOC_FREE ( state ) ;
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
END_PROFILE ( SMBtrans2 ) ;
return ;
}
memcpy ( state - > param , smb_base ( req - > inbuf ) + psoff , pscnt ) ;
}
state - > received_data = dscnt ;
state - > received_param = pscnt ;
if ( ( state - > received_param = = state - > total_param ) & &
( state - > received_data = = state - > total_data ) ) {
handle_trans2 ( conn , req , state ) ;
SAFE_FREE ( state - > data ) ;
SAFE_FREE ( state - > param ) ;
TALLOC_FREE ( state ) ;
END_PROFILE ( SMBtrans2 ) ;
return ;
}
DLIST_ADD ( conn - > pending_trans , state ) ;
/* We need to send an interim response then receive the rest
of the parameter / data bytes */
2022-04-05 05:53:20 +03:00
reply_smb1_outbuf ( req , 0 , 0 ) ;
2022-03-21 21:29:26 +03:00
show_msg ( ( char * ) req - > outbuf ) ;
END_PROFILE ( SMBtrans2 ) ;
return ;
bad_param :
DEBUG ( 0 , ( " reply_trans2: invalid trans parameters \n " ) ) ;
SAFE_FREE ( state - > data ) ;
SAFE_FREE ( state - > param ) ;
TALLOC_FREE ( state ) ;
END_PROFILE ( SMBtrans2 ) ;
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
2022-03-21 22:15:19 +03:00
/****************************************************************************
Reply to a SMBtranss2
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void reply_transs2 ( struct smb_request * req )
{
connection_struct * conn = req - > conn ;
unsigned int pcnt , poff , dcnt , doff , pdisp , ddisp ;
struct trans_state * state ;
START_PROFILE ( SMBtranss2 ) ;
show_msg ( ( const char * ) req - > inbuf ) ;
/* Windows clients expect all replies to
a transact secondary ( SMBtranss2 0x33 )
to have a command code of transact
( SMBtrans2 0x32 ) . See bug # 8989
and also [ MS - CIFS ] section 2.2 .4 .47 .2
for details .
*/
req - > cmd = SMBtrans2 ;
if ( req - > wct < 8 ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
END_PROFILE ( SMBtranss2 ) ;
return ;
}
for ( state = conn - > pending_trans ; state ! = NULL ;
state = state - > next ) {
if ( state - > mid = = req - > mid ) {
break ;
}
}
if ( ( state = = NULL ) | | ( state - > cmd ! = SMBtrans2 ) ) {
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
END_PROFILE ( SMBtranss2 ) ;
return ;
}
/* Revise state->total_param and state->total_data in case they have
changed downwards */
if ( SVAL ( req - > vwv + 0 , 0 ) < state - > total_param )
state - > total_param = SVAL ( req - > vwv + 0 , 0 ) ;
if ( SVAL ( req - > vwv + 1 , 0 ) < state - > total_data )
state - > total_data = SVAL ( req - > vwv + 1 , 0 ) ;
pcnt = SVAL ( req - > vwv + 2 , 0 ) ;
poff = SVAL ( req - > vwv + 3 , 0 ) ;
pdisp = SVAL ( req - > vwv + 4 , 0 ) ;
dcnt = SVAL ( req - > vwv + 5 , 0 ) ;
doff = SVAL ( req - > vwv + 6 , 0 ) ;
ddisp = SVAL ( req - > vwv + 7 , 0 ) ;
state - > received_param + = pcnt ;
state - > received_data + = dcnt ;
if ( ( state - > received_data > state - > total_data ) | |
( state - > received_param > state - > total_param ) )
goto bad_param ;
if ( pcnt ) {
if ( smb_buffer_oob ( state - > total_param , pdisp , pcnt )
| | smb_buffer_oob ( smb_len ( req - > inbuf ) , poff , pcnt ) ) {
goto bad_param ;
}
memcpy ( state - > param + pdisp , smb_base ( req - > inbuf ) + poff , pcnt ) ;
}
if ( dcnt ) {
if ( smb_buffer_oob ( state - > total_data , ddisp , dcnt )
| | smb_buffer_oob ( smb_len ( req - > inbuf ) , doff , dcnt ) ) {
goto bad_param ;
}
memcpy ( state - > data + ddisp , smb_base ( req - > inbuf ) + doff , dcnt ) ;
}
if ( ( state - > received_param < state - > total_param ) | |
( state - > received_data < state - > total_data ) ) {
END_PROFILE ( SMBtranss2 ) ;
return ;
}
handle_trans2 ( conn , req , state ) ;
DLIST_REMOVE ( conn - > pending_trans , state ) ;
SAFE_FREE ( state - > data ) ;
SAFE_FREE ( state - > param ) ;
TALLOC_FREE ( state ) ;
END_PROFILE ( SMBtranss2 ) ;
return ;
bad_param :
DEBUG ( 0 , ( " reply_transs2: invalid trans parameters \n " ) ) ;
DLIST_REMOVE ( conn - > pending_trans , state ) ;
SAFE_FREE ( state - > data ) ;
SAFE_FREE ( state - > param ) ;
TALLOC_FREE ( state ) ;
reply_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
END_PROFILE ( SMBtranss2 ) ;
return ;
}