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"
2011-03-25 00:33:07 +03:00
# include "ntdomain.h"
2014-05-08 04:17:32 +04:00
# include "librpc/gen_ndr/srv_winbind.h"
2009-07-28 23:06:11 +04:00
2010-08-12 17:28:27 +04:00
struct wbint_bh_state {
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
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 ) ;
2010-08-12 17:28:27 +04:00
if ( ! hs - > child ) {
2010-08-06 13:50:09 +04:00
return false ;
}
return true ;
}
2010-09-03 22:05:08 +04:00
static uint32_t wbint_bh_set_timeout ( struct dcerpc_binding_handle * h ,
uint32_t timeout )
{
/* TODO: implement timeouts */
return UINT32_MAX ;
}
2010-08-06 13:50:09 +04:00
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 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 ;
}
2010-08-12 17:28:27 +04:00
state - > domain = hs - > domain ;
2010-08-06 13:50:09 +04:00
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 ) {
2011-09-14 19:57:37 +04:00
tevent_req_nterror ( req , NT_STATUS_CONNECTION_DISCONNECTED ) ;
2010-08-06 13:50:09 +04:00
return tevent_req_post ( req , ev ) ;
}
if ( ( state - > domain ! = NULL )
& & wcache_fetch_ndr ( state , state - > domain , state - > opnum ,
& state - > in_data , & state - > out_data ) ) {
2017-08-01 17:36:27 +03:00
DBG_DEBUG ( " Got opnum % " PRIu32 " for domain %s from cache \n " ,
state - > opnum , state - > domain - > name ) ;
2010-08-06 13:50:09 +04:00
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 ;
2010-08-12 17:28:27 +04:00
subreq = wb_child_request_send ( state , ev , hs - > child ,
2010-08-06 13:50:09 +04:00
& 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 ) {
2011-06-19 23:10:01 +04:00
tevent_req_oom ( req ) ;
2010-08-06 13:50:09 +04:00
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 ) {
2011-09-14 19:57:37 +04:00
tevent_req_nterror ( req , NT_STATUS_CONNECTION_DISCONNECTED ) ;
2010-08-06 13:50:09 +04:00
return tevent_req_post ( req , ev ) ;
}
/*
* TODO : do a real async disconnect . . .
*
* For now the caller needs to free rpc_cli
*/
2010-08-12 17:28:27 +04:00
hs - > child = NULL ;
2010-08-06 13:50:09 +04:00
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 ,
2010-09-03 22:05:08 +04:00
. set_timeout = wbint_bh_set_timeout ,
2010-08-06 13:50:09 +04:00
. 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 */
2010-08-12 17:28:27 +04:00
struct dcerpc_binding_handle * wbint_binding_handle ( TALLOC_CTX * mem_ctx ,
struct winbindd_domain * domain ,
struct winbindd_child * child )
2010-08-06 13:50:09 +04:00
{
struct dcerpc_binding_handle * h ;
struct wbint_bh_state * hs ;
2010-08-12 17:28:27 +04:00
h = dcerpc_binding_handle_create ( mem_ctx ,
2010-08-06 13:50:09 +04:00
& wbint_bh_ops ,
NULL ,
2014-05-08 04:17:32 +04:00
& ndr_table_winbind ,
2010-08-06 13:50:09 +04:00
& hs ,
struct wbint_bh_state ,
__location__ ) ;
if ( h = = NULL ) {
return NULL ;
}
2010-08-12 17:28:27 +04:00
hs - > domain = domain ;
hs - > child = child ;
2010-08-06 13:50:09 +04:00
return h ;
}
2009-07-28 23:06:11 +04:00
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 ;
2016-11-21 01:57:48 +03:00
const struct api_struct * fns ;
2009-07-28 23:06:11 +04:00
int num_fns ;
bool ret ;
2016-11-21 01:57:48 +03:00
fns = winbind_get_pipe_fns ( & num_fns ) ;
2009-07-28 23:06:11 +04:00
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 ;
}