2013-01-15 20:22:57 +04:00
/*
Unix SMB / CIFS implementation .
Core SMB2 server
Copyright ( C ) Stefan Metzmacher 2009
2013-01-15 20:23:00 +04:00
Copyright ( C ) David Disseldorp 2012
2013-01-15 20:22:57 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
# include "smbd/smbd.h"
# include "smbd/globals.h"
# include "../libcli/smb/smb_common.h"
2013-01-15 20:23:10 +04:00
# include "../libcli/security/security.h"
2013-01-15 20:22:57 +04:00
# include "../lib/util/tevent_ntstatus.h"
# include "include/ntioctl.h"
# include "../librpc/ndr/libndr.h"
2013-01-15 20:22:58 +04:00
# include "librpc/gen_ndr/ndr_ioctl.h"
2013-01-15 20:22:57 +04:00
# include "smb2_ioctl_private.h"
2014-06-13 19:42:00 +04:00
# include "../lib/tsocket/tsocket.h"
2020-06-25 16:32:11 +03:00
# include "lib/messages_ctdb.h"
# include "ctdbd_conn.h"
2013-01-15 20:22:57 +04:00
2018-03-21 22:01:05 +03:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_SMB2
2013-01-15 20:23:00 +04:00
static void copychunk_pack_limits ( struct srv_copychunk_rsp * cc_rsp )
{
cc_rsp - > chunks_written = COPYCHUNK_MAX_CHUNKS ;
cc_rsp - > chunk_bytes_written = COPYCHUNK_MAX_CHUNK_LEN ;
cc_rsp - > total_bytes_written = COPYCHUNK_MAX_TOTAL_LEN ;
}
static NTSTATUS copychunk_check_limits ( struct srv_copychunk_copy * cc_copy )
{
uint32_t i ;
uint32_t total_len = 0 ;
2014-02-06 23:12:21 +04:00
/*
* [ MS - SMB2 ] 3.3 .5 .15 .6 Handling a Server - Side Data Copy Request
* Send and invalid parameter response if :
* - The ChunkCount value is greater than
* ServerSideCopyMaxNumberofChunks
*/
2013-01-15 20:23:00 +04:00
if ( cc_copy - > chunk_count > COPYCHUNK_MAX_CHUNKS ) {
return NT_STATUS_INVALID_PARAMETER ;
}
for ( i = 0 ; i < cc_copy - > chunk_count ; i + + ) {
2014-02-06 23:12:21 +04:00
/*
* - The Length value in a single chunk is greater than
* ServerSideCopyMaxChunkSize or equal to zero .
*/
if ( ( cc_copy - > chunks [ i ] . length = = 0 )
| | ( cc_copy - > chunks [ i ] . length > COPYCHUNK_MAX_CHUNK_LEN ) ) {
2013-01-15 20:23:00 +04:00
return NT_STATUS_INVALID_PARAMETER ;
}
total_len + = cc_copy - > chunks [ i ] . length ;
}
2014-02-06 23:12:21 +04:00
/*
* - Sum of Lengths in all chunks is greater than
* ServerSideCopyMaxDataSize
*/
2013-01-15 20:23:00 +04:00
if ( total_len > COPYCHUNK_MAX_TOTAL_LEN ) {
return NT_STATUS_INVALID_PARAMETER ;
}
return NT_STATUS_OK ;
}
struct fsctl_srv_copychunk_state {
2017-03-21 11:17:03 +03:00
struct tevent_context * ev ;
2013-01-15 20:23:00 +04:00
struct connection_struct * conn ;
2017-03-21 10:26:37 +03:00
struct srv_copychunk_copy cc_copy ;
2017-03-21 11:17:03 +03:00
uint32_t current_chunk ;
2013-01-15 20:23:00 +04:00
NTSTATUS status ;
off_t total_written ;
2017-06-09 14:02:49 +03:00
uint32_t ctl_code ;
DATA_BLOB token ;
2013-01-15 20:23:05 +04:00
struct files_struct * src_fsp ;
struct files_struct * dst_fsp ;
2013-01-15 20:23:11 +04:00
enum {
COPYCHUNK_OUT_EMPTY = 0 ,
COPYCHUNK_OUT_LIMITS ,
COPYCHUNK_OUT_RSP ,
} out_data ;
2013-01-15 20:23:00 +04:00
} ;
static void fsctl_srv_copychunk_vfs_done ( struct tevent_req * subreq ) ;
2017-03-21 11:17:03 +03:00
static NTSTATUS fsctl_srv_copychunk_loop ( struct tevent_req * req ) ;
2013-01-15 20:23:00 +04:00
static struct tevent_req * fsctl_srv_copychunk_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
2013-10-19 05:47:06 +04:00
uint32_t ctl_code ,
2013-01-15 20:23:00 +04:00
struct files_struct * dst_fsp ,
DATA_BLOB * in_input ,
2013-01-15 20:23:12 +04:00
size_t in_max_output ,
2013-01-15 20:23:05 +04:00
struct smbd_smb2_request * smb2req )
2013-01-15 20:23:00 +04:00
{
2017-03-21 11:17:03 +03:00
struct tevent_req * req = NULL ;
struct fsctl_srv_copychunk_state * state = NULL ;
enum ndr_err_code ndr_ret ;
NTSTATUS status ;
2013-01-15 20:23:00 +04:00
2013-10-19 05:47:06 +04:00
/* handler for both copy-chunk variants */
SMB_ASSERT ( ( ctl_code = = FSCTL_SRV_COPYCHUNK )
| | ( ctl_code = = FSCTL_SRV_COPYCHUNK_WRITE ) ) ;
2013-01-15 20:23:00 +04:00
req = tevent_req_create ( mem_ctx , & state ,
struct fsctl_srv_copychunk_state ) ;
if ( req = = NULL ) {
return NULL ;
}
2017-03-21 11:17:03 +03:00
* state = ( struct fsctl_srv_copychunk_state ) {
. conn = dst_fsp - > conn ,
. ev = ev ,
2017-06-09 14:02:49 +03:00
. ctl_code = ctl_code ,
. dst_fsp = dst_fsp ,
2017-03-21 11:17:03 +03:00
} ;
2013-01-15 20:23:12 +04:00
if ( in_max_output < sizeof ( struct srv_copychunk_rsp ) ) {
DEBUG ( 3 , ( " max output %d not large enough to hold copy chunk "
" response %lu \n " , ( int ) in_max_output ,
2013-01-19 01:57:16 +04:00
( unsigned long ) sizeof ( struct srv_copychunk_rsp ) ) ) ;
2013-01-15 20:23:12 +04:00
state - > status = NT_STATUS_INVALID_PARAMETER ;
tevent_req_nterror ( req , state - > status ) ;
return tevent_req_post ( req , ev ) ;
}
2017-03-21 10:26:37 +03:00
ndr_ret = ndr_pull_struct_blob ( in_input , mem_ctx , & state - > cc_copy ,
2013-01-15 20:23:00 +04:00
( ndr_pull_flags_fn_t ) ndr_pull_srv_copychunk_copy ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
DEBUG ( 0 , ( " failed to unmarshall copy chunk req \n " ) ) ;
state - > status = NT_STATUS_INVALID_PARAMETER ;
tevent_req_nterror ( req , state - > status ) ;
return tevent_req_post ( req , ev ) ;
}
2017-06-09 14:02:49 +03:00
state - > token = data_blob_const ( state - > cc_copy . source_key ,
sizeof ( state - > cc_copy . source_key ) ) ;
2013-01-15 20:23:05 +04:00
2017-03-21 10:26:37 +03:00
state - > status = copychunk_check_limits ( & state - > cc_copy ) ;
2015-06-09 18:47:31 +03:00
if ( ! NT_STATUS_IS_OK ( state - > status ) ) {
2013-01-15 20:23:00 +04:00
DEBUG ( 3 , ( " copy chunk req exceeds limits \n " ) ) ;
2013-01-15 20:23:11 +04:00
state - > out_data = COPYCHUNK_OUT_LIMITS ;
2015-06-09 18:47:31 +03:00
tevent_req_nterror ( req , state - > status ) ;
2013-01-15 20:23:00 +04:00
return tevent_req_post ( req , ev ) ;
}
2013-01-15 20:23:11 +04:00
/* any errors from here onwards should carry copychunk response data */
state - > out_data = COPYCHUNK_OUT_RSP ;
2017-03-21 11:17:03 +03:00
status = fsctl_srv_copychunk_loop ( req ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
return req ;
}
static NTSTATUS fsctl_srv_copychunk_loop ( struct tevent_req * req )
{
struct fsctl_srv_copychunk_state * state = tevent_req_data (
req , struct fsctl_srv_copychunk_state ) ;
struct tevent_req * subreq = NULL ;
2017-06-09 17:50:05 +03:00
uint32_t length = 0 ;
off_t source_off = 0 ;
off_t target_off = 0 ;
2017-03-21 11:17:03 +03:00
2017-06-09 17:50:05 +03:00
/*
* chunk_count can be 0 which must either just do nothing returning
* success saying number of copied chunks is 0 ( verified against
* Windows ) .
*
* Or it can be a special macOS copyfile request , so we send this into
2024-01-10 02:25:25 +03:00
* the VFS , vfs_fruit if loaded implements the macOS copyfile semantics .
2017-06-09 17:50:05 +03:00
*/
if ( state - > cc_copy . chunk_count > 0 ) {
struct srv_copychunk * chunk = NULL ;
2015-04-22 23:29:16 +03:00
2017-06-09 17:50:05 +03:00
chunk = & state - > cc_copy . chunks [ state - > current_chunk ] ;
length = chunk - > length ;
source_off = chunk - > source_off ;
target_off = chunk - > target_off ;
}
2017-03-21 11:17:03 +03:00
2017-06-04 14:50:33 +03:00
subreq = SMB_VFS_OFFLOAD_WRITE_SEND ( state - > dst_fsp - > conn ,
2017-03-21 11:17:03 +03:00
state ,
state - > ev ,
2017-06-09 14:02:49 +03:00
state - > ctl_code ,
& state - > token ,
2017-06-09 17:50:05 +03:00
source_off ,
2017-03-21 11:17:03 +03:00
state - > dst_fsp ,
2017-06-09 17:50:05 +03:00
target_off ,
2017-06-10 10:05:55 +03:00
length ) ;
2017-03-21 11:17:03 +03:00
if ( tevent_req_nomem ( subreq , req ) ) {
return NT_STATUS_NO_MEMORY ;
2013-01-15 20:23:00 +04:00
}
2017-03-21 11:17:03 +03:00
tevent_req_set_callback ( subreq , fsctl_srv_copychunk_vfs_done , req ) ;
2013-01-15 20:23:00 +04:00
2017-03-21 11:17:03 +03:00
return NT_STATUS_OK ;
2013-01-15 20:23:00 +04:00
}
static void fsctl_srv_copychunk_vfs_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
2017-03-21 11:17:03 +03:00
struct fsctl_srv_copychunk_state * state = tevent_req_data (
req , struct fsctl_srv_copychunk_state ) ;
2013-01-15 20:23:00 +04:00
off_t chunk_nwritten ;
NTSTATUS status ;
2017-06-04 14:50:33 +03:00
status = SMB_VFS_OFFLOAD_WRITE_RECV ( state - > conn , subreq ,
2013-01-15 20:23:00 +04:00
& chunk_nwritten ) ;
2013-01-15 20:23:05 +04:00
TALLOC_FREE ( subreq ) ;
2017-03-21 11:17:03 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " copy chunk failed [%s] chunk [%u] of [%u] \n " ,
nt_errstr ( status ) ,
2017-06-09 17:35:39 +03:00
( unsigned int ) state - > current_chunk ,
2017-03-21 11:17:03 +03:00
( unsigned int ) state - > cc_copy . chunk_count ) ;
tevent_req_nterror ( req , status ) ;
return ;
2013-01-15 20:23:00 +04:00
}
2017-03-21 11:17:03 +03:00
DBG_DEBUG ( " good copy chunk [%u] of [%u] \n " ,
2017-06-09 17:35:39 +03:00
( unsigned int ) state - > current_chunk ,
2017-03-21 11:17:03 +03:00
( unsigned int ) state - > cc_copy . chunk_count ) ;
state - > total_written + = chunk_nwritten ;
2017-06-09 17:50:05 +03:00
if ( state - > cc_copy . chunk_count = = 0 ) {
/*
* This must not produce an error but just return a chunk count
* of 0 in the response .
*/
tevent_req_done ( req ) ;
return ;
}
2017-06-09 17:35:39 +03:00
state - > current_chunk + + ;
2017-03-21 20:34:22 +03:00
if ( state - > current_chunk = = state - > cc_copy . chunk_count ) {
2017-03-21 11:17:03 +03:00
tevent_req_done ( req ) ;
2013-01-15 20:23:00 +04:00
return ;
}
2017-03-21 11:17:03 +03:00
status = fsctl_srv_copychunk_loop ( req ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
2013-01-15 20:23:00 +04:00
}
}
static NTSTATUS fsctl_srv_copychunk_recv ( struct tevent_req * req ,
2013-01-15 20:23:11 +04:00
struct srv_copychunk_rsp * cc_rsp ,
bool * pack_rsp )
2013-01-15 20:23:00 +04:00
{
struct fsctl_srv_copychunk_state * state = tevent_req_data ( req ,
struct fsctl_srv_copychunk_state ) ;
NTSTATUS status ;
2013-01-15 20:23:11 +04:00
switch ( state - > out_data ) {
case COPYCHUNK_OUT_EMPTY :
* pack_rsp = false ;
break ;
case COPYCHUNK_OUT_LIMITS :
2013-01-15 20:23:00 +04:00
/* 2.2.32.1 - send back our maximum transfer size limits */
copychunk_pack_limits ( cc_rsp ) ;
2013-01-15 20:23:11 +04:00
* pack_rsp = true ;
break ;
case COPYCHUNK_OUT_RSP :
2017-06-09 17:50:05 +03:00
cc_rsp - > chunks_written = state - > current_chunk ;
2013-01-15 20:23:11 +04:00
cc_rsp - > chunk_bytes_written = 0 ;
cc_rsp - > total_bytes_written = state - > total_written ;
* pack_rsp = true ;
break ;
default : /* not reached */
assert ( 1 ) ;
break ;
2013-01-15 20:23:00 +04:00
}
2017-03-21 11:17:03 +03:00
status = tevent_req_simple_recv_ntstatus ( req ) ;
2013-01-15 20:23:00 +04:00
return status ;
}
2020-09-07 01:17:11 +03:00
struct cluster_movable_ips {
uint32_t array_len ;
uint32_t array_index ;
struct sockaddr_storage * ips ;
} ;
static int stash_cluster_movable_ips ( uint32_t total_ip_count ,
const struct sockaddr_storage * ip ,
bool is_movable_ip ,
void * private_data )
{
struct cluster_movable_ips * cluster_movable_ips
= talloc_get_type_abort ( private_data ,
struct cluster_movable_ips ) ;
if ( ! is_movable_ip ) {
return 0 ;
}
if ( cluster_movable_ips - > array_len = = 0 ) {
SMB_ASSERT ( total_ip_count < INT_MAX ) ;
cluster_movable_ips - > ips
= talloc_zero_array ( cluster_movable_ips ,
struct sockaddr_storage ,
total_ip_count ) ;
if ( cluster_movable_ips - > ips = = NULL ) {
return ENOMEM ;
}
cluster_movable_ips - > array_len = total_ip_count ;
}
SMB_ASSERT ( cluster_movable_ips - > array_index
< cluster_movable_ips - > array_len ) ;
cluster_movable_ips - > ips [ cluster_movable_ips - > array_index ] = * ip ;
cluster_movable_ips - > array_index + + ;
return 0 ;
}
static bool find_in_cluster_movable_ips (
struct cluster_movable_ips * cluster_movable_ips ,
const struct sockaddr_storage * ifss )
{
struct samba_sockaddr srv_ip = {
. u = {
. ss = * ifss ,
} ,
} ;
2020-11-04 13:29:21 +03:00
uint32_t i ;
2020-09-07 01:17:11 +03:00
for ( i = 0 ; i < cluster_movable_ips - > array_index ; i + + ) {
struct samba_sockaddr pub_ip = {
. u = {
. ss = cluster_movable_ips - > ips [ i ] ,
} ,
} ;
if ( sockaddr_equal ( & pub_ip . u . sa , & srv_ip . u . sa ) ) {
return true ;
}
}
return false ;
}
2014-06-13 19:42:00 +04:00
static NTSTATUS fsctl_network_iface_info ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct smbXsrv_connection * xconn ,
DATA_BLOB * in_input ,
uint32_t in_max_output ,
DATA_BLOB * out_output )
{
2023-12-29 15:09:32 +03:00
struct samba_sockaddr xconn_srv_addr = { . sa_socklen = 0 , } ;
2014-06-13 19:42:00 +04:00
struct fsctl_net_iface_info * array = NULL ;
struct fsctl_net_iface_info * first = NULL ;
struct fsctl_net_iface_info * last = NULL ;
size_t i ;
2024-01-04 04:42:15 +03:00
size_t num_ifaces ;
2014-06-13 19:42:00 +04:00
enum ndr_err_code ndr_err ;
2020-09-07 01:17:11 +03:00
struct cluster_movable_ips * cluster_movable_ips = NULL ;
2023-12-29 15:09:32 +03:00
ssize_t sret ;
2020-09-07 01:17:11 +03:00
int ret ;
2014-06-13 19:42:00 +04:00
if ( in_input - > length ! = 0 ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2024-01-04 04:42:15 +03:00
/*
* The list of probed interfaces might have changed , we might need to
* refresh local_interfaces to get up - to - date network information , and
* respond to clients which sent FSCTL_QUERY_NETWORK_INTERFACE_INFO .
* For example , network speed is changed , interfaces count is changed
* ( some link down or link up ) , and etc .
*/
load_interfaces ( ) ;
num_ifaces = iface_count ( ) ;
2014-06-13 19:42:00 +04:00
* out_output = data_blob_null ;
array = talloc_zero_array ( mem_ctx ,
struct fsctl_net_iface_info ,
num_ifaces ) ;
if ( array = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2020-09-07 01:17:11 +03:00
if ( lp_clustering ( ) ) {
cluster_movable_ips = talloc_zero ( array ,
struct cluster_movable_ips ) ;
if ( cluster_movable_ips = = NULL ) {
TALLOC_FREE ( array ) ;
return NT_STATUS_NO_MEMORY ;
}
ret = ctdbd_public_ip_foreach ( messaging_ctdb_connection ( ) ,
stash_cluster_movable_ips ,
cluster_movable_ips ) ;
if ( ret ! = 0 ) {
TALLOC_FREE ( array ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
}
2023-12-29 15:09:32 +03:00
sret = tsocket_address_bsd_sockaddr ( xconn - > local_address ,
& xconn_srv_addr . u . sa ,
sizeof ( xconn_srv_addr . u . ss ) ) ;
if ( sret < 0 ) {
return NT_STATUS_INTERNAL_ERROR ;
}
xconn_srv_addr . sa_socklen = sret ;
2014-06-13 19:42:00 +04:00
for ( i = 0 ; i < num_ifaces ; i + + ) {
struct fsctl_net_iface_info * cur = & array [ i ] ;
const struct interface * iface = get_interface ( i ) ;
const struct sockaddr_storage * ifss = & iface - > ip ;
const void * ifptr = ifss ;
const struct sockaddr * ifsa = ( const struct sockaddr * ) ifptr ;
struct tsocket_address * a = NULL ;
char * addr ;
bool ok ;
ret = tsocket_address_bsd_from_sockaddr ( array ,
ifsa , sizeof ( struct sockaddr_storage ) ,
& a ) ;
if ( ret ! = 0 ) {
2020-09-07 00:59:04 +03:00
NTSTATUS status = map_nt_error_from_unix_common ( errno ) ;
TALLOC_FREE ( array ) ;
return status ;
2014-06-13 19:42:00 +04:00
}
ok = tsocket_address_is_inet ( a , " ip " ) ;
if ( ! ok ) {
continue ;
}
addr = tsocket_address_inet_addr_string ( a , array ) ;
if ( addr = = NULL ) {
TALLOC_FREE ( array ) ;
return NT_STATUS_NO_MEMORY ;
}
2023-12-29 15:09:32 +03:00
if ( sockaddr_equal ( ifsa , & xconn_srv_addr . u . sa ) ) {
/*
* We can announce the ip of the current connection even
* if it is a moveable cluster address . . . as the client
* is already connected to it .
*
* It means in a typical ctdb cluster , where we
* only have public addresses , the client can at least
* have more than one multichannel ' ed connection to the
* public ip .
*/
} else if ( cluster_movable_ips ! = NULL ) {
2020-09-07 01:17:11 +03:00
bool is_movable_ip = find_in_cluster_movable_ips (
cluster_movable_ips ,
ifss ) ;
if ( is_movable_ip ) {
2020-06-25 16:32:11 +03:00
DBG_DEBUG ( " Interface [%s] - "
2020-09-07 01:17:11 +03:00
" has movable public ip - "
2020-06-25 16:32:11 +03:00
" skipping address [%s]. \n " ,
iface - > name , addr ) ;
continue ;
}
}
2014-06-13 19:42:00 +04:00
cur - > ifindex = iface - > if_index ;
if ( cur - > ifindex = = 0 ) {
/*
* Did not get interface index from kernel ,
* nor from the config . = = > Apply a common
* default value for these cases .
*/
cur - > ifindex = UINT32_MAX ;
}
cur - > capability = iface - > capability ;
cur - > linkspeed = iface - > linkspeed ;
if ( cur - > linkspeed = = 0 ) {
DBG_DEBUG ( " Link speed 0 on interface [%s] - skipping "
" address [%s]. \n " , iface - > name , addr ) ;
continue ;
}
ok = tsocket_address_is_inet ( a , " ipv4 " ) ;
if ( ok ) {
cur - > sockaddr . family = FSCTL_NET_IFACE_AF_INET ;
cur - > sockaddr . saddr . saddr_in . ipv4 = addr ;
}
ok = tsocket_address_is_inet ( a , " ipv6 " ) ;
if ( ok ) {
cur - > sockaddr . family = FSCTL_NET_IFACE_AF_INET6 ;
cur - > sockaddr . saddr . saddr_in6 . ipv6 = addr ;
}
if ( first = = NULL ) {
first = cur ;
}
if ( last ! = NULL ) {
last - > next = cur ;
}
last = cur ;
}
if ( first = = NULL ) {
TALLOC_FREE ( array ) ;
return NT_STATUS_OK ;
}
if ( DEBUGLEVEL > = 10 ) {
NDR_PRINT_DEBUG ( fsctl_net_iface_info , first ) ;
}
ndr_err = ndr_push_struct_blob ( out_output , mem_ctx , first ,
( ndr_push_flags_fn_t ) ndr_push_fsctl_net_iface_info ) ;
TALLOC_FREE ( array ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return ndr_map_error2ntstatus ( ndr_err ) ;
}
return NT_STATUS_OK ;
}
2013-01-15 20:22:57 +04:00
static NTSTATUS fsctl_validate_neg_info ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct smbXsrv_connection * conn ,
DATA_BLOB * in_input ,
uint32_t in_max_output ,
DATA_BLOB * out_output ,
bool * disconnect )
{
uint32_t in_capabilities ;
DATA_BLOB in_guid_blob ;
struct GUID in_guid ;
uint16_t in_security_mode ;
uint16_t in_num_dialects ;
2014-06-21 08:41:19 +04:00
uint16_t dialect ;
2020-09-29 11:20:41 +03:00
struct GUID_ndr_buf out_guid_buf = { . buf = { 0 } , } ;
2013-01-15 20:22:57 +04:00
NTSTATUS status ;
2014-06-21 08:41:19 +04:00
enum protocol_types protocol = PROTOCOL_NONE ;
2013-01-15 20:22:57 +04:00
2017-05-05 19:49:37 +03:00
if ( lp_server_max_protocol ( ) < = PROTOCOL_SMB2_02 ) {
/*
* With SMB 2.02 we didn ' t get the
2024-01-10 02:25:25 +03:00
* capabilities , client guid , security mode
2017-05-05 19:49:37 +03:00
* and dialects the client would have offered .
*
* So we behave compatible with a true
* SMB 2.02 server and return NT_STATUS_FILE_CLOSED .
*
* As SMB > = 2.10 offers the two phase SMB2 Negotiate
* we keep supporting FSCTL_VALIDATE_NEGOTIATE_INFO
* starting with SMB 2.10 , while Windows only supports
* it starting with SMB > 2.10 .
*/
return NT_STATUS_FILE_CLOSED ;
}
2013-01-15 20:22:57 +04:00
if ( in_input - > length < 0x18 ) {
return NT_STATUS_INVALID_PARAMETER ;
}
in_capabilities = IVAL ( in_input - > data , 0x00 ) ;
in_guid_blob = data_blob_const ( in_input - > data + 0x04 , 16 ) ;
in_security_mode = SVAL ( in_input - > data , 0x14 ) ;
in_num_dialects = SVAL ( in_input - > data , 0x16 ) ;
if ( in_input - > length < ( 0x18 + in_num_dialects * 2 ) ) {
return NT_STATUS_INVALID_PARAMETER ;
}
if ( in_max_output < 0x18 ) {
return NT_STATUS_BUFFER_TOO_SMALL ;
}
status = GUID_from_ndr_blob ( & in_guid_blob , & in_guid ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2014-06-21 08:41:19 +04:00
/*
* From : [ MS - SMB2 ]
* 3.3 .5 .15 .12 Handling a Validate Negotiate Info Request
*
* The server MUST determine the greatest common dialect
* between the dialects it implements and the Dialects array
* of the VALIDATE_NEGOTIATE_INFO request . If no dialect is
* matched , or if the value is not equal to Connection . Dialect ,
* the server MUST terminate the transport connection
* and free the Connection object .
*/
protocol = smbd_smb2_protocol_dialect_match ( in_input - > data + 0x18 ,
in_num_dialects ,
& dialect ) ;
if ( conn - > protocol ! = protocol ) {
2013-01-15 20:22:57 +04:00
* disconnect = true ;
return NT_STATUS_ACCESS_DENIED ;
}
2014-06-05 14:04:43 +04:00
if ( ! GUID_equal ( & in_guid , & conn - > smb2 . client . guid ) ) {
2013-01-15 20:22:57 +04:00
* disconnect = true ;
return NT_STATUS_ACCESS_DENIED ;
}
if ( in_security_mode ! = conn - > smb2 . client . security_mode ) {
* disconnect = true ;
return NT_STATUS_ACCESS_DENIED ;
}
if ( in_capabilities ! = conn - > smb2 . client . capabilities ) {
* disconnect = true ;
return NT_STATUS_ACCESS_DENIED ;
}
2024-02-09 21:09:35 +03:00
GUID_to_ndr_buf ( & conn - > smb2 . server . guid , & out_guid_buf ) ;
2013-01-15 20:22:57 +04:00
* out_output = data_blob_talloc ( mem_ctx , NULL , 0x18 ) ;
if ( out_output - > data = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
SIVAL ( out_output - > data , 0x00 , conn - > smb2 . server . capabilities ) ;
2020-09-29 11:20:41 +03:00
memcpy ( out_output - > data + 0x04 , out_guid_buf . buf , 16 ) ;
2014-02-07 19:37:38 +04:00
SSVAL ( out_output - > data , 0x14 , conn - > smb2 . server . security_mode ) ;
SSVAL ( out_output - > data , 0x16 , conn - > smb2 . server . dialect ) ;
2013-01-15 20:22:57 +04:00
return NT_STATUS_OK ;
}
2013-01-15 20:23:00 +04:00
static void smb2_ioctl_network_fs_copychunk_done ( struct tevent_req * subreq ) ;
2017-06-06 13:23:27 +03:00
static void smb2_ioctl_network_fs_offload_read_done ( struct tevent_req * subreq ) ;
2013-01-15 20:23:00 +04:00
struct tevent_req * smb2_ioctl_network_fs ( uint32_t ctl_code ,
struct tevent_context * ev ,
struct tevent_req * req ,
struct smbd_smb2_ioctl_state * state )
2013-01-15 20:22:57 +04:00
{
2013-01-15 20:23:00 +04:00
struct tevent_req * subreq ;
2013-01-15 20:22:57 +04:00
NTSTATUS status ;
switch ( ctl_code ) {
2013-10-19 05:47:06 +04:00
/*
* [ MS - SMB2 ] 2.2 .31
* FSCTL_SRV_COPYCHUNK is issued when a handle has
* FILE_READ_DATA and FILE_WRITE_DATA access to the file ;
* FSCTL_SRV_COPYCHUNK_WRITE is issued when a handle only has
* FILE_WRITE_DATA access .
*/
case FSCTL_SRV_COPYCHUNK_WRITE : /* FALL THROUGH */
2013-01-15 20:23:00 +04:00
case FSCTL_SRV_COPYCHUNK :
2013-10-19 05:47:06 +04:00
subreq = fsctl_srv_copychunk_send ( state , ev ,
ctl_code ,
state - > fsp ,
2013-01-15 20:23:00 +04:00
& state - > in_input ,
2013-01-15 20:23:12 +04:00
state - > in_max_output ,
2013-01-15 20:23:05 +04:00
state - > smb2req ) ;
2013-01-15 20:23:00 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq ,
smb2_ioctl_network_fs_copychunk_done ,
req ) ;
return req ;
break ;
2014-06-13 19:42:00 +04:00
case FSCTL_QUERY_NETWORK_INTERFACE_INFO :
2016-01-25 14:01:44 +03:00
if ( ! state - > smbreq - > xconn - > client - > server_multi_channel_enabled )
{
if ( IS_IPC ( state - > smbreq - > conn ) ) {
status = NT_STATUS_FS_DRIVER_REQUIRED ;
} else {
status = NT_STATUS_INVALID_DEVICE_REQUEST ;
}
tevent_req_nterror ( req , status ) ;
return tevent_req_post ( req , ev ) ;
}
2014-06-13 19:42:00 +04:00
status = fsctl_network_iface_info ( state , ev ,
state - > smbreq - > xconn ,
& state - > in_input ,
state - > in_max_output ,
& state - > out_output ) ;
if ( ! tevent_req_nterror ( req , status ) ) {
tevent_req_done ( req ) ;
}
return tevent_req_post ( req , ev ) ;
break ;
2013-01-15 20:22:57 +04:00
case FSCTL_VALIDATE_NEGOTIATE_INFO :
status = fsctl_validate_neg_info ( state , ev ,
2014-06-12 10:38:48 +04:00
state - > smbreq - > xconn ,
2013-01-15 20:22:57 +04:00
& state - > in_input ,
state - > in_max_output ,
& state - > out_output ,
& state - > disconnect ) ;
if ( ! tevent_req_nterror ( req , status ) ) {
tevent_req_done ( req ) ;
}
return tevent_req_post ( req , ev ) ;
2013-01-15 20:22:58 +04:00
break ;
case FSCTL_SRV_REQUEST_RESUME_KEY :
2017-06-06 13:23:27 +03:00
subreq = SMB_VFS_OFFLOAD_READ_SEND ( state ,
ev ,
state - > fsp ,
FSCTL_SRV_REQUEST_RESUME_KEY ,
0 , 0 , 0 ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
2013-01-15 20:22:58 +04:00
}
2017-06-06 13:23:27 +03:00
tevent_req_set_callback (
subreq , smb2_ioctl_network_fs_offload_read_done , req ) ;
return req ;
2013-01-15 20:22:57 +04:00
default : {
uint8_t * out_data = NULL ;
uint32_t out_data_len = 0 ;
2013-01-15 20:23:08 +04:00
if ( state - > fsp = = NULL ) {
status = NT_STATUS_NOT_SUPPORTED ;
} else {
status = SMB_VFS_FSCTL ( state - > fsp ,
state ,
ctl_code ,
state - > smbreq - > flags2 ,
state - > in_input . data ,
state - > in_input . length ,
& out_data ,
state - > in_max_output ,
& out_data_len ) ;
state - > out_output = data_blob_const ( out_data , out_data_len ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
2013-01-15 20:22:57 +04:00
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NOT_SUPPORTED ) ) {
if ( IS_IPC ( state - > smbreq - > conn ) ) {
status = NT_STATUS_FS_DRIVER_REQUIRED ;
} else {
status = NT_STATUS_INVALID_DEVICE_REQUEST ;
}
}
tevent_req_nterror ( req , status ) ;
return tevent_req_post ( req , ev ) ;
break ;
}
}
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
return tevent_req_post ( req , ev ) ;
}
2013-01-15 20:23:00 +04:00
static void smb2_ioctl_network_fs_copychunk_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct smbd_smb2_ioctl_state * ioctl_state = tevent_req_data ( req ,
struct smbd_smb2_ioctl_state ) ;
struct srv_copychunk_rsp cc_rsp ;
NTSTATUS status ;
2013-01-15 20:23:11 +04:00
bool pack_rsp = false ;
2013-01-15 20:23:00 +04:00
ZERO_STRUCT ( cc_rsp ) ;
2013-01-15 20:23:11 +04:00
status = fsctl_srv_copychunk_recv ( subreq , & cc_rsp , & pack_rsp ) ;
2013-01-15 20:23:05 +04:00
TALLOC_FREE ( subreq ) ;
2013-01-15 20:23:11 +04:00
if ( pack_rsp = = true ) {
enum ndr_err_code ndr_ret ;
ndr_ret = ndr_push_struct_blob ( & ioctl_state - > out_output ,
ioctl_state ,
& cc_rsp ,
( ndr_push_flags_fn_t ) ndr_push_srv_copychunk_rsp ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
status = NT_STATUS_INTERNAL_ERROR ;
}
2013-01-15 20:23:00 +04:00
}
if ( ! tevent_req_nterror ( req , status ) ) {
tevent_req_done ( req ) ;
}
}
2017-06-06 13:23:27 +03:00
static void smb2_ioctl_network_fs_offload_read_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct smbd_smb2_ioctl_state * state = tevent_req_data (
req , struct smbd_smb2_ioctl_state ) ;
struct req_resume_key_rsp rkey_rsp ;
enum ndr_err_code ndr_ret ;
2021-06-22 21:13:02 +03:00
uint32_t flags ;
uint64_t xferlen ;
2017-06-06 13:23:27 +03:00
DATA_BLOB token ;
NTSTATUS status ;
2021-06-22 21:13:02 +03:00
/*
* Note that both flags and xferlen are not used with copy - chunk .
*/
2017-06-06 13:23:27 +03:00
status = SMB_VFS_OFFLOAD_READ_RECV ( subreq ,
state - > fsp - > conn ,
state ,
2021-06-22 21:13:02 +03:00
& flags ,
& xferlen ,
2017-06-06 13:23:27 +03:00
& token ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
if ( token . length ! = sizeof ( rkey_rsp . resume_key ) ) {
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
return ;
}
ZERO_STRUCT ( rkey_rsp ) ;
memcpy ( rkey_rsp . resume_key , token . data , token . length ) ;
ndr_ret = ndr_push_struct_blob ( & state - > out_output , state , & rkey_rsp ,
( ndr_push_flags_fn_t ) ndr_push_req_resume_key_rsp ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
return ;
}
tevent_req_done ( req ) ;
return ;
}