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"
2013-01-15 20:22:58 +04:00
# include "../lib/ccan/build_assert/build_assert.h"
2013-01-15 20:22:57 +04:00
# 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"
2013-01-15 20:23:00 +04:00
# define COPYCHUNK_MAX_CHUNKS 256 /* 2k8r2 & win8 = 256 */
# define COPYCHUNK_MAX_CHUNK_LEN 1048576 /* 2k8r2 & win8 = 1048576 */
# define COPYCHUNK_MAX_TOTAL_LEN 16777216 /* 2k8r2 & win8 = 16777216 */
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 ;
if ( cc_copy - > chunk_count > COPYCHUNK_MAX_CHUNKS ) {
return NT_STATUS_INVALID_PARAMETER ;
}
for ( i = 0 ; i < cc_copy - > chunk_count ; i + + ) {
if ( cc_copy - > chunks [ i ] . length > COPYCHUNK_MAX_CHUNK_LEN ) {
return NT_STATUS_INVALID_PARAMETER ;
}
total_len + = cc_copy - > chunks [ i ] . length ;
}
if ( total_len > COPYCHUNK_MAX_TOTAL_LEN ) {
return NT_STATUS_INVALID_PARAMETER ;
}
return NT_STATUS_OK ;
}
struct fsctl_srv_copychunk_state {
struct connection_struct * conn ;
uint32_t dispatch_count ;
uint32_t recv_count ;
uint32_t bad_recv_count ;
NTSTATUS status ;
off_t total_written ;
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 ) ;
2013-01-17 00:51:32 +04:00
static NTSTATUS copychunk_check_handles ( struct files_struct * src_fsp ,
struct files_struct * dst_fsp ,
struct smb_request * smb1req )
{
/*
* [ MS - SMB2 ] 3.3 .5 .15 .6 Handling a Server - Side Data Copy Request
* If Open . GrantedAccess of the destination file does not
* include FILE_WRITE_DATA , then the request MUST be failed with
* STATUS_ACCESS_DENIED . If Open . GrantedAccess of the
* destination file does not include FILE_READ_DATA access and
* the CtlCode is FSCTL_SRV_COPYCHUNK , then the request MUST be
* failed with STATUS_ACCESS_DENIED .
*/
if ( ! CHECK_WRITE ( dst_fsp ) ) {
DEBUG ( 5 , ( " copy chunk no write on dest handle (%s). \n " ,
smb_fname_str_dbg ( dst_fsp - > fsp_name ) ) ) ;
return NT_STATUS_ACCESS_DENIED ;
}
if ( ! CHECK_READ ( dst_fsp , smb1req ) ) {
DEBUG ( 5 , ( " copy chunk no read on dest handle (%s). \n " ,
smb_fname_str_dbg ( dst_fsp - > fsp_name ) ) ) ;
return NT_STATUS_ACCESS_DENIED ;
}
if ( ! CHECK_READ ( src_fsp , smb1req ) ) {
DEBUG ( 5 , ( " copy chunk no read on src handle (%s). \n " ,
smb_fname_str_dbg ( src_fsp - > fsp_name ) ) ) ;
return NT_STATUS_ACCESS_DENIED ;
}
2013-01-17 00:58:17 +04:00
if ( src_fsp - > is_directory ) {
DEBUG ( 5 , ( " copy chunk no read on src directory handle (%s). \n " ,
smb_fname_str_dbg ( src_fsp - > fsp_name ) ) ) ;
return NT_STATUS_ACCESS_DENIED ;
}
if ( dst_fsp - > is_directory ) {
DEBUG ( 5 , ( " copy chunk no read on dst directory handle (%s). \n " ,
smb_fname_str_dbg ( dst_fsp - > fsp_name ) ) ) ;
return NT_STATUS_ACCESS_DENIED ;
}
if ( IS_IPC ( src_fsp - > conn ) | | IS_IPC ( dst_fsp - > conn ) ) {
DEBUG ( 5 , ( " copy chunk no access on IPC$ handle. \n " ) ) ;
return NT_STATUS_ACCESS_DENIED ;
}
if ( IS_PRINT ( src_fsp - > conn ) | | IS_PRINT ( dst_fsp - > conn ) ) {
DEBUG ( 5 , ( " copy chunk no access on PRINT handle. \n " ) ) ;
return NT_STATUS_ACCESS_DENIED ;
}
2013-01-17 00:51:32 +04:00
return NT_STATUS_OK ;
}
2013-01-15 20:23:00 +04:00
static struct tevent_req * fsctl_srv_copychunk_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
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
{
struct tevent_req * req ;
struct srv_copychunk_copy cc_copy ;
enum ndr_err_code ndr_ret ;
2013-01-15 20:23:05 +04:00
uint64_t src_persistent_h ;
uint64_t src_volatile_h ;
2013-01-15 20:23:00 +04:00
int i ;
struct srv_copychunk * chunk ;
struct fsctl_srv_copychunk_state * state ;
req = tevent_req_create ( mem_ctx , & state ,
struct fsctl_srv_copychunk_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > conn = dst_fsp - > conn ;
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 ) ;
}
2013-01-15 20:23:00 +04:00
ndr_ret = ndr_pull_struct_blob ( in_input , mem_ctx , & cc_copy ,
( 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 ) ;
}
2013-01-15 20:23:05 +04:00
/* persistent/volatile keys sent as the resume key */
src_persistent_h = BVAL ( cc_copy . source_key , 0 ) ;
src_volatile_h = BVAL ( cc_copy . source_key , 8 ) ;
state - > src_fsp = file_fsp_get ( smb2req , src_persistent_h , src_volatile_h ) ;
if ( state - > src_fsp = = NULL ) {
2013-01-15 20:23:00 +04:00
DEBUG ( 3 , ( " invalid resume key in copy chunk req \n " ) ) ;
state - > status = NT_STATUS_OBJECT_NAME_NOT_FOUND ;
tevent_req_nterror ( req , state - > status ) ;
return tevent_req_post ( req , ev ) ;
}
2013-01-15 20:23:05 +04:00
state - > dst_fsp = dst_fsp ;
2013-01-17 00:51:32 +04:00
state - > status = copychunk_check_handles ( state - > src_fsp ,
state - > dst_fsp ,
smb2req - > smb1req ) ;
if ( ! NT_STATUS_IS_OK ( state - > status ) ) {
2013-01-15 20:23:10 +04:00
tevent_req_nterror ( req , state - > status ) ;
return tevent_req_post ( req , ev ) ;
}
2013-01-15 20:23:05 +04:00
2013-01-15 20:23:00 +04:00
state - > status = copychunk_check_limits ( & cc_copy ) ;
if ( tevent_req_nterror ( req , state - > status ) ) {
DEBUG ( 3 , ( " copy chunk req exceeds limits \n " ) ) ;
2013-01-15 20:23:11 +04:00
state - > out_data = COPYCHUNK_OUT_LIMITS ;
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 ;
2013-01-15 20:23:00 +04:00
for ( i = 0 ; i < cc_copy . chunk_count ; i + + ) {
struct tevent_req * vfs_subreq ;
chunk = & cc_copy . chunks [ i ] ;
vfs_subreq = SMB_VFS_COPY_CHUNK_SEND ( dst_fsp - > conn ,
state , ev ,
2013-01-15 20:23:05 +04:00
state - > src_fsp ,
chunk - > source_off ,
state - > dst_fsp ,
chunk - > target_off ,
2013-01-15 20:23:00 +04:00
chunk - > length ) ;
if ( vfs_subreq = = NULL ) {
DEBUG ( 0 , ( " VFS copy chunk send failed \n " ) ) ;
state - > status = NT_STATUS_NO_MEMORY ;
if ( state - > dispatch_count = = 0 ) {
/* nothing dispatched, return immediately */
tevent_req_nterror ( req , state - > status ) ;
return tevent_req_post ( req , ev ) ;
} else {
/*
* wait for dispatched to complete before
2013-01-17 04:30:04 +04:00
* returning error .
2013-01-15 20:23:00 +04:00
*/
break ;
}
}
tevent_req_set_callback ( vfs_subreq ,
fsctl_srv_copychunk_vfs_done , req ) ;
state - > dispatch_count + + ;
}
return req ;
}
static void fsctl_srv_copychunk_vfs_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct fsctl_srv_copychunk_state * state = tevent_req_data ( req ,
struct fsctl_srv_copychunk_state ) ;
off_t chunk_nwritten ;
NTSTATUS status ;
state - > recv_count + + ;
status = SMB_VFS_COPY_CHUNK_RECV ( state - > conn , subreq ,
& chunk_nwritten ) ;
2013-01-15 20:23:05 +04:00
TALLOC_FREE ( subreq ) ;
2013-01-15 20:23:00 +04:00
if ( NT_STATUS_IS_OK ( status ) ) {
2013-01-19 01:57:16 +04:00
DEBUG ( 10 , ( " good copy chunk recv %u of %u \n " ,
( unsigned int ) state - > recv_count ,
( unsigned int ) state - > dispatch_count ) ) ;
2013-01-15 20:23:00 +04:00
state - > total_written + = chunk_nwritten ;
} else {
2013-01-19 01:57:16 +04:00
DEBUG ( 0 , ( " bad status in copy chunk recv %u of %u: %s \n " ,
( unsigned int ) state - > recv_count ,
( unsigned int ) state - > dispatch_count ,
2013-01-15 20:23:00 +04:00
nt_errstr ( status ) ) ) ;
state - > bad_recv_count + + ;
/* may overwrite previous failed status */
state - > status = status ;
}
if ( state - > recv_count ! = state - > dispatch_count ) {
/*
* Wait for all VFS copy_chunk requests to complete , even
* if an error is received for a specific chunk .
*/
return ;
}
if ( ! tevent_req_nterror ( req , state - > status ) ) {
tevent_req_done ( req ) ;
}
}
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 :
cc_rsp - > chunks_written = state - > recv_count - state - > bad_recv_count ;
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
}
status = state - > status ;
tevent_req_received ( req ) ;
return status ;
}
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 ;
uint16_t i ;
DATA_BLOB out_guid_blob ;
NTSTATUS status ;
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 ;
}
if ( in_num_dialects ! = conn - > smb2 . client . num_dialects ) {
* disconnect = true ;
return NT_STATUS_ACCESS_DENIED ;
}
for ( i = 0 ; i < in_num_dialects ; i + + ) {
uint16_t v = SVAL ( in_input - > data , 0x18 + i * 2 ) ;
if ( conn - > smb2 . client . dialects [ i ] ! = v ) {
* disconnect = true ;
return NT_STATUS_ACCESS_DENIED ;
}
}
if ( GUID_compare ( & in_guid , & conn - > smb2 . client . guid ) ! = 0 ) {
* 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 ;
}
status = GUID_to_ndr_blob ( & conn - > smb2 . server . guid , mem_ctx ,
& out_guid_blob ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
* 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 ) ;
memcpy ( out_output - > data + 0x04 , out_guid_blob . data , 16 ) ;
SIVAL ( out_output - > data , 0x14 , conn - > smb2 . server . security_mode ) ;
SIVAL ( out_output - > data , 0x16 , conn - > smb2 . server . dialect ) ;
return NT_STATUS_OK ;
}
2013-01-15 20:22:58 +04:00
static NTSTATUS fsctl_srv_req_resume_key ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct files_struct * fsp ,
uint32_t in_max_output ,
DATA_BLOB * out_output )
{
struct req_resume_key_rsp rkey_rsp ;
enum ndr_err_code ndr_ret ;
DATA_BLOB output ;
if ( fsp = = NULL ) {
return NT_STATUS_FILE_CLOSED ;
}
ZERO_STRUCT ( rkey_rsp ) ;
2013-01-15 20:23:05 +04:00
/* combine persistent and volatile handles for the resume key */
SBVAL ( rkey_rsp . resume_key , 0 , fsp - > op - > global - > open_persistent_id ) ;
SBVAL ( rkey_rsp . resume_key , 8 , fsp - > op - > global - > open_volatile_id ) ;
2013-01-15 20:22:58 +04:00
ndr_ret = ndr_push_struct_blob ( & output , mem_ctx , & rkey_rsp ,
( ndr_push_flags_fn_t ) ndr_push_req_resume_key_rsp ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
return NT_STATUS_INTERNAL_ERROR ;
}
if ( in_max_output < output . length ) {
DEBUG ( 1 , ( " max output %u too small for resume key rsp %ld \n " ,
2013-01-19 01:57:16 +04:00
( unsigned int ) in_max_output , ( long int ) output . length ) ) ;
2013-01-15 20:22:58 +04:00
return NT_STATUS_INVALID_PARAMETER ;
}
* out_output = output ;
return NT_STATUS_OK ;
}
2013-01-15 20:23:00 +04:00
static void smb2_ioctl_network_fs_copychunk_done ( struct tevent_req * subreq ) ;
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-01-15 20:23:00 +04:00
case FSCTL_SRV_COPYCHUNK :
subreq = fsctl_srv_copychunk_send ( state , ev , state - > fsp ,
& 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 ;
2013-01-15 20:22:57 +04:00
case FSCTL_VALIDATE_NEGOTIATE_INFO :
status = fsctl_validate_neg_info ( state , ev ,
state - > smbreq - > sconn - > conn ,
& 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 :
status = fsctl_srv_req_resume_key ( state , ev , state - > fsp ,
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
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 ) ;
}
}