2003-10-28 13:24:13 +03:00
/*
Unix SMB / CIFS implementation .
2003-11-24 15:40:47 +03:00
dcerpc over SMB transport
2003-10-28 13:24:13 +03:00
2003-11-03 09:22:45 +03:00
Copyright ( C ) Tim Potter 2003
Copyright ( C ) Andrew Tridgell 2003
2003-10-28 13:24:13 +03: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 2 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 , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
2003-11-24 14:45:33 +03:00
/* transport private information used by SMB pipe transport */
struct smb_private {
uint16 fnum ;
struct cli_tree * tree ;
} ;
2003-11-04 12:10:31 +03:00
2003-11-24 14:45:33 +03:00
static struct cli_request * dcerpc_raw_send ( struct dcerpc_pipe * p , DATA_BLOB * blob )
2003-10-28 13:24:13 +03:00
{
2003-11-24 14:45:33 +03:00
struct smb_private * smb = p - > transport . private ;
2003-10-28 13:24:13 +03:00
struct smb_trans2 trans ;
uint16 setup [ 2 ] ;
2003-11-03 09:22:45 +03:00
struct cli_request * req ;
TALLOC_CTX * mem_ctx ;
2003-10-28 13:24:13 +03:00
2003-11-03 09:22:45 +03:00
mem_ctx = talloc_init ( " dcerpc_raw_send " ) ;
if ( ! mem_ctx ) return NULL ;
2003-10-28 13:24:13 +03:00
2003-11-03 09:22:45 +03:00
trans . in . data = * blob ;
trans . in . params = data_blob ( NULL , 0 ) ;
setup [ 0 ] = TRANSACT_DCERPCCMD ;
2003-11-24 14:45:33 +03:00
setup [ 1 ] = smb - > fnum ;
2003-10-28 13:24:13 +03:00
2003-11-03 09:22:45 +03:00
trans . in . max_param = 0 ;
trans . in . max_data = 0x8000 ;
2003-11-05 01:42:00 +03:00
trans . in . max_setup = 0 ;
2003-10-28 13:24:13 +03:00
trans . in . setup_count = 2 ;
2003-11-05 01:42:00 +03:00
trans . in . flags = 0 ;
trans . in . timeout = 0 ;
2003-10-28 13:24:13 +03:00
trans . in . setup = setup ;
trans . in . trans_name = " \\ PIPE \\ " ;
2003-11-24 14:45:33 +03:00
req = smb_raw_trans_send ( smb - > tree , & trans ) ;
2003-10-28 13:24:13 +03:00
2003-11-03 09:22:45 +03:00
talloc_destroy ( mem_ctx ) ;
2003-10-28 13:24:13 +03:00
2003-11-03 09:22:45 +03:00
return req ;
2003-10-28 13:24:13 +03:00
}
2003-11-04 05:28:08 +03:00
2003-11-24 14:45:33 +03:00
static NTSTATUS dcerpc_raw_recv ( struct dcerpc_pipe * p ,
struct cli_request * req ,
TALLOC_CTX * mem_ctx ,
DATA_BLOB * blob )
2003-10-28 13:24:13 +03:00
{
2003-11-24 14:45:33 +03:00
struct smb_private * smb = p - > transport . private ;
2003-10-28 13:24:13 +03:00
struct smb_trans2 trans ;
2003-11-03 09:22:45 +03:00
NTSTATUS status ;
2003-11-04 05:28:08 +03:00
uint16 frag_length ;
DATA_BLOB payload ;
2003-10-28 13:24:13 +03:00
2003-11-03 09:22:45 +03:00
status = smb_raw_trans_recv ( req , mem_ctx , & trans ) ;
2003-11-04 05:28:08 +03:00
/* STATUS_BUFFER_OVERFLOW means that there is more data
available via SMBreadX */
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , STATUS_BUFFER_OVERFLOW ) ) {
2003-11-03 09:22:45 +03:00
return status ;
}
2003-10-28 13:24:13 +03:00
2003-11-04 05:28:08 +03:00
payload = trans . out . data ;
if ( trans . out . data . length < 16 | |
! NT_STATUS_EQUAL ( status , STATUS_BUFFER_OVERFLOW ) ) {
goto done ;
}
/* we might have recieved a partial fragment, in which case we
need to pull the rest of it */
2003-12-16 12:02:58 +03:00
frag_length = dcerpc_get_frag_length ( & payload ) ;
2003-11-04 05:28:08 +03:00
if ( frag_length < = payload . length ) {
goto done ;
}
/* make sure the payload can hold the whole fragment */
payload . data = talloc_realloc ( mem_ctx , payload . data , frag_length ) ;
if ( ! payload . data ) {
return NT_STATUS_NO_MEMORY ;
}
/* the rest of the data is available via SMBreadX */
while ( frag_length > payload . length ) {
uint32 n ;
union smb_read io ;
n = frag_length - payload . length ;
if ( n > 0xFF00 ) {
n = 0xFF00 ;
}
io . generic . level = RAW_READ_READX ;
2003-11-24 14:45:33 +03:00
io . readx . in . fnum = smb - > fnum ;
2003-11-04 05:28:08 +03:00
io . readx . in . mincnt = n ;
io . readx . in . maxcnt = n ;
io . readx . in . offset = 0 ;
io . readx . in . remaining = 0 ;
io . readx . out . data = payload . data + payload . length ;
2003-11-24 14:45:33 +03:00
status = smb_raw_read ( smb - > tree , & io ) ;
2003-11-04 05:28:08 +03:00
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , STATUS_BUFFER_OVERFLOW ) ) {
break ;
}
n = io . readx . out . nread ;
if ( n = = 0 ) {
status = NT_STATUS_UNSUCCESSFUL ;
break ;
}
payload . length + = n ;
/* if the SMBreadX returns NT_STATUS_OK then there
isn ' t any more data to be read */
if ( NT_STATUS_IS_OK ( status ) ) {
break ;
}
}
done :
2003-11-03 09:22:45 +03:00
if ( blob ) {
2003-11-04 05:28:08 +03:00
* blob = payload ;
2003-11-03 09:22:45 +03:00
}
2003-10-28 13:24:13 +03:00
2003-11-03 09:22:45 +03:00
return status ;
2003-10-28 13:24:13 +03:00
}
2003-11-24 14:45:33 +03:00
static NTSTATUS smb_full_request ( struct dcerpc_pipe * p ,
TALLOC_CTX * mem_ctx ,
DATA_BLOB * request_blob ,
DATA_BLOB * reply_blob )
2003-10-28 13:24:13 +03:00
{
2003-11-03 09:22:45 +03:00
struct cli_request * req ;
req = dcerpc_raw_send ( p , request_blob ) ;
return dcerpc_raw_recv ( p , req , mem_ctx , reply_blob ) ;
2003-10-28 13:24:13 +03:00
}
2003-11-03 09:22:45 +03:00
2003-11-04 05:28:08 +03:00
/*
retrieve a secondary pdu from a pipe
*/
2004-02-03 14:05:36 +03:00
static NTSTATUS smb_secondary_request ( struct dcerpc_pipe * p ,
2003-11-24 14:45:33 +03:00
TALLOC_CTX * mem_ctx ,
DATA_BLOB * blob )
2003-11-04 05:28:08 +03:00
{
2003-11-24 14:45:33 +03:00
struct smb_private * smb = p - > transport . private ;
2003-11-04 05:28:08 +03:00
union smb_read io ;
uint32 n = 0x2000 ;
uint32 frag_length ;
NTSTATUS status ;
* blob = data_blob_talloc ( mem_ctx , NULL , n ) ;
if ( ! blob - > data ) {
return NT_STATUS_NO_MEMORY ;
}
io . generic . level = RAW_READ_READX ;
2003-11-24 14:45:33 +03:00
io . readx . in . fnum = smb - > fnum ;
2003-11-04 05:28:08 +03:00
io . readx . in . mincnt = n ;
io . readx . in . maxcnt = n ;
io . readx . in . offset = 0 ;
io . readx . in . remaining = 0 ;
io . readx . out . data = blob - > data ;
2003-11-24 14:45:33 +03:00
status = smb_raw_read ( smb - > tree , & io ) ;
2003-11-04 05:28:08 +03:00
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , STATUS_BUFFER_OVERFLOW ) ) {
return status ;
}
blob - > length = io . readx . out . nread ;
if ( blob - > length < 16 ) {
return status ;
}
2003-12-16 12:02:58 +03:00
frag_length = dcerpc_get_frag_length ( blob ) ;
2003-11-04 05:28:08 +03:00
if ( frag_length < = blob - > length ) {
return status ;
}
blob - > data = talloc_realloc ( mem_ctx , blob - > data , frag_length ) ;
if ( ! blob - > data ) {
return NT_STATUS_NO_MEMORY ;
}
while ( frag_length > blob - > length & &
NT_STATUS_EQUAL ( status , STATUS_BUFFER_OVERFLOW ) ) {
n = frag_length - blob - > length ;
if ( n > 0xFF00 ) {
n = 0xFF00 ;
}
io . readx . in . mincnt = n ;
io . readx . in . maxcnt = n ;
io . readx . out . data = blob - > data + blob - > length ;
2003-11-24 14:45:33 +03:00
status = smb_raw_read ( smb - > tree , & io ) ;
2003-11-04 05:28:08 +03:00
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , STATUS_BUFFER_OVERFLOW ) ) {
return status ;
}
n = io . readx . out . nread ;
blob - > length + = n ;
}
return status ;
}
2003-11-04 06:38:46 +03:00
/*
send an initial pdu in a multi - pdu sequence
*/
2003-11-24 14:45:33 +03:00
static NTSTATUS smb_initial_request ( struct dcerpc_pipe * p ,
TALLOC_CTX * mem_ctx ,
DATA_BLOB * blob )
2003-11-04 06:38:46 +03:00
{
2003-11-24 14:45:33 +03:00
struct smb_private * smb = p - > transport . private ;
2003-11-04 06:38:46 +03:00
union smb_write io ;
NTSTATUS status ;
io . generic . level = RAW_WRITE_WRITEX ;
2003-11-24 14:45:33 +03:00
io . writex . in . fnum = smb - > fnum ;
2003-11-04 06:38:46 +03:00
io . writex . in . offset = 0 ;
io . writex . in . wmode = PIPE_START_MESSAGE ;
io . writex . in . remaining = blob - > length ;
io . writex . in . count = blob - > length ;
io . writex . in . data = blob - > data ;
2003-11-24 14:45:33 +03:00
status = smb_raw_write ( smb - > tree , & io ) ;
2003-11-04 06:38:46 +03:00
if ( NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/* make sure it accepted it all */
if ( io . writex . out . nwritten ! = blob - > length ) {
return NT_STATUS_UNSUCCESSFUL ;
}
return status ;
}
2003-11-24 14:45:33 +03:00
/*
shutdown SMB pipe connection
*/
static NTSTATUS smb_shutdown_pipe ( struct dcerpc_pipe * p )
{
struct smb_private * smb = p - > transport . private ;
union smb_close c ;
/* maybe we're still starting up */
if ( ! smb ) return NT_STATUS_OK ;
c . close . level = RAW_CLOSE_CLOSE ;
c . close . in . fnum = smb - > fnum ;
c . close . in . write_time = 0 ;
smb_raw_close ( smb - > tree , & c ) ;
cli_tree_close ( smb - > tree ) ;
return NT_STATUS_OK ;
}
/*
return SMB server name
*/
static const char * smb_peer_name ( struct dcerpc_pipe * p )
{
struct smb_private * smb = p - > transport . private ;
return smb - > tree - > session - > transport - > called . name ;
}
/*
open a rpc connection to a named pipe
*/
NTSTATUS dcerpc_pipe_open_smb ( struct dcerpc_pipe * * p ,
struct cli_tree * tree ,
2003-11-26 04:16:41 +03:00
const char * pipe_name )
2003-11-24 14:45:33 +03:00
{
struct smb_private * smb ;
NTSTATUS status ;
char * name = NULL ;
union smb_open io ;
TALLOC_CTX * mem_ctx ;
2003-12-11 12:07:45 +03:00
asprintf ( & name , " \\ %s " , pipe_name ) ;
2003-11-24 14:45:33 +03:00
if ( ! name ) {
return NT_STATUS_NO_MEMORY ;
}
io . ntcreatex . level = RAW_OPEN_NTCREATEX ;
io . ntcreatex . in . flags = 0 ;
io . ntcreatex . in . root_fid = 0 ;
io . ntcreatex . in . access_mask =
STD_RIGHT_READ_CONTROL_ACCESS |
SA_RIGHT_FILE_WRITE_ATTRIBUTES |
SA_RIGHT_FILE_WRITE_EA |
GENERIC_RIGHTS_FILE_READ |
GENERIC_RIGHTS_FILE_WRITE ;
io . ntcreatex . in . file_attr = 0 ;
io . ntcreatex . in . alloc_size = 0 ;
io . ntcreatex . in . share_access =
NTCREATEX_SHARE_ACCESS_READ |
NTCREATEX_SHARE_ACCESS_WRITE ;
io . ntcreatex . in . open_disposition = NTCREATEX_DISP_OPEN ;
io . ntcreatex . in . create_options = 0 ;
io . ntcreatex . in . impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION ;
io . ntcreatex . in . security_flags = 0 ;
io . ntcreatex . in . fname = name ;
mem_ctx = talloc_init ( " torture_rpc_connection " ) ;
if ( ! mem_ctx ) {
free ( name ) ;
return NT_STATUS_NO_MEMORY ;
}
status = smb_raw_open ( tree , mem_ctx , & io ) ;
free ( name ) ;
talloc_destroy ( mem_ctx ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2003-11-24 15:40:47 +03:00
if ( ! ( * p = dcerpc_pipe_init ( ) ) ) {
2003-11-24 14:45:33 +03:00
return NT_STATUS_NO_MEMORY ;
}
/*
fill in the transport methods
*/
2004-01-20 09:07:09 +03:00
( * p ) - > transport . transport = NCACN_NP ;
2003-11-24 14:45:33 +03:00
( * p ) - > transport . private = NULL ;
( * p ) - > transport . full_request = smb_full_request ;
( * p ) - > transport . secondary_request = smb_secondary_request ;
( * p ) - > transport . initial_request = smb_initial_request ;
( * p ) - > transport . shutdown_pipe = smb_shutdown_pipe ;
( * p ) - > transport . peer_name = smb_peer_name ;
smb = talloc ( ( * p ) - > mem_ctx , sizeof ( * smb ) ) ;
if ( ! smb ) {
dcerpc_pipe_close ( * p ) ;
return NT_STATUS_NO_MEMORY ;
}
smb - > fnum = io . ntcreatex . out . fnum ;
smb - > tree = tree ;
( * p ) - > transport . private = smb ;
tree - > reference_count + + ;
2003-11-24 15:40:47 +03:00
return NT_STATUS_OK ;
2003-11-24 14:45:33 +03:00
}
2004-01-20 09:07:09 +03:00
/*
return the SMB tree used for a dcerpc over SMB pipe
*/
struct cli_tree * dcerpc_smb_tree ( struct dcerpc_pipe * p )
{
struct smb_private * smb = p - > transport . private ;
if ( p - > transport . transport ! = NCACN_NP ) {
return NULL ;
}
return smb - > tree ;
}