2009-07-28 23:06:11 +04:00
/*
Unix SMB / CIFS implementation .
Provide parent - > child communication based on NDR marshalling
Copyright ( C ) Volker Lendecke 2009
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/>.
*/
/*
* This file implements an RPC between winbind parent and child processes ,
* leveraging the autogenerated marshalling routines for MSRPC . This is not
* MSRPC , as it does not go through the whole DCERPC fragmentation , we just
* leverage much the same infrastructure we already have for it .
*/
# include "includes.h"
# include "winbindd/winbindd.h"
# include "winbindd/winbindd_proto.h"
# include "librpc/gen_ndr/srv_wbint.h"
struct wb_ndr_transport_priv {
2009-08-24 02:13:02 +04:00
struct winbindd_domain * domain ;
2009-07-28 23:06:11 +04:00
struct winbindd_child * child ;
} ;
2010-08-06 13:50:09 +04:00
struct wbint_bh_state {
struct rpc_pipe_client * rpc_cli ;
} ;
static bool wbint_bh_is_connected ( struct dcerpc_binding_handle * h )
{
struct wbint_bh_state * hs = dcerpc_binding_handle_data ( h ,
struct wbint_bh_state ) ;
if ( ! hs - > rpc_cli ) {
return false ;
}
return true ;
}
struct wbint_bh_raw_call_state {
struct winbindd_domain * domain ;
uint32_t opnum ;
DATA_BLOB in_data ;
struct winbindd_request request ;
struct winbindd_response * response ;
DATA_BLOB out_data ;
} ;
static void wbint_bh_raw_call_done ( struct tevent_req * subreq ) ;
static struct tevent_req * wbint_bh_raw_call_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct dcerpc_binding_handle * h ,
const struct GUID * object ,
uint32_t opnum ,
uint32_t in_flags ,
const uint8_t * in_data ,
size_t in_length )
{
struct wbint_bh_state * hs =
dcerpc_binding_handle_data ( h ,
struct wbint_bh_state ) ;
struct wb_ndr_transport_priv * transport =
talloc_get_type_abort ( hs - > rpc_cli - > transport - > priv ,
struct wb_ndr_transport_priv ) ;
struct tevent_req * req ;
struct wbint_bh_raw_call_state * state ;
bool ok ;
struct tevent_req * subreq ;
req = tevent_req_create ( mem_ctx , & state ,
struct wbint_bh_raw_call_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > domain = transport - > domain ;
state - > opnum = opnum ;
state - > in_data . data = discard_const_p ( uint8_t , in_data ) ;
state - > in_data . length = in_length ;
ok = wbint_bh_is_connected ( h ) ;
if ( ! ok ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_CONNECTION ) ;
return tevent_req_post ( req , ev ) ;
}
if ( ( state - > domain ! = NULL )
& & wcache_fetch_ndr ( state , state - > domain , state - > opnum ,
& state - > in_data , & state - > out_data ) ) {
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
state - > request . cmd = WINBINDD_DUAL_NDRCMD ;
state - > request . data . ndrcmd = state - > opnum ;
state - > request . extra_data . data = ( char * ) state - > in_data . data ;
state - > request . extra_len = state - > in_data . length ;
subreq = wb_child_request_send ( state , ev , transport - > child ,
& state - > request ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , wbint_bh_raw_call_done , req ) ;
return req ;
}
static void wbint_bh_raw_call_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct wbint_bh_raw_call_state * state =
tevent_req_data ( req ,
struct wbint_bh_raw_call_state ) ;
int ret , err ;
ret = wb_child_request_recv ( subreq , state , & state - > response , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
NTSTATUS status = map_nt_error_from_unix ( err ) ;
tevent_req_nterror ( req , status ) ;
return ;
}
state - > out_data = data_blob_talloc ( state ,
state - > response - > extra_data . data ,
state - > response - > length - sizeof ( struct winbindd_response ) ) ;
if ( state - > response - > extra_data . data & & ! state - > out_data . data ) {
tevent_req_nomem ( NULL , req ) ;
return ;
}
if ( state - > domain ! = NULL ) {
wcache_store_ndr ( state - > domain , state - > opnum ,
& state - > in_data , & state - > out_data ) ;
}
tevent_req_done ( req ) ;
}
static NTSTATUS wbint_bh_raw_call_recv ( struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
uint8_t * * out_data ,
size_t * out_length ,
uint32_t * out_flags )
{
struct wbint_bh_raw_call_state * state =
tevent_req_data ( req ,
struct wbint_bh_raw_call_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
* out_data = talloc_move ( mem_ctx , & state - > out_data . data ) ;
* out_length = state - > out_data . length ;
* out_flags = 0 ;
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
struct wbint_bh_disconnect_state {
uint8_t _dummy ;
} ;
static struct tevent_req * wbint_bh_disconnect_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct dcerpc_binding_handle * h )
{
struct wbint_bh_state * hs = dcerpc_binding_handle_data ( h ,
struct wbint_bh_state ) ;
struct tevent_req * req ;
struct wbint_bh_disconnect_state * state ;
bool ok ;
req = tevent_req_create ( mem_ctx , & state ,
struct wbint_bh_disconnect_state ) ;
if ( req = = NULL ) {
return NULL ;
}
ok = wbint_bh_is_connected ( h ) ;
if ( ! ok ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_CONNECTION ) ;
return tevent_req_post ( req , ev ) ;
}
/*
* TODO : do a real async disconnect . . .
*
* For now the caller needs to free rpc_cli
*/
hs - > rpc_cli = NULL ;
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
static NTSTATUS wbint_bh_disconnect_recv ( struct tevent_req * req )
{
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
static bool wbint_bh_ref_alloc ( struct dcerpc_binding_handle * h )
{
return true ;
}
static void wbint_bh_do_ndr_print ( struct dcerpc_binding_handle * h ,
int ndr_flags ,
const void * _struct_ptr ,
const struct ndr_interface_call * call )
{
void * struct_ptr = discard_const ( _struct_ptr ) ;
if ( DEBUGLEVEL < 10 ) {
return ;
}
if ( ndr_flags & NDR_IN ) {
ndr_print_function_debug ( call - > ndr_print ,
call - > name ,
ndr_flags ,
struct_ptr ) ;
}
if ( ndr_flags & NDR_OUT ) {
ndr_print_function_debug ( call - > ndr_print ,
call - > name ,
ndr_flags ,
struct_ptr ) ;
}
}
static const struct dcerpc_binding_handle_ops wbint_bh_ops = {
. name = " wbint " ,
. is_connected = wbint_bh_is_connected ,
. raw_call_send = wbint_bh_raw_call_send ,
. raw_call_recv = wbint_bh_raw_call_recv ,
. disconnect_send = wbint_bh_disconnect_send ,
. disconnect_recv = wbint_bh_disconnect_recv ,
. ref_alloc = wbint_bh_ref_alloc ,
. do_ndr_print = wbint_bh_do_ndr_print ,
} ;
/* initialise a wbint binding handle */
static struct dcerpc_binding_handle * wbint_binding_handle ( struct rpc_pipe_client * rpc_cli )
{
struct dcerpc_binding_handle * h ;
struct wbint_bh_state * hs ;
h = dcerpc_binding_handle_create ( rpc_cli ,
& wbint_bh_ops ,
NULL ,
NULL , /* TODO */
& hs ,
struct wbint_bh_state ,
__location__ ) ;
if ( h = = NULL ) {
return NULL ;
}
hs - > rpc_cli = rpc_cli ;
return h ;
}
2009-07-28 23:06:11 +04:00
struct rpc_pipe_client * wbint_rpccli_create ( TALLOC_CTX * mem_ctx ,
2009-08-24 02:13:02 +04:00
struct winbindd_domain * domain ,
2009-07-28 23:06:11 +04:00
struct winbindd_child * child )
{
struct rpc_pipe_client * result ;
struct wb_ndr_transport_priv * transp ;
result = talloc ( mem_ctx , struct rpc_pipe_client ) ;
if ( result = = NULL ) {
return NULL ;
}
result - > abstract_syntax = ndr_table_wbint . syntax_id ;
result - > transfer_syntax = ndr_transfer_syntax ;
result - > max_xmit_frag = RPC_MAX_PDU_FRAG_LEN ;
result - > max_recv_frag = RPC_MAX_PDU_FRAG_LEN ;
result - > desthost = NULL ;
result - > srv_name_slash = NULL ;
/*
* Initialize a fake transport . Due to our own wb_ndr_dispatch
* function we don ' t use all the fragmentation engine in
* cli_pipe , which would use all the _read and _write
* functions in rpc_cli_transport . But we need a place to
* store the child struct in , and we ' re re - using
* result - > transport - > priv for that .
*/
result - > transport = talloc_zero ( result , struct rpc_cli_transport ) ;
if ( result - > transport = = NULL ) {
TALLOC_FREE ( result ) ;
return NULL ;
}
transp = talloc ( result - > transport , struct wb_ndr_transport_priv ) ;
if ( transp = = NULL ) {
TALLOC_FREE ( result ) ;
return NULL ;
}
2009-08-24 02:13:02 +04:00
transp - > domain = domain ;
2009-07-28 23:06:11 +04:00
transp - > child = child ;
result - > transport - > priv = transp ;
2010-08-06 13:50:09 +04:00
result - > binding_handle = wbint_binding_handle ( result ) ;
if ( result - > binding_handle = = NULL ) {
TALLOC_FREE ( result ) ;
return NULL ;
}
2009-07-28 23:06:11 +04:00
return result ;
}
enum winbindd_result winbindd_dual_ndrcmd ( struct winbindd_domain * domain ,
struct winbindd_cli_state * state )
{
2010-07-28 12:28:36 +04:00
struct pipes_struct p ;
2009-07-28 23:06:11 +04:00
struct api_struct * fns ;
int num_fns ;
bool ret ;
wbint_get_pipe_fns ( & fns , & num_fns ) ;
if ( state - > request - > data . ndrcmd > = num_fns ) {
return WINBINDD_ERROR ;
}
2009-08-22 19:14:32 +04:00
DEBUG ( 10 , ( " winbindd_dual_ndrcmd: Running command %s (%s) \n " ,
fns [ state - > request - > data . ndrcmd ] . name ,
domain ? domain - > name : " no domain " ) ) ;
2009-07-28 23:06:11 +04:00
ZERO_STRUCT ( p ) ;
p . mem_ctx = talloc_stackframe ( ) ;
2010-07-15 18:28:59 +04:00
p . in_data . data = data_blob_const ( state - > request - > extra_data . data ,
state - > request - > extra_len ) ;
2009-07-28 23:06:11 +04:00
ret = fns [ state - > request - > data . ndrcmd ] . fn ( & p ) ;
if ( ! ret ) {
2010-07-13 23:43:44 +04:00
TALLOC_FREE ( p . mem_ctx ) ;
2009-07-28 23:06:11 +04:00
return WINBINDD_ERROR ;
}
state - > response - > extra_data . data =
2010-07-13 23:43:44 +04:00
talloc_move ( state - > mem_ctx , & p . out_data . rdata . data ) ;
state - > response - > length + = p . out_data . rdata . length ;
p . out_data . rdata . length = 0 ;
TALLOC_FREE ( p . mem_ctx ) ;
2009-07-28 23:06:11 +04:00
if ( state - > response - > extra_data . data = = NULL ) {
return WINBINDD_ERROR ;
}
return WINBINDD_OK ;
}