2005-11-25 05:25:37 +00:00
/*
Unix SMB / CIFS implementation .
dcerpc over SMB2 transport
Copyright ( C ) Andrew Tridgell 2005
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
2007-07-10 02:07:03 +00:00
the Free Software Foundation ; either version 3 of the License , or
2005-11-25 05:25:37 +00:00
( 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
2007-07-10 02:07:03 +00:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2005-11-25 05:25:37 +00:00
*/
# include "includes.h"
# include "libcli/raw/libcliraw.h"
# include "libcli/composite/composite.h"
# include "libcli/smb2/smb2.h"
# include "libcli/smb2/smb2_calls.h"
2006-02-23 15:52:24 +00:00
# include "libcli/raw/ioctl.h"
2006-03-18 15:42:57 +00:00
# include "librpc/rpc/dcerpc.h"
2008-04-02 04:53:27 +02:00
# include "librpc/rpc/dcerpc_proto.h"
2005-11-25 05:25:37 +00:00
/* transport private information used by SMB2 pipe transport */
struct smb2_private {
struct smb2_handle handle ;
struct smb2_tree * tree ;
const char * server_name ;
2007-04-27 05:45:53 +00:00
bool dead ;
2005-11-25 05:25:37 +00:00
} ;
/*
tell the dcerpc layer that the transport is dead
*/
static void pipe_dead ( struct dcerpc_connection * c , NTSTATUS status )
{
2007-09-07 13:31:15 +00:00
struct smb2_private * smb = ( struct smb2_private * ) c - > transport . private_data ;
2007-04-27 05:45:53 +00:00
if ( smb - > dead ) {
return ;
}
2007-05-05 19:05:42 +00:00
smb - > dead = true ;
2007-04-23 10:39:20 +00:00
if ( NT_STATUS_EQUAL ( NT_STATUS_UNSUCCESSFUL , status ) ) {
status = NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
}
if ( NT_STATUS_EQUAL ( NT_STATUS_OK , status ) ) {
status = NT_STATUS_END_OF_FILE ;
}
if ( c - > transport . recv_data ) {
c - > transport . recv_data ( c , NULL , status ) ;
}
2005-11-25 05:25:37 +00:00
}
/*
this holds the state of an in - flight call
*/
struct smb2_read_state {
struct dcerpc_connection * c ;
DATA_BLOB data ;
} ;
/*
called when a read request has completed
*/
static void smb2_read_callback ( struct smb2_request * req )
{
struct smb2_private * smb ;
struct smb2_read_state * state ;
struct smb2_read io ;
uint16_t frag_length ;
NTSTATUS status ;
2008-05-16 15:03:58 +10:00
state = talloc_get_type ( req - > async . private_data , struct smb2_read_state ) ;
2007-05-16 14:52:54 +00:00
smb = talloc_get_type ( state - > c - > transport . private_data , struct smb2_private ) ;
2005-11-25 05:25:37 +00:00
status = smb2_read_recv ( req , state , & io ) ;
if ( NT_STATUS_IS_ERR ( status ) ) {
pipe_dead ( state - > c , status ) ;
talloc_free ( state ) ;
return ;
}
2007-08-29 13:07:03 +00:00
if ( ! data_blob_append ( state , & state - > data ,
io . out . data . data , io . out . data . length ) ) {
pipe_dead ( state - > c , NT_STATUS_NO_MEMORY ) ;
2005-11-25 05:25:37 +00:00
talloc_free ( state ) ;
return ;
}
if ( state - > data . length < 16 ) {
DEBUG ( 0 , ( " dcerpc_smb2: short packet (length %d) in read callback! \n " ,
( int ) state - > data . length ) ) ;
pipe_dead ( state - > c , NT_STATUS_INFO_LENGTH_MISMATCH ) ;
talloc_free ( state ) ;
return ;
}
frag_length = dcerpc_get_frag_length ( & state - > data ) ;
if ( frag_length < = state - > data . length ) {
DATA_BLOB data = state - > data ;
struct dcerpc_connection * c = state - > c ;
talloc_steal ( c , data . data ) ;
talloc_free ( state ) ;
c - > transport . recv_data ( c , & data , NT_STATUS_OK ) ;
return ;
}
/* initiate another read request, as we only got part of a fragment */
ZERO_STRUCT ( io ) ;
2006-05-20 10:46:38 +00:00
io . in . file . handle = smb - > handle ;
2005-11-25 05:25:37 +00:00
io . in . length = MIN ( state - > c - > srv_max_xmit_frag ,
frag_length - state - > data . length ) ;
2005-11-25 05:46:46 +00:00
if ( io . in . length < 16 ) {
io . in . length = 16 ;
}
2005-11-25 05:25:37 +00:00
req = smb2_read_send ( smb - > tree , & io ) ;
if ( req = = NULL ) {
pipe_dead ( state - > c , NT_STATUS_NO_MEMORY ) ;
talloc_free ( state ) ;
return ;
}
req - > async . fn = smb2_read_callback ;
2008-05-16 15:03:58 +10:00
req - > async . private_data = state ;
2005-11-25 05:25:37 +00:00
}
/*
trigger a read request from the server , possibly with some initial
data in the read buffer
*/
static NTSTATUS send_read_request_continue ( struct dcerpc_connection * c , DATA_BLOB * blob )
{
2007-09-07 13:31:15 +00:00
struct smb2_private * smb = ( struct smb2_private * ) c - > transport . private_data ;
2005-11-25 05:25:37 +00:00
struct smb2_read io ;
struct smb2_read_state * state ;
struct smb2_request * req ;
state = talloc ( smb , struct smb2_read_state ) ;
if ( state = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
state - > c = c ;
if ( blob = = NULL ) {
state - > data = data_blob ( NULL , 0 ) ;
} else {
state - > data = * blob ;
talloc_steal ( state , state - > data . data ) ;
}
ZERO_STRUCT ( io ) ;
2006-05-20 10:46:38 +00:00
io . in . file . handle = smb - > handle ;
2005-11-25 05:25:37 +00:00
if ( state - > data . length > = 16 ) {
uint16_t frag_length = dcerpc_get_frag_length ( & state - > data ) ;
io . in . length = frag_length - state - > data . length ;
} else {
io . in . length = 0x2000 ;
}
req = smb2_read_send ( smb - > tree , & io ) ;
if ( req = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
req - > async . fn = smb2_read_callback ;
2008-05-16 15:03:58 +10:00
req - > async . private_data = state ;
2005-11-25 05:25:37 +00:00
return NT_STATUS_OK ;
}
/*
trigger a read request from the server
*/
static NTSTATUS send_read_request ( struct dcerpc_connection * c )
{
2007-09-07 13:31:15 +00:00
struct smb2_private * smb = ( struct smb2_private * ) c - > transport . private_data ;
2007-04-27 05:45:53 +00:00
if ( smb - > dead ) {
return NT_STATUS_CONNECTION_DISCONNECTED ;
}
2005-11-25 05:25:37 +00:00
return send_read_request_continue ( c , NULL ) ;
}
/*
this holds the state of an in - flight trans call
*/
struct smb2_trans_state {
struct dcerpc_connection * c ;
} ;
/*
called when a trans request has completed
*/
static void smb2_trans_callback ( struct smb2_request * req )
{
2008-05-16 15:03:58 +10:00
struct smb2_trans_state * state = talloc_get_type ( req - > async . private_data ,
2005-11-25 05:25:37 +00:00
struct smb2_trans_state ) ;
struct dcerpc_connection * c = state - > c ;
NTSTATUS status ;
2005-12-01 00:18:29 +00:00
struct smb2_ioctl io ;
2005-11-25 05:25:37 +00:00
2005-12-01 00:18:29 +00:00
status = smb2_ioctl_recv ( req , state , & io ) ;
2005-11-25 05:25:37 +00:00
if ( NT_STATUS_IS_ERR ( status ) ) {
pipe_dead ( c , status ) ;
return ;
}
if ( ! NT_STATUS_EQUAL ( status , STATUS_BUFFER_OVERFLOW ) ) {
DATA_BLOB data = io . out . out ;
talloc_steal ( c , data . data ) ;
talloc_free ( state ) ;
c - > transport . recv_data ( c , & data , NT_STATUS_OK ) ;
return ;
}
/* there is more to receive - setup a read */
send_read_request_continue ( c , & io . out . out ) ;
talloc_free ( state ) ;
}
/*
2005-12-01 00:18:29 +00:00
send a SMBtrans style request , using a named pipe read_write fsctl
2005-11-25 05:25:37 +00:00
*/
static NTSTATUS smb2_send_trans_request ( struct dcerpc_connection * c , DATA_BLOB * blob )
{
2007-05-16 14:52:54 +00:00
struct smb2_private * smb = talloc_get_type ( c - > transport . private_data ,
2005-11-25 05:25:37 +00:00
struct smb2_private ) ;
2005-12-01 00:18:29 +00:00
struct smb2_ioctl io ;
2005-11-25 05:25:37 +00:00
struct smb2_trans_state * state ;
struct smb2_request * req ;
state = talloc ( smb , struct smb2_trans_state ) ;
if ( state = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
state - > c = c ;
ZERO_STRUCT ( io ) ;
2006-05-20 10:46:38 +00:00
io . in . file . handle = smb - > handle ;
io . in . function = FSCTL_NAMED_PIPE_READ_WRITE ;
2009-04-17 19:57:39 +02:00
io . in . max_response_size = 0x2000 ;
2006-05-20 10:46:38 +00:00
io . in . flags = 1 ;
io . in . out = * blob ;
2005-11-25 05:25:37 +00:00
2005-12-01 00:18:29 +00:00
req = smb2_ioctl_send ( smb - > tree , & io ) ;
2005-11-25 05:25:37 +00:00
if ( req = = NULL ) {
talloc_free ( state ) ;
return NT_STATUS_NO_MEMORY ;
}
req - > async . fn = smb2_trans_callback ;
2008-05-16 15:03:58 +10:00
req - > async . private_data = state ;
2005-11-25 05:25:37 +00:00
talloc_steal ( state , req ) ;
return NT_STATUS_OK ;
}
/*
called when a write request has completed
*/
static void smb2_write_callback ( struct smb2_request * req )
{
2008-05-16 15:03:58 +10:00
struct dcerpc_connection * c = ( struct dcerpc_connection * ) req - > async . private_data ;
2005-11-25 05:25:37 +00:00
if ( ! NT_STATUS_IS_OK ( req - > status ) ) {
DEBUG ( 0 , ( " dcerpc_smb2: write callback error \n " ) ) ;
pipe_dead ( c , req - > status ) ;
}
smb2_request_destroy ( req ) ;
}
/*
send a packet to the server
*/
static NTSTATUS smb2_send_request ( struct dcerpc_connection * c , DATA_BLOB * blob ,
2007-09-25 16:05:08 +00:00
bool trigger_read )
2005-11-25 05:25:37 +00:00
{
2007-09-07 13:31:15 +00:00
struct smb2_private * smb = ( struct smb2_private * ) c - > transport . private_data ;
2005-11-25 05:25:37 +00:00
struct smb2_write io ;
struct smb2_request * req ;
2007-04-27 05:45:53 +00:00
if ( smb - > dead ) {
return NT_STATUS_CONNECTION_DISCONNECTED ;
}
2005-11-25 05:25:37 +00:00
if ( trigger_read ) {
return smb2_send_trans_request ( c , blob ) ;
}
ZERO_STRUCT ( io ) ;
2006-05-20 10:46:38 +00:00
io . in . file . handle = smb - > handle ;
io . in . data = * blob ;
2005-11-25 05:25:37 +00:00
req = smb2_write_send ( smb - > tree , & io ) ;
if ( req = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
req - > async . fn = smb2_write_callback ;
2008-05-16 15:03:58 +10:00
req - > async . private_data = c ;
2005-11-25 05:25:37 +00:00
return NT_STATUS_OK ;
}
2009-07-01 13:59:52 +10:00
static void free_request ( struct smb2_request * req )
{
talloc_free ( req ) ;
}
2005-11-25 05:25:37 +00:00
/*
shutdown SMB pipe connection
*/
2007-04-22 23:00:22 +00:00
static NTSTATUS smb2_shutdown_pipe ( struct dcerpc_connection * c , NTSTATUS status )
2005-11-25 05:25:37 +00:00
{
2007-09-07 13:31:15 +00:00
struct smb2_private * smb = ( struct smb2_private * ) c - > transport . private_data ;
2005-11-25 05:25:37 +00:00
struct smb2_close io ;
struct smb2_request * req ;
/* maybe we're still starting up */
2007-04-22 23:00:22 +00:00
if ( ! smb ) return status ;
2005-11-25 05:25:37 +00:00
ZERO_STRUCT ( io ) ;
2006-05-20 10:46:38 +00:00
io . in . file . handle = smb - > handle ;
2005-11-25 05:25:37 +00:00
req = smb2_close_send ( smb - > tree , & io ) ;
if ( req ! = NULL ) {
/* we don't care if this fails, so just free it if it succeeds */
2009-07-01 13:59:52 +10:00
req - > async . fn = free_request ;
2005-11-25 05:25:37 +00:00
}
talloc_free ( smb ) ;
2007-04-22 23:00:22 +00:00
return status ;
2005-11-25 05:25:37 +00:00
}
/*
return SMB server name
*/
static const char * smb2_peer_name ( struct dcerpc_connection * c )
{
2007-05-16 14:52:54 +00:00
struct smb2_private * smb = talloc_get_type ( c - > transport . private_data ,
2005-11-25 05:25:37 +00:00
struct smb2_private ) ;
return smb - > server_name ;
}
2006-03-25 11:39:09 +00:00
/*
return remote name we make the actual connection ( good for kerberos )
*/
static const char * smb2_target_hostname ( struct dcerpc_connection * c )
{
2007-05-16 14:52:54 +00:00
struct smb2_private * smb = talloc_get_type ( c - > transport . private_data ,
2006-03-25 11:39:09 +00:00
struct smb2_private ) ;
return smb - > tree - > session - > transport - > socket - > hostname ;
}
2005-11-25 05:25:37 +00:00
/*
fetch the user session key
*/
static NTSTATUS smb2_session_key ( struct dcerpc_connection * c , DATA_BLOB * session_key )
{
2007-05-16 14:52:54 +00:00
struct smb2_private * smb = talloc_get_type ( c - > transport . private_data ,
2005-11-25 05:25:37 +00:00
struct smb2_private ) ;
2008-06-07 11:10:23 -07:00
* session_key = smb - > tree - > session - > session_key ;
2005-11-25 05:25:37 +00:00
if ( session_key - > data = = NULL ) {
return NT_STATUS_NO_USER_SESSION_KEY ;
}
return NT_STATUS_OK ;
}
struct pipe_open_smb2_state {
struct dcerpc_connection * c ;
struct composite_context * ctx ;
} ;
static void pipe_open_recv ( struct smb2_request * req ) ;
2007-02-26 05:37:19 +00:00
struct composite_context * dcerpc_pipe_open_smb2_send ( struct dcerpc_pipe * p ,
2005-11-25 05:25:37 +00:00
struct smb2_tree * tree ,
const char * pipe_name )
{
struct composite_context * ctx ;
struct pipe_open_smb2_state * state ;
struct smb2_create io ;
struct smb2_request * req ;
2007-02-26 05:37:19 +00:00
struct dcerpc_connection * c = p - > conn ;
2005-11-25 05:25:37 +00:00
2006-07-30 18:36:17 +00:00
ctx = composite_create ( c , c - > event_ctx ) ;
if ( ctx = = NULL ) return NULL ;
2005-11-25 05:25:37 +00:00
state = talloc ( ctx , struct pipe_open_smb2_state ) ;
2006-07-30 18:36:17 +00:00
if ( composite_nomem ( state , ctx ) ) return ctx ;
ctx - > private_data = state ;
2005-11-25 05:25:37 +00:00
state - > c = c ;
state - > ctx = ctx ;
ZERO_STRUCT ( io ) ;
2008-02-13 17:24:23 +11:00
io . in . desired_access =
2005-11-25 05:25:37 +00:00
SEC_STD_READ_CONTROL |
SEC_FILE_READ_ATTRIBUTE |
SEC_FILE_WRITE_ATTRIBUTE |
SEC_STD_SYNCHRONIZE |
SEC_FILE_READ_EA |
SEC_FILE_WRITE_EA |
SEC_FILE_READ_DATA |
SEC_FILE_WRITE_DATA |
SEC_FILE_APPEND_DATA ;
io . in . share_access =
NTCREATEX_SHARE_ACCESS_READ |
NTCREATEX_SHARE_ACCESS_WRITE ;
2008-02-13 17:24:23 +11:00
io . in . create_disposition = NTCREATEX_DISP_OPEN ;
2005-11-25 05:46:46 +00:00
io . in . create_options =
NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
2008-07-16 15:28:54 +10:00
NTCREATEX_OPTIONS_NO_RECALL ;
2008-02-13 17:24:23 +11:00
io . in . impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION ;
2005-11-25 05:25:37 +00:00
if ( ( strncasecmp ( pipe_name , " /pipe/ " , 6 ) = = 0 ) | |
( strncasecmp ( pipe_name , " \\ pipe \\ " , 6 ) = = 0 ) ) {
pipe_name + = 6 ;
}
io . in . fname = pipe_name ;
req = smb2_create_send ( tree , & io ) ;
2006-07-30 18:36:17 +00:00
composite_continue_smb2 ( ctx , req , pipe_open_recv , state ) ;
2005-11-25 05:25:37 +00:00
return ctx ;
}
static void pipe_open_recv ( struct smb2_request * req )
{
struct pipe_open_smb2_state * state =
2008-05-16 15:03:58 +10:00
talloc_get_type ( req - > async . private_data ,
2005-11-25 05:25:37 +00:00
struct pipe_open_smb2_state ) ;
struct composite_context * ctx = state - > ctx ;
struct dcerpc_connection * c = state - > c ;
struct smb2_tree * tree = req - > tree ;
struct smb2_private * smb ;
struct smb2_create io ;
ctx - > status = smb2_create_recv ( req , state , & io ) ;
if ( ! composite_is_ok ( ctx ) ) return ;
/*
fill in the transport methods
*/
c - > transport . transport = NCACN_NP ;
2007-05-16 14:52:54 +00:00
c - > transport . private_data = NULL ;
2005-11-25 05:25:37 +00:00
c - > transport . shutdown_pipe = smb2_shutdown_pipe ;
c - > transport . peer_name = smb2_peer_name ;
2006-03-25 11:39:09 +00:00
c - > transport . target_hostname = smb2_target_hostname ;
2005-11-25 05:25:37 +00:00
c - > transport . send_request = smb2_send_request ;
c - > transport . send_read = send_read_request ;
c - > transport . recv_data = NULL ;
/* Over-ride the default session key with the SMB session key */
c - > security_state . session_key = smb2_session_key ;
smb = talloc ( c , struct smb2_private ) ;
if ( composite_nomem ( smb , ctx ) ) return ;
2006-05-20 10:46:38 +00:00
smb - > handle = io . out . file . handle ;
2005-11-25 05:25:37 +00:00
smb - > tree = talloc_reference ( smb , tree ) ;
smb - > server_name = strupper_talloc ( smb ,
tree - > session - > transport - > socket - > hostname ) ;
if ( composite_nomem ( smb - > server_name , ctx ) ) return ;
2007-04-27 05:45:53 +00:00
smb - > dead = false ;
2005-11-25 05:25:37 +00:00
2007-05-16 14:52:54 +00:00
c - > transport . private_data = smb ;
2005-11-25 05:25:37 +00:00
composite_done ( ctx ) ;
}
NTSTATUS dcerpc_pipe_open_smb2_recv ( struct composite_context * c )
{
NTSTATUS status = composite_wait ( c ) ;
talloc_free ( c ) ;
return status ;
}
2007-02-26 05:37:19 +00:00
NTSTATUS dcerpc_pipe_open_smb2 ( struct dcerpc_pipe * p ,
2005-11-25 05:25:37 +00:00
struct smb2_tree * tree ,
const char * pipe_name )
{
2007-02-26 05:37:19 +00:00
struct composite_context * ctx = dcerpc_pipe_open_smb2_send ( p , tree , pipe_name ) ;
2005-11-25 05:25:37 +00:00
return dcerpc_pipe_open_smb2_recv ( ctx ) ;
}
/*
return the SMB2 tree used for a dcerpc over SMB2 pipe
*/
struct smb2_tree * dcerpc_smb2_tree ( struct dcerpc_connection * c )
{
2007-05-16 14:52:54 +00:00
struct smb2_private * smb = talloc_get_type ( c - > transport . private_data ,
2005-11-25 05:25:37 +00:00
struct smb2_private ) ;
return smb - > tree ;
}