2011-05-05 20:12:07 +04:00
/*
Unix SMB / CIFS implementation .
smb2 lib
Copyright ( C ) Volker Lendecke 2011
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"
2012-05-15 12:23:54 +04:00
# include "system/network.h"
2011-05-05 20:12:07 +04:00
# include "lib/util/tevent_ntstatus.h"
2012-05-15 12:23:54 +04:00
# include "smb_common.h"
# include "smbXcli_base.h"
# include "smb2_create_blob.h"
2011-05-05 20:12:07 +04:00
struct smb2cli_create_state {
uint8_t fixed [ 56 ] ;
uint64_t fid_persistent ;
uint64_t fid_volatile ;
2014-05-09 07:08:41 +04:00
struct smb_create_returns cr ;
2011-05-05 20:12:07 +04:00
struct smb2_create_blobs blobs ;
2014-06-20 14:37:14 +04:00
struct tevent_req * subreq ;
2011-05-05 20:12:07 +04:00
} ;
static void smb2cli_create_done ( struct tevent_req * subreq ) ;
2014-06-20 14:37:14 +04:00
static bool smb2cli_create_cancel ( struct tevent_req * req ) ;
2011-05-05 20:12:07 +04:00
struct tevent_req * smb2cli_create_send (
TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
2012-05-10 20:34:40 +04:00
struct smbXcli_conn * conn ,
uint32_t timeout_msec ,
struct smbXcli_session * session ,
2012-07-24 00:32:49 +04:00
struct smbXcli_tcon * tcon ,
2011-05-05 20:12:07 +04:00
const char * filename ,
uint8_t oplock_level , /* SMB2_OPLOCK_LEVEL_* */
uint32_t impersonation_level , /* SMB2_IMPERSONATION_* */
uint32_t desired_access ,
uint32_t file_attributes ,
uint32_t share_access ,
uint32_t create_disposition ,
uint32_t create_options ,
struct smb2_create_blobs * blobs )
{
struct tevent_req * req , * subreq ;
struct smb2cli_create_state * state ;
uint8_t * fixed ;
uint8_t * name_utf16 ;
size_t name_utf16_len ;
DATA_BLOB blob ;
NTSTATUS status ;
size_t blobs_offset ;
uint8_t * dyn ;
size_t dyn_len ;
2013-08-13 12:25:52 +04:00
size_t max_dyn_len ;
2013-09-18 05:50:42 +04:00
uint32_t additional_flags = 0 ;
uint32_t clear_flags = 0 ;
2011-05-05 20:12:07 +04:00
req = tevent_req_create ( mem_ctx , & state ,
struct smb2cli_create_state ) ;
if ( req = = NULL ) {
return NULL ;
}
if ( ! convert_string_talloc ( state , CH_UNIX , CH_UTF16 ,
2011-09-05 20:22:57 +04:00
filename , strlen ( filename ) ,
2011-05-05 20:12:07 +04:00
& name_utf16 , & name_utf16_len ) ) {
2011-07-10 11:21:45 +04:00
tevent_req_oom ( req ) ;
2011-05-05 20:12:07 +04:00
return tevent_req_post ( req , ev ) ;
}
2011-09-05 20:22:57 +04:00
if ( strlen ( filename ) = = 0 ) {
TALLOC_FREE ( name_utf16 ) ;
name_utf16_len = 0 ;
}
2011-05-05 20:12:07 +04:00
fixed = state - > fixed ;
SSVAL ( fixed , 0 , 57 ) ;
SCVAL ( fixed , 3 , oplock_level ) ;
SIVAL ( fixed , 4 , impersonation_level ) ;
SIVAL ( fixed , 24 , desired_access ) ;
SIVAL ( fixed , 28 , file_attributes ) ;
SIVAL ( fixed , 32 , share_access ) ;
SIVAL ( fixed , 36 , create_disposition ) ;
SIVAL ( fixed , 40 , create_options ) ;
SSVAL ( fixed , 44 , SMB2_HDR_BODY + 56 ) ;
SSVAL ( fixed , 46 , name_utf16_len ) ;
blob = data_blob_null ;
if ( blobs ! = NULL ) {
2012-05-03 14:05:13 +04:00
status = smb2_create_blob_push ( state , & blob , * blobs ) ;
2011-05-05 20:12:07 +04:00
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
}
blobs_offset = name_utf16_len ;
blobs_offset = ( ( blobs_offset + 3 ) & ~ 3 ) ;
2011-09-07 10:15:00 +04:00
if ( blob . length > 0 ) {
2016-08-20 02:58:39 +03:00
blobs_offset = ( ( blobs_offset + 7 ) & ~ 7 ) ;
2011-09-07 10:15:00 +04:00
SIVAL ( fixed , 48 , blobs_offset + SMB2_HDR_BODY + 56 ) ;
SIVAL ( fixed , 52 , blob . length ) ;
}
2011-05-05 20:12:07 +04:00
2011-09-05 20:22:57 +04:00
dyn_len = MAX ( 1 , blobs_offset + blob . length ) ;
2011-05-05 20:12:07 +04:00
dyn = talloc_zero_array ( state , uint8_t , dyn_len ) ;
if ( tevent_req_nomem ( dyn , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2011-09-05 20:22:57 +04:00
if ( name_utf16 ) {
memcpy ( dyn , name_utf16 , name_utf16_len ) ;
TALLOC_FREE ( name_utf16 ) ;
}
2011-05-05 20:12:07 +04:00
if ( blob . data ! = NULL ) {
2011-09-07 10:15:00 +04:00
memcpy ( dyn + blobs_offset ,
2011-05-05 20:12:07 +04:00
blob . data , blob . length ) ;
data_blob_free ( & blob ) ;
}
2013-09-18 05:50:42 +04:00
if ( smbXcli_conn_dfs_supported ( conn ) & &
smbXcli_tcon_is_dfs_share ( tcon ) )
{
additional_flags | = SMB2_HDR_FLAG_DFS ;
}
2013-08-13 12:25:52 +04:00
/*
* We use max_dyn_len = 0
* as we don ' t explicitly ask for any output length .
*
* But it ' s still possible for the server to return
* large create blobs .
*/
max_dyn_len = 0 ;
2012-05-10 20:34:40 +04:00
subreq = smb2cli_req_send ( state , ev , conn , SMB2_OP_CREATE ,
2013-09-18 05:50:42 +04:00
additional_flags , clear_flags ,
2012-05-10 20:34:40 +04:00
timeout_msec ,
2012-07-25 12:36:27 +04:00
tcon ,
2012-05-10 20:34:40 +04:00
session ,
2011-05-05 20:12:07 +04:00
state - > fixed , sizeof ( state - > fixed ) ,
2013-08-13 12:25:52 +04:00
dyn , dyn_len ,
max_dyn_len ) ;
2011-05-05 20:12:07 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , smb2cli_create_done , req ) ;
2014-06-20 14:37:14 +04:00
state - > subreq = subreq ;
tevent_req_set_cancel_fn ( req , smb2cli_create_cancel ) ;
2011-05-05 20:12:07 +04:00
return req ;
}
2014-06-20 14:37:14 +04:00
static bool smb2cli_create_cancel ( struct tevent_req * req )
{
struct smb2cli_create_state * state = tevent_req_data ( req ,
struct smb2cli_create_state ) ;
return tevent_req_cancel ( state - > subreq ) ;
}
2011-05-05 20:12:07 +04:00
static void smb2cli_create_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct smb2cli_create_state * state =
tevent_req_data ( req ,
struct smb2cli_create_state ) ;
NTSTATUS status ;
struct iovec * iov ;
uint8_t * body ;
uint32_t offset , length ;
2011-08-31 02:40:06 +04:00
static const struct smb2cli_req_expected_response expected [ ] = {
{
. status = NT_STATUS_OK ,
. body_size = 0x59
}
} ;
2011-05-05 20:12:07 +04:00
2012-05-03 14:05:13 +04:00
status = smb2cli_req_recv ( subreq , state , & iov ,
2011-08-31 02:40:06 +04:00
expected , ARRAY_SIZE ( expected ) ) ;
2012-09-28 21:48:26 +04:00
TALLOC_FREE ( subreq ) ;
2011-05-05 20:12:07 +04:00
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
body = ( uint8_t * ) iov [ 1 ] . iov_base ;
2013-08-08 02:01:50 +04:00
state - > cr . oplock_level = CVAL ( body , 2 ) ;
state - > cr . create_action = IVAL ( body , 4 ) ;
state - > cr . creation_time = BVAL ( body , 8 ) ;
state - > cr . last_access_time = BVAL ( body , 16 ) ;
state - > cr . last_write_time = BVAL ( body , 24 ) ;
state - > cr . change_time = BVAL ( body , 32 ) ;
state - > cr . allocation_size = BVAL ( body , 40 ) ;
state - > cr . end_of_file = BVAL ( body , 48 ) ;
state - > cr . file_attributes = IVAL ( body , 56 ) ;
2011-05-05 20:12:07 +04:00
state - > fid_persistent = BVAL ( body , 64 ) ;
state - > fid_volatile = BVAL ( body , 72 ) ;
offset = IVAL ( body , 80 ) ;
length = IVAL ( body , 84 ) ;
if ( ( offset ! = 0 ) & & ( length ! = 0 ) ) {
if ( ( offset ! = SMB2_HDR_BODY + 88 ) | |
( length > iov [ 2 ] . iov_len ) ) {
tevent_req_nterror (
req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
status = smb2_create_blob_parse (
state , data_blob_const ( iov [ 2 ] . iov_base , length ) ,
& state - > blobs ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
}
tevent_req_done ( req ) ;
}
NTSTATUS smb2cli_create_recv ( struct tevent_req * req ,
uint64_t * fid_persistent ,
2013-08-08 02:01:50 +04:00
uint64_t * fid_volatile ,
2014-06-27 13:32:34 +04:00
struct smb_create_returns * cr ,
TALLOC_CTX * mem_ctx ,
struct smb2_create_blobs * blobs )
2011-05-05 20:12:07 +04:00
{
struct smb2cli_create_state * state =
tevent_req_data ( req ,
struct smb2cli_create_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
return status ;
}
* fid_persistent = state - > fid_persistent ;
* fid_volatile = state - > fid_volatile ;
2013-08-08 02:01:50 +04:00
if ( cr ) {
* cr = state - > cr ;
}
2014-06-27 13:32:34 +04:00
if ( blobs ) {
blobs - > num_blobs = state - > blobs . num_blobs ;
blobs - > blobs = talloc_move ( mem_ctx , & state - > blobs . blobs ) ;
}
2011-05-05 20:12:07 +04:00
return NT_STATUS_OK ;
}
2012-05-10 20:34:40 +04:00
NTSTATUS smb2cli_create ( struct smbXcli_conn * conn ,
uint32_t timeout_msec ,
struct smbXcli_session * session ,
2012-07-24 00:32:49 +04:00
struct smbXcli_tcon * tcon ,
2011-05-05 20:12:07 +04:00
const char * filename ,
uint8_t oplock_level , /* SMB2_OPLOCK_LEVEL_* */
uint32_t impersonation_level , /* SMB2_IMPERSONATION_* */
uint32_t desired_access ,
uint32_t file_attributes ,
uint32_t share_access ,
uint32_t create_disposition ,
uint32_t create_options ,
struct smb2_create_blobs * blobs ,
uint64_t * fid_persistent ,
2013-08-08 02:01:50 +04:00
uint64_t * fid_volatile ,
2014-06-27 13:32:34 +04:00
struct smb_create_returns * cr ,
TALLOC_CTX * mem_ctx ,
struct smb2_create_blobs * ret_blobs )
2011-05-05 20:12:07 +04:00
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2012-05-14 11:56:47 +04:00
struct tevent_context * ev ;
2011-05-05 20:12:07 +04:00
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
2012-05-10 20:34:40 +04:00
if ( smbXcli_conn_has_async_calls ( conn ) ) {
2011-05-05 20:12:07 +04:00
/*
* Can ' t use sync call while an async call is in flight
*/
status = NT_STATUS_INVALID_PARAMETER ;
goto fail ;
}
2013-02-18 12:07:11 +04:00
ev = samba_tevent_context_init ( frame ) ;
2011-05-05 20:12:07 +04:00
if ( ev = = NULL ) {
goto fail ;
}
2012-07-24 00:32:49 +04:00
req = smb2cli_create_send ( frame , ev , conn , timeout_msec ,
session , tcon ,
filename , oplock_level ,
2011-05-05 20:12:07 +04:00
impersonation_level , desired_access ,
file_attributes , share_access ,
create_disposition , create_options ,
blobs ) ;
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
2014-06-27 13:32:34 +04:00
status = smb2cli_create_recv ( req , fid_persistent , fid_volatile , cr ,
mem_ctx , ret_blobs ) ;
2011-05-05 20:12:07 +04:00
fail :
TALLOC_FREE ( frame ) ;
return status ;
}