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"
2019-10-22 19:28:44 +03:00
# include "librpc/rpc/dcesrv_core.h"
# include "librpc/gen_ndr/ndr_winbind.h"
2019-11-18 16:01:52 +03:00
# include "rpc_server/rpc_config.h"
# include "rpc_server/rpc_server.h"
# include "rpc_dce.h"
2022-02-17 14:29:12 +03:00
# include "lib/tsocket/tsocket.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 ) ;
2018-02-13 18:04:44 +03:00
if ( ( hs - > domain = = NULL ) & & ( hs - > child = = NULL ) ) {
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 ;
} ;
2018-02-13 18:04:44 +03:00
static void wbint_bh_raw_call_child_done ( struct tevent_req * subreq ) ;
static void wbint_bh_raw_call_domain_done ( struct tevent_req * subreq ) ;
2010-08-06 13:50:09 +04:00
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 ;
2018-02-13 18:04:44 +03:00
if ( hs - > child ! = NULL ) {
subreq = wb_child_request_send ( state , ev , hs - > child ,
& state - > request ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback (
subreq , wbint_bh_raw_call_child_done , req ) ;
return req ;
}
subreq = wb_domain_request_send ( state , ev , hs - > domain ,
& state - > request ) ;
2010-08-06 13:50:09 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2018-02-13 18:04:44 +03:00
tevent_req_set_callback ( subreq , wbint_bh_raw_call_domain_done , req ) ;
2010-08-06 13:50:09 +04:00
return req ;
}
2018-02-13 18:04:44 +03:00
static void wbint_bh_raw_call_child_done ( struct tevent_req * subreq )
2010-08-06 13:50:09 +04:00
{
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 ) ;
}
2018-02-13 18:04:44 +03:00
static void wbint_bh_raw_call_domain_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_domain_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_oom ( req ) ;
return ;
}
if ( state - > domain ! = NULL ) {
wcache_store_ndr ( state - > domain , state - > opnum ,
& state - > in_data , & state - > out_data ) ;
}
tevent_req_done ( req ) ;
}
2010-08-06 13:50:09 +04:00
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 . . .
*/
2018-02-13 18:04:44 +03:00
hs - > domain = NULL ;
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 ,
} ;
2019-11-18 16:01:52 +03:00
static NTSTATUS make_internal_ncacn_conn ( TALLOC_CTX * mem_ctx ,
const struct ndr_interface_table * table ,
struct dcerpc_ncacn_conn * * _out )
{
struct dcerpc_ncacn_conn * ncacn_conn = NULL ;
ncacn_conn = talloc_zero ( mem_ctx , struct dcerpc_ncacn_conn ) ;
if ( ncacn_conn = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2021-10-06 13:20:17 +03:00
ncacn_conn - > p . mem_ctx = mem_ctx ;
2019-11-18 16:01:52 +03:00
* _out = ncacn_conn ;
return NT_STATUS_OK ;
}
static NTSTATUS find_ncalrpc_default_endpoint ( struct dcesrv_context * dce_ctx ,
struct dcesrv_endpoint * * ep )
{
TALLOC_CTX * tmp_ctx = NULL ;
struct dcerpc_binding * binding = NULL ;
NTSTATUS status ;
tmp_ctx = talloc_new ( dce_ctx ) ;
if ( tmp_ctx = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
/*
* Some services use a rpcint binding handle in their initialization ,
* before the server is fully initialized . Search the NCALRPC endpoint
* with and without endpoint
*/
status = dcerpc_parse_binding ( tmp_ctx , " ncalrpc: " , & binding ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto out ;
}
status = dcesrv_find_endpoint ( dce_ctx , binding , ep ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
goto out ;
}
2021-08-23 15:27:49 +03:00
status = dcerpc_parse_binding ( tmp_ctx , " ncalrpc:[WINBIND] " , & binding ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto out ;
}
status = dcesrv_find_endpoint ( dce_ctx , binding , ep ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
goto out ;
}
2019-11-18 16:01:52 +03:00
status = dcerpc_parse_binding ( tmp_ctx , " ncalrpc:[DEFAULT] " , & binding ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto out ;
}
status = dcesrv_find_endpoint ( dce_ctx , binding , ep ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto out ;
}
out :
talloc_free ( tmp_ctx ) ;
return status ;
}
static NTSTATUS make_internal_dcesrv_connection ( TALLOC_CTX * mem_ctx ,
const struct ndr_interface_table * ndr_table ,
struct dcerpc_ncacn_conn * ncacn_conn ,
struct dcesrv_connection * * _out )
{
struct dcesrv_connection * conn = NULL ;
struct dcesrv_connection_context * context = NULL ;
struct dcesrv_endpoint * endpoint = NULL ;
NTSTATUS status ;
conn = talloc_zero ( mem_ctx , struct dcesrv_connection ) ;
if ( conn = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
conn - > dce_ctx = global_dcesrv_context ( ) ;
conn - > preferred_transfer = & ndr_transfer_syntax_ndr ;
conn - > transport . private_data = ncacn_conn ;
status = find_ncalrpc_default_endpoint ( conn - > dce_ctx , & endpoint ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
conn - > endpoint = endpoint ;
conn - > default_auth_state = talloc_zero ( conn , struct dcesrv_auth ) ;
if ( conn - > default_auth_state = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
conn - > default_auth_state - > auth_finished = true ;
context = talloc_zero ( conn , struct dcesrv_connection_context ) ;
if ( context = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
context - > conn = conn ;
context - > context_id = 0 ;
context - > transfer_syntax = * ( conn - > preferred_transfer ) ;
2021-01-27 15:51:33 +03:00
context - > iface = find_interface_by_syntax_id (
conn - > endpoint , & ndr_table - > syntax_id ) ;
2019-11-18 16:01:52 +03:00
if ( context - > iface = = NULL ) {
status = NT_STATUS_RPC_INTERFACE_NOT_FOUND ;
goto fail ;
}
DLIST_ADD ( conn - > contexts , context ) ;
* _out = conn ;
return NT_STATUS_OK ;
fail :
talloc_free ( conn ) ;
return status ;
}
2022-02-17 14:29:12 +03:00
static NTSTATUS set_remote_addresses ( struct dcesrv_connection * conn ,
int sock )
{
struct sockaddr_storage st = { 0 } ;
struct sockaddr * sar = ( struct sockaddr * ) & st ;
struct tsocket_address * remote = NULL ;
struct tsocket_address * local = NULL ;
socklen_t sa_len = sizeof ( st ) ;
NTSTATUS status ;
int ret ;
ZERO_STRUCT ( st ) ;
ret = getpeername ( sock , sar , & sa_len ) ;
if ( ret ! = 0 ) {
status = map_nt_error_from_unix ( ret ) ;
2023-08-07 07:51:21 +03:00
DBG_ERR ( " getpeername failed: %s \n " , nt_errstr ( status ) ) ;
2022-02-17 14:29:12 +03:00
return status ;
}
ret = tsocket_address_bsd_from_sockaddr ( conn , sar , sa_len , & remote ) ;
if ( ret ! = 0 ) {
status = map_nt_error_from_unix ( ret ) ;
2023-08-07 07:51:21 +03:00
DBG_ERR ( " tsocket_address_bsd_from_sockaddr failed: %s \n " ,
2022-02-17 14:29:12 +03:00
nt_errstr ( status ) ) ;
return status ;
}
ZERO_STRUCT ( st ) ;
ret = getsockname ( sock , sar , & sa_len ) ;
if ( ret ! = 0 ) {
status = map_nt_error_from_unix ( ret ) ;
2023-08-07 07:51:21 +03:00
DBG_ERR ( " getsockname failed: %s \n " , nt_errstr ( status ) ) ;
2022-02-17 14:29:12 +03:00
return status ;
}
ret = tsocket_address_bsd_from_sockaddr ( conn , sar , sa_len , & local ) ;
if ( ret ! = 0 ) {
status = map_nt_error_from_unix ( ret ) ;
2023-08-07 07:51:21 +03:00
DBG_ERR ( " tsocket_address_bsd_from_sockaddr failed: %s \n " ,
2022-02-17 14:29:12 +03:00
nt_errstr ( status ) ) ;
return status ;
}
conn - > local_address = talloc_move ( conn , & local ) ;
conn - > remote_address = talloc_move ( conn , & remote ) ;
return NT_STATUS_OK ;
}
2010-08-06 13:50:09 +04:00
/* 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 )
{
2019-11-18 16:01:52 +03:00
struct dcerpc_ncacn_conn * ncacn_conn = NULL ;
struct dcesrv_connection * dcesrv_conn = NULL ;
struct dcesrv_call_state * dcesrv_call = NULL ;
struct data_blob_list_item * rep = NULL ;
2021-01-29 20:16:08 +03:00
struct dcesrv_context_callbacks * cb = NULL ;
2019-10-22 19:28:44 +03:00
uint32_t opnum = state - > request - > data . ndrcmd ;
TALLOC_CTX * mem_ctx ;
NTSTATUS status ;
DBG_DEBUG ( " Running command %s (domain '%s') \n " ,
ndr_table_winbind . calls [ opnum ] . name ,
domain ? domain - > name : " (null) " ) ;
2019-11-18 16:01:52 +03:00
mem_ctx = talloc_stackframe ( ) ;
if ( mem_ctx = = NULL ) {
2023-08-07 07:51:21 +03:00
DBG_ERR ( " No memory \n " ) ;
2019-10-22 19:28:44 +03:00
return WINBINDD_ERROR ;
}
2009-07-28 23:06:11 +04:00
2019-11-18 16:01:52 +03:00
status = make_internal_ncacn_conn ( mem_ctx ,
& ndr_table_winbind ,
& ncacn_conn ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto out ;
2019-10-22 19:28:44 +03:00
}
2009-07-28 23:06:11 +04:00
2019-11-18 16:01:52 +03:00
status = make_internal_dcesrv_connection ( ncacn_conn ,
& ndr_table_winbind ,
ncacn_conn ,
& dcesrv_conn ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto out ;
2009-07-28 23:06:11 +04:00
}
2022-02-17 14:29:12 +03:00
status = set_remote_addresses ( dcesrv_conn , state - > sock ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto out ;
}
2019-11-18 16:01:52 +03:00
dcesrv_call = talloc_zero ( dcesrv_conn , struct dcesrv_call_state ) ;
if ( dcesrv_call = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto out ;
2019-10-22 19:28:44 +03:00
}
2009-08-22 19:14:32 +04:00
2019-11-18 16:01:52 +03:00
dcesrv_call - > conn = dcesrv_conn ;
dcesrv_call - > context = dcesrv_conn - > contexts ;
dcesrv_call - > auth_state = dcesrv_conn - > default_auth_state ;
2009-07-28 23:06:11 +04:00
2019-11-18 16:01:52 +03:00
ZERO_STRUCT ( dcesrv_call - > pkt ) ;
dcesrv_call - > pkt . u . bind . assoc_group_id = 0 ;
2021-01-29 20:16:08 +03:00
2021-02-02 17:07:35 +03:00
cb = dcesrv_call - > conn - > dce_ctx - > callbacks ;
2021-01-29 20:16:08 +03:00
status = cb - > assoc_group . find (
dcesrv_call , cb - > assoc_group . private_data ) ;
2019-10-22 19:28:44 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2019-11-18 16:01:52 +03:00
goto out ;
2009-07-28 23:06:11 +04:00
}
2019-11-18 16:01:52 +03:00
ZERO_STRUCT ( dcesrv_call - > pkt ) ;
dcesrv_call - > pkt . u . request . opnum = opnum ;
dcesrv_call - > pkt . u . request . context_id = 0 ;
dcesrv_call - > pkt . u . request . stub_and_verifier =
data_blob_const ( state - > request - > extra_data . data ,
state - > request - > extra_len ) ;
2010-07-13 23:43:44 +04:00
2020-10-23 12:42:14 +03:00
status = dcesrv_call_dispatch_local ( dcesrv_call ) ;
2019-11-18 16:01:52 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto out ;
2009-07-28 23:06:11 +04:00
}
2019-10-22 19:28:44 +03:00
2019-11-18 16:01:52 +03:00
rep = dcesrv_call - > replies ;
DLIST_REMOVE ( dcesrv_call - > replies , rep ) ;
state - > response - > extra_data . data = talloc_steal ( state - > mem_ctx ,
rep - > blob . data ) ;
state - > response - > length + = rep - > blob . length ;
talloc_free ( rep ) ;
out :
talloc_free ( mem_ctx ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
return WINBINDD_OK ;
}
return WINBINDD_ERROR ;
2009-07-28 23:06:11 +04:00
}