2021-04-07 10:00:23 +03:00
/*
* Unix SMB / CIFS implementation .
*
* 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/>.
*/
# include "source3/include/includes.h"
# include "lib/cmdline/cmdline.h"
# include "rpc_worker.h"
# include "rpc_config.h"
# include "librpc/rpc/dcesrv_core.h"
# include "librpc/rpc/dcerpc_util.h"
# include "source3/librpc/gen_ndr/ndr_rpc_host.h"
# include "lib/util/debug.h"
# include "lib/util/fault.h"
# include "rpc_server.h"
# include "rpc_pipes.h"
# include "source3/smbd/proto.h"
# include "source3/lib/smbd_shim.h"
# include "source3/lib/global_contexts.h"
# include "source3/lib/util_procid.h"
# include "lib/tsocket/tsocket.h"
# include "libcli/named_pipe_auth/npa_tstream.h"
# include "libcli/smb/smb_constants.h"
# include "lib/param/param.h"
# include "lib/util/idtree_random.h"
# include "lib/util/tevent_unix.h"
# include "lib/async_req/async_sock.h"
# include "lib/util/dlinklist.h"
# include "source3/include/auth.h"
# include "nsswitch/winbind_client.h"
# include "source3/include/messages.h"
# include "libcli/security/security_token.h"
2023-04-18 13:47:04 +03:00
# include "libcli/security/dom_sid.h"
# include "source3/include/proto.h"
2021-04-07 10:00:23 +03:00
/*
* This is the generic code that becomes the
* template that all rpcd_ * instances that
* serve DCERPC can use to provide services to samba - dcerpcd .
*
* The external entry point is :
* rpc_worker_main ( ) which takes an argc / argv list
* and two functions :
*
* get_interfaces ( ) - List all interfaces that this server provides
* get_servers ( ) - Provide the RPC server implementations
*
* Each rpcd_ * service needs only to provide
* the implementations of get_interfaces ( ) and get_servers ( )
* and call rpc_worker_main ( ) from their main ( ) function
* to provide services that can be connected to from samba - dcerpcd .
*/
struct rpc_worker {
struct dcerpc_ncacn_conn * conns ;
struct server_id rpc_host_pid ;
struct messaging_context * msg_ctx ;
struct dcesrv_context * dce_ctx ;
struct dcesrv_context_callbacks cb ;
struct rpc_worker_status status ;
bool done ;
} ;
static void rpc_worker_print_interface (
FILE * f , const struct ndr_interface_table * t )
{
const struct ndr_interface_string_array * endpoints = t - > endpoints ;
uint32_t i ;
struct ndr_syntax_id_buf id_buf ;
fprintf ( f ,
" %s %s \n " ,
ndr_syntax_id_buf_string ( & t - > syntax_id , & id_buf ) ,
t - > name ) ;
for ( i = 0 ; i < endpoints - > count ; i + + ) {
fprintf ( f , " %s \n " , endpoints - > names [ i ] ) ;
}
}
static NTSTATUS rpc_worker_report_status ( struct rpc_worker * worker )
{
2023-02-20 20:46:50 +03:00
uint8_t buf [ 9 ] ;
2021-04-07 10:00:23 +03:00
DATA_BLOB blob = { . data = buf , . length = sizeof ( buf ) , } ;
enum ndr_err_code ndr_err ;
NTSTATUS status ;
if ( DEBUGLEVEL > = 10 ) {
NDR_PRINT_DEBUG ( rpc_worker_status , & worker - > status ) ;
}
ndr_err = ndr_push_struct_into_fixed_blob (
& blob ,
& worker - > status ,
( ndr_push_flags_fn_t ) ndr_push_rpc_worker_status ) ;
SMB_ASSERT ( NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) ;
status = messaging_send (
worker - > msg_ctx ,
worker - > rpc_host_pid ,
MSG_RPC_WORKER_STATUS ,
& blob ) ;
return status ;
}
static void rpc_worker_connection_terminated (
struct dcesrv_connection * conn , void * private_data )
{
struct rpc_worker * worker = talloc_get_type_abort (
private_data , struct rpc_worker ) ;
struct dcerpc_ncacn_conn * ncacn_conn = talloc_get_type_abort (
conn - > transport . private_data , struct dcerpc_ncacn_conn ) ;
struct dcerpc_ncacn_conn * w = NULL ;
NTSTATUS status ;
bool found = false ;
SMB_ASSERT ( worker - > status . num_clients > 0 ) ;
for ( w = worker - > conns ; w ! = NULL ; w = w - > next ) {
if ( w = = ncacn_conn ) {
found = true ;
break ;
}
}
SMB_ASSERT ( found ) ;
DLIST_REMOVE ( worker - > conns , ncacn_conn ) ;
worker - > status . num_clients - = 1 ;
status = rpc_worker_report_status ( worker ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " rpc_worker_report_status returned %s \n " ,
nt_errstr ( status ) ) ;
}
}
static int dcesrv_connection_destructor ( struct dcesrv_connection * conn )
{
struct dcerpc_ncacn_conn * ncacn_conn = talloc_get_type_abort (
conn - > transport . private_data ,
struct dcerpc_ncacn_conn ) ;
if ( ncacn_conn - > termination_fn ! = NULL ) {
ncacn_conn - > termination_fn ( conn , ncacn_conn - > termination_data ) ;
}
return 0 ;
}
/*
* A new client has been passed to us from samba - dcerpcd .
*/
static void rpc_worker_new_client (
struct rpc_worker * worker ,
struct rpc_host_client * client ,
int sock )
{
struct dcesrv_context * dce_ctx = worker - > dce_ctx ;
2023-04-18 13:29:34 +03:00
struct named_pipe_auth_req_info7 * info7 = client - > npa_info7 ;
2021-10-06 12:16:21 +03:00
struct tsocket_address * remote_client_addr = NULL ;
2021-10-06 12:16:21 +03:00
struct tsocket_address * local_server_addr = NULL ;
2021-04-07 10:00:23 +03:00
struct dcerpc_binding * b = NULL ;
enum dcerpc_transport_t transport ;
struct dcesrv_endpoint * ep = NULL ;
2021-10-06 12:16:21 +03:00
struct tstream_context * tstream = NULL ;
2021-04-07 10:00:23 +03:00
struct dcerpc_ncacn_conn * ncacn_conn = NULL ;
struct dcesrv_connection * dcesrv_conn = NULL ;
DATA_BLOB buffer = { . data = NULL } ;
struct ncacn_packet * pkt = NULL ;
2023-04-18 13:47:04 +03:00
struct security_token * token = NULL ;
uint32_t npa_flags , state_flags ;
bool found_npa_flags ;
2021-04-07 10:00:23 +03:00
NTSTATUS status ;
int ret ;
DBG_DEBUG ( " Got new conn sock %d for binding %s \n " ,
sock ,
client - > binding ) ;
status = dcerpc_parse_binding ( client , client - > binding , & b ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " dcerpc_parse_binding(%s) failed: %s \n " ,
client - > binding ,
nt_errstr ( status ) ) ;
goto fail ;
}
transport = dcerpc_binding_get_transport ( b ) ;
status = dcesrv_find_endpoint ( dce_ctx , b , & ep ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NOT_FOUND ) & &
( ( transport = = NCACN_IP_TCP ) | | ( transport = = NCALRPC ) ) & &
( dcerpc_binding_get_string_option ( b , " endpoint " ) ! = NULL ) ) {
/*
* We have two kinds of servers : Those who explicitly
* bind to a port ( e . g . 135 for epmapper ) and those
* who just specify a transport . The client specified
* a port ( or socket name ) , but we did not find this
* in the list of servers having specified a
* port . Retry just matching for the transport ,
* catching the servers that did not explicitly
* specify a port .
*
* This is not fully correct , what we should do is
* that once the port the server listens on has been
* finalized we should mark this in the server list ,
* but for now it works . We don ' t have the same RPC
* interface listening twice on different ports .
*/
struct dcerpc_binding * b_without_port = dcerpc_binding_dup (
client , b ) ;
if ( b_without_port = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
status = dcerpc_binding_set_string_option (
b_without_port , " endpoint " , NULL ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " Could not delete endpoint: %s \n " ,
nt_errstr ( status ) ) ;
TALLOC_FREE ( b_without_port ) ;
goto fail ;
}
status = dcesrv_find_endpoint ( dce_ctx , b_without_port , & ep ) ;
TALLOC_FREE ( b_without_port ) ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " Could not find endpoint for %s: %s \n " ,
client - > binding ,
nt_errstr ( status ) ) ;
goto fail ;
}
2021-09-28 14:26:18 +03:00
ncacn_conn = talloc ( dce_ctx , struct dcerpc_ncacn_conn ) ;
if ( ncacn_conn = = NULL ) {
DBG_DEBUG ( " talloc failed \n " ) ;
2021-04-07 10:00:23 +03:00
goto fail ;
}
2021-09-28 14:26:18 +03:00
* ncacn_conn = ( struct dcerpc_ncacn_conn ) {
. endpoint = ep ,
. sock = sock ,
. termination_fn = rpc_worker_connection_terminated ,
. termination_data = worker ,
} ;
2021-04-07 10:00:23 +03:00
if ( transport = = NCALRPC ) {
2023-04-18 13:29:34 +03:00
ret = tsocket_address_unix_from_path ( ncacn_conn ,
info7 - > remote_client_addr ,
& remote_client_addr ) ;
2021-04-07 10:00:23 +03:00
if ( ret = = - 1 ) {
DBG_DEBUG ( " tsocket_address_unix_from_path "
" (%s) failed: %s \n " ,
2023-04-18 13:29:34 +03:00
info7 - > remote_client_addr ,
2021-04-07 10:00:23 +03:00
strerror ( errno ) ) ;
goto fail ;
}
2023-04-18 13:29:34 +03:00
ncacn_conn - > remote_client_name =
talloc_strdup ( ncacn_conn , info7 - > remote_client_name ) ;
2021-04-07 10:00:23 +03:00
if ( ncacn_conn - > remote_client_name = = NULL ) {
DBG_DEBUG ( " talloc_strdup(%s) failed \n " ,
2023-04-18 13:29:34 +03:00
info7 - > remote_client_name ) ;
2021-04-07 10:00:23 +03:00
goto fail ;
}
2023-04-18 13:29:34 +03:00
ret = tsocket_address_unix_from_path ( ncacn_conn ,
info7 - > local_server_addr ,
& local_server_addr ) ;
2021-04-07 10:00:23 +03:00
if ( ret = = - 1 ) {
DBG_DEBUG ( " tsocket_address_unix_from_path "
" (%s) failed: %s \n " ,
2023-04-18 13:29:34 +03:00
info7 - > local_server_addr ,
2021-04-07 10:00:23 +03:00
strerror ( errno ) ) ;
goto fail ;
}
2023-04-18 13:29:34 +03:00
ncacn_conn - > local_server_name =
talloc_strdup ( ncacn_conn , info7 - > local_server_name ) ;
2021-04-07 10:00:23 +03:00
if ( ncacn_conn - > local_server_name = = NULL ) {
DBG_DEBUG ( " talloc_strdup(%s) failed \n " ,
2023-04-18 13:29:34 +03:00
info7 - > local_server_name ) ;
2021-04-07 10:00:23 +03:00
goto fail ;
}
} else {
ret = tsocket_address_inet_from_strings (
ncacn_conn ,
" ip " ,
2023-04-18 13:29:34 +03:00
info7 - > remote_client_addr ,
info7 - > remote_client_port ,
2021-10-06 12:16:21 +03:00
& remote_client_addr ) ;
2021-04-07 10:00:23 +03:00
if ( ret = = - 1 ) {
DBG_DEBUG ( " tsocket_address_inet_from_strings "
2023-04-18 13:29:34 +03:00
" (%s, % " PRIu16 " ) failed: %s \n " ,
info7 - > remote_client_addr ,
info7 - > remote_client_port ,
2021-04-07 10:00:23 +03:00
strerror ( errno ) ) ;
goto fail ;
}
2023-04-18 13:29:34 +03:00
ncacn_conn - > remote_client_name =
talloc_strdup ( ncacn_conn , info7 - > remote_client_name ) ;
2021-04-07 10:00:23 +03:00
if ( ncacn_conn - > remote_client_name = = NULL ) {
DBG_DEBUG ( " talloc_strdup(%s) failed \n " ,
2023-04-18 13:29:34 +03:00
info7 - > remote_client_name ) ;
2021-04-07 10:00:23 +03:00
goto fail ;
}
ret = tsocket_address_inet_from_strings (
ncacn_conn ,
" ip " ,
2023-04-18 13:29:34 +03:00
info7 - > local_server_addr ,
info7 - > local_server_port ,
2021-10-06 12:16:21 +03:00
& local_server_addr ) ;
2021-04-07 10:00:23 +03:00
if ( ret = = - 1 ) {
DBG_DEBUG ( " tsocket_address_inet_from_strings "
2023-04-18 13:29:34 +03:00
" (%s, % " PRIu16 " ) failed: %s \n " ,
info7 - > local_server_addr ,
info7 - > local_server_port ,
2021-04-07 10:00:23 +03:00
strerror ( errno ) ) ;
goto fail ;
}
2023-04-18 13:29:34 +03:00
ncacn_conn - > local_server_name =
talloc_strdup ( ncacn_conn , info7 - > local_server_name ) ;
2021-04-07 10:00:23 +03:00
if ( ncacn_conn - > local_server_name = = NULL ) {
DBG_DEBUG ( " talloc_strdup(%s) failed \n " ,
2023-04-18 13:29:34 +03:00
info7 - > local_server_name ) ;
2021-04-07 10:00:23 +03:00
goto fail ;
}
}
if ( transport = = NCACN_NP ) {
ret = tstream_npa_existing_socket (
ncacn_conn ,
sock ,
FILE_TYPE_MESSAGE_MODE_PIPE ,
2021-10-06 12:16:21 +03:00
& tstream ) ;
2021-04-07 10:00:23 +03:00
if ( ret = = - 1 ) {
DBG_DEBUG ( " tstream_npa_existing_socket failed: %s \n " ,
strerror ( errno ) ) ;
goto fail ;
}
/*
* " transport " so far is implicitly assigned by the
* socket that the client connected to , passed in from
* samba - dcerpcd via the binding . For NCACN_NP ( root
* only by unix permissions ) we got a
2023-04-18 13:29:34 +03:00
* named_pipe_auth_req_info7 where the transport can
2021-04-07 10:00:23 +03:00
* be overridden .
*/
2023-04-18 13:29:34 +03:00
transport = info7 - > transport ;
2021-04-07 10:00:23 +03:00
} else {
ret = tstream_bsd_existing_socket (
2021-10-06 12:16:21 +03:00
ncacn_conn , sock , & tstream ) ;
2021-04-07 10:00:23 +03:00
if ( ret = = - 1 ) {
DBG_DEBUG ( " tstream_bsd_existing_socket failed: %s \n " ,
strerror ( errno ) ) ;
goto fail ;
}
}
sock = - 1 ;
2023-04-18 13:47:04 +03:00
token = info7 - > session_info - > session_info - > security_token ;
if ( security_token_is_system ( token ) & & ( transport ! = NCALRPC ) ) {
2021-04-07 10:00:23 +03:00
DBG_DEBUG ( " System token only allowed on NCALRPC \n " ) ;
goto fail ;
}
2023-04-18 13:47:04 +03:00
state_flags = DCESRV_CALL_STATE_FLAG_MAY_ASYNC ;
found_npa_flags = security_token_find_npa_flags ( token , & npa_flags ) ;
if ( found_npa_flags ) {
if ( npa_flags & SAMBA_NPA_FLAGS_WINBIND_OFF ) {
state_flags | =
DCESRV_CALL_STATE_FLAG_WINBIND_OFF ;
}
/*
* Delete the flags so that we don ' t bail in
* local_np_connect_send ( ) on subsequent
* connects . Once we connect to another RPC service , a
* new flags sid will be added if required .
*/
security_token_del_npa_flags ( token ) ;
}
2021-10-06 13:20:17 +03:00
ncacn_conn - > p . msg_ctx = global_messaging_context ( ) ;
ncacn_conn - > p . transport = transport ;
2021-04-07 10:00:23 +03:00
2023-04-18 13:29:34 +03:00
status = dcesrv_endpoint_connect ( dce_ctx ,
ncacn_conn ,
ep ,
info7 - > session_info - > session_info ,
global_event_context ( ) ,
2023-04-18 13:47:04 +03:00
state_flags ,
2023-04-18 13:29:34 +03:00
& dcesrv_conn ) ;
2021-04-07 10:00:23 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " Failed to connect to endpoint: %s \n " ,
nt_errstr ( status ) ) ;
goto fail ;
}
talloc_set_destructor ( dcesrv_conn , dcesrv_connection_destructor ) ;
dcesrv_conn - > transport . private_data = ncacn_conn ;
dcesrv_conn - > transport . report_output_data =
dcesrv_sock_report_output_data ;
dcesrv_conn - > transport . terminate_connection =
dcesrv_transport_terminate_connection ;
dcesrv_conn - > send_queue = tevent_queue_create (
dcesrv_conn , " dcesrv send queue " ) ;
if ( dcesrv_conn - > send_queue = = NULL ) {
DBG_DEBUG ( " tevent_queue_create failed \n " ) ;
goto fail ;
}
2021-10-06 12:16:21 +03:00
dcesrv_conn - > stream = talloc_move ( dcesrv_conn , & tstream ) ;
2021-10-06 12:16:21 +03:00
dcesrv_conn - > local_address =
talloc_move ( dcesrv_conn , & local_server_addr ) ;
2021-10-06 12:16:21 +03:00
dcesrv_conn - > remote_address =
talloc_move ( dcesrv_conn , & remote_client_addr ) ;
2021-04-07 10:00:23 +03:00
if ( client - > bind_packet . length = = 0 ) {
DBG_DEBUG ( " Expected bind packet \n " ) ;
goto fail ;
}
buffer = ( DATA_BLOB ) {
. data = talloc_move ( dcesrv_conn , & client - > bind_packet . data ) ,
. length = client - > bind_packet . length ,
} ;
pkt = talloc ( dcesrv_conn , struct ncacn_packet ) ;
if ( pkt = = NULL ) {
DBG_DEBUG ( " talloc failed \n " ) ;
goto fail ;
}
status = dcerpc_pull_ncacn_packet ( pkt , & buffer , pkt ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " dcerpc_pull_ncacn_packet failed: %s \n " ,
nt_errstr ( status ) ) ;
goto fail ;
}
TALLOC_FREE ( client ) ;
DLIST_ADD ( worker - > conns , ncacn_conn ) ;
worker - > status . num_clients + = 1 ;
dcesrv_loop_next_packet ( dcesrv_conn , pkt , buffer ) ;
return ;
fail :
TALLOC_FREE ( ncacn_conn ) ;
TALLOC_FREE ( dcesrv_conn ) ;
TALLOC_FREE ( client ) ;
if ( sock ! = - 1 ) {
close ( sock ) ;
}
/*
* Parent thinks it successfully sent us a client . Tell it
* that we declined .
*/
status = rpc_worker_report_status ( worker ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " rpc_worker_report_status returned %s \n " ,
nt_errstr ( status ) ) ;
}
}
/*
* New client message processing .
*/
static bool rpc_worker_new_client_filter (
struct messaging_rec * rec , void * private_data )
{
struct rpc_worker * worker = talloc_get_type_abort (
private_data , struct rpc_worker ) ;
struct dcesrv_context * dce_ctx = worker - > dce_ctx ;
struct rpc_host_client * client = NULL ;
enum ndr_err_code ndr_err ;
int sock ;
if ( rec - > msg_type ! = MSG_RPC_HOST_NEW_CLIENT ) {
return false ;
}
if ( rec - > num_fds ! = 1 ) {
DBG_DEBUG ( " Got % " PRIu8 " fds \n " , rec - > num_fds ) ;
return false ;
}
client = talloc ( dce_ctx , struct rpc_host_client ) ;
if ( client = = NULL ) {
DBG_DEBUG ( " talloc failed \n " ) ;
return false ;
}
ndr_err = ndr_pull_struct_blob_all (
& rec - > buf ,
client ,
client ,
( ndr_pull_flags_fn_t ) ndr_pull_rpc_host_client ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DBG_DEBUG ( " ndr_pull_rpc_host_client failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ;
TALLOC_FREE ( client ) ;
return false ;
}
if ( DEBUGLEVEL > = 10 ) {
NDR_PRINT_DEBUG ( rpc_host_client , client ) ;
}
sock = rec - > fds [ 0 ] ;
rec - > fds [ 0 ] = - 1 ;
rpc_worker_new_client ( worker , client , sock ) ;
return false ;
}
/*
* Return your status message processing .
*/
static bool rpc_worker_status_filter (
struct messaging_rec * rec , void * private_data )
{
struct rpc_worker * worker = talloc_get_type_abort (
private_data , struct rpc_worker ) ;
struct dcerpc_ncacn_conn * conn = NULL ;
FILE * f = NULL ;
int fd ;
if ( rec - > msg_type ! = MSG_RPC_DUMP_STATUS ) {
return false ;
}
if ( rec - > num_fds ! = 1 ) {
DBG_DEBUG ( " Got % " PRIu8 " fds \n " , rec - > num_fds ) ;
return false ;
}
fd = dup ( rec - > fds [ 0 ] ) ;
if ( fd = = - 1 ) {
DBG_DEBUG ( " dup(% " PRIi64 " ) failed: %s \n " ,
rec - > fds [ 0 ] ,
strerror ( errno ) ) ;
return false ;
}
f = fdopen ( fd , " w " ) ;
if ( f = = NULL ) {
DBG_DEBUG ( " fdopen failed: %s \n " , strerror ( errno ) ) ;
close ( fd ) ;
return false ;
}
for ( conn = worker - > conns ; conn ! = NULL ; conn = conn - > next ) {
char * endpoint = NULL ;
endpoint = dcerpc_binding_string (
conn , conn - > endpoint - > ep_description ) ;
fprintf ( f ,
" endpoint=%s client=%s server=%s \n " ,
endpoint ? endpoint : " UNKNOWN " ,
conn - > remote_client_name ,
conn - > local_server_name ) ;
TALLOC_FREE ( endpoint ) ;
}
fclose ( f ) ;
return false ;
}
/*
take a reference to an existing association group
*/
static struct dcesrv_assoc_group * rpc_worker_assoc_group_reference (
struct dcesrv_connection * conn ,
uint32_t id )
{
const struct dcesrv_endpoint * endpoint = conn - > endpoint ;
enum dcerpc_transport_t transport = dcerpc_binding_get_transport (
endpoint - > ep_description ) ;
struct dcesrv_assoc_group * assoc_group = NULL ;
void * id_ptr = NULL ;
/* find an association group given a assoc_group_id */
id_ptr = idr_find ( conn - > dce_ctx - > assoc_groups_idr , id & 0xffffff ) ;
if ( id_ptr = = NULL ) {
DBG_NOTICE ( " Failed to find assoc_group 0x%08x \n " , id ) ;
return NULL ;
}
assoc_group = talloc_get_type_abort ( id_ptr , struct dcesrv_assoc_group ) ;
if ( assoc_group - > transport ! = transport ) {
const char * at = derpc_transport_string_by_transport (
assoc_group - > transport ) ;
const char * ct = derpc_transport_string_by_transport (
transport ) ;
DBG_NOTICE ( " assoc_group 0x%08x (transport %s) "
" is not available on transport %s " ,
id , at , ct ) ;
return NULL ;
}
/*
* Yes , this is a talloc_reference : The assoc group must be
* removed when all connections go . This should be replaced by
* adding a linked list of dcesrv_connection structs to the
* assoc group .
*/
return talloc_reference ( conn , assoc_group ) ;
}
static int rpc_worker_assoc_group_destructor (
struct dcesrv_assoc_group * assoc_group )
{
int ret ;
ret = idr_remove (
assoc_group - > dce_ctx - > assoc_groups_idr ,
assoc_group - > id & 0xffffff ) ;
if ( ret ! = 0 ) {
DBG_WARNING ( " Failed to remove assoc_group 0x%08x \n " ,
assoc_group - > id ) ;
}
return 0 ;
}
/*
allocate a new association group
*/
static struct dcesrv_assoc_group * rpc_worker_assoc_group_new (
struct dcesrv_connection * conn , uint8_t worker_index )
{
struct dcesrv_context * dce_ctx = conn - > dce_ctx ;
const struct dcesrv_endpoint * endpoint = conn - > endpoint ;
enum dcerpc_transport_t transport = dcerpc_binding_get_transport (
endpoint - > ep_description ) ;
struct dcesrv_assoc_group * assoc_group = NULL ;
int id ;
assoc_group = talloc_zero ( conn , struct dcesrv_assoc_group ) ;
if ( assoc_group = = NULL ) {
return NULL ;
}
id = idr_get_new_random (
2023-01-04 13:43:59 +03:00
dce_ctx - > assoc_groups_idr , assoc_group , 1 , UINT16_MAX ) ;
2021-04-07 10:00:23 +03:00
if ( id = = - 1 ) {
talloc_free ( assoc_group ) ;
DBG_WARNING ( " Out of association groups! \n " ) ;
return NULL ;
}
assoc_group - > id = ( worker_index < < 24 ) + id ;
assoc_group - > transport = transport ;
assoc_group - > dce_ctx = dce_ctx ;
talloc_set_destructor ( assoc_group , rpc_worker_assoc_group_destructor ) ;
return assoc_group ;
}
static NTSTATUS rpc_worker_assoc_group_find (
struct dcesrv_call_state * call ,
void * private_data )
{
struct rpc_worker * w = talloc_get_type_abort (
private_data , struct rpc_worker ) ;
uint32_t assoc_group_id = call - > pkt . u . bind . assoc_group_id ;
if ( assoc_group_id ! = 0 ) {
uint8_t worker_index = ( assoc_group_id & 0xff000000 ) > > 24 ;
if ( worker_index ! = w - > status . worker_index ) {
DBG_DEBUG ( " Wrong worker id % " PRIu8 " , "
" expected % " PRIu8 " \n " ,
worker_index ,
w - > status . worker_index ) ;
return NT_STATUS_NOT_FOUND ;
}
call - > conn - > assoc_group = rpc_worker_assoc_group_reference (
call - > conn , assoc_group_id ) ;
} else {
call - > conn - > assoc_group = rpc_worker_assoc_group_new (
call - > conn , w - > status . worker_index ) ;
}
if ( call - > conn - > assoc_group = = NULL ) {
/* TODO Return correct status */
return NT_STATUS_UNSUCCESSFUL ;
}
return NT_STATUS_OK ;
}
static struct rpc_worker * rpc_worker_new (
TALLOC_CTX * mem_ctx ,
struct messaging_context * msg_ctx )
{
struct rpc_worker * worker = NULL ;
worker = talloc_zero ( mem_ctx , struct rpc_worker ) ;
if ( worker = = NULL ) {
return NULL ;
}
worker - > rpc_host_pid = ( struct server_id ) { . pid = 0 } ;
worker - > msg_ctx = msg_ctx ;
worker - > cb = ( struct dcesrv_context_callbacks ) {
. log . successful_authz = dcesrv_log_successful_authz ,
. auth . gensec_prepare = dcesrv_auth_gensec_prepare ,
2022-01-22 03:08:26 +03:00
. auth . become_root = become_root ,
. auth . unbecome_root = unbecome_root ,
2021-04-07 10:00:23 +03:00
. assoc_group . find = rpc_worker_assoc_group_find ,
. assoc_group . private_data = worker ,
} ;
worker - > dce_ctx = global_dcesrv_context ( ) ;
if ( worker - > dce_ctx = = NULL ) {
goto fail ;
}
dcesrv_context_set_callbacks ( worker - > dce_ctx , & worker - > cb ) ;
return worker ;
fail :
TALLOC_FREE ( worker ) ;
return NULL ;
}
static struct dcesrv_context * rpc_worker_dce_ctx ( struct rpc_worker * w )
{
return w - > dce_ctx ;
}
struct rpc_worker_state {
struct tevent_context * ev ;
struct rpc_worker * w ;
struct tevent_req * new_client_req ;
struct tevent_req * status_req ;
struct tevent_req * finish_req ;
} ;
static void rpc_worker_done ( struct tevent_req * subreq ) ;
static void rpc_worker_shutdown (
struct messaging_context * msg ,
void * private_data ,
uint32_t msg_type ,
struct server_id server_id ,
DATA_BLOB * data ) ;
static struct tevent_req * rpc_worker_send (
TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct rpc_worker * w ,
pid_t rpc_host_pid ,
int server_index ,
int worker_index )
{
struct tevent_req * req = NULL ;
struct rpc_worker_state * state = NULL ;
NTSTATUS status ;
req = tevent_req_create ( mem_ctx , & state , struct rpc_worker_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > w = w ;
if ( ( server_index < 0 ) | | ( ( unsigned ) server_index > UINT32_MAX ) ) {
DBG_ERR ( " Invalid server index %d \n " , server_index ) ;
tevent_req_error ( req , EINVAL ) ;
return tevent_req_post ( req , ev ) ;
}
if ( ( worker_index < 0 ) | | ( ( unsigned ) worker_index > UINT32_MAX ) ) {
DBG_ERR ( " Invalid worker index %d \n " , worker_index ) ;
tevent_req_error ( req , EINVAL ) ;
return tevent_req_post ( req , ev ) ;
}
w - > rpc_host_pid = pid_to_procid ( rpc_host_pid ) ;
w - > status = ( struct rpc_worker_status ) {
. server_index = server_index ,
. worker_index = worker_index ,
} ;
/* Wait for new client messages. */
state - > new_client_req = messaging_filtered_read_send (
w ,
messaging_tevent_context ( w - > msg_ctx ) ,
w - > msg_ctx ,
rpc_worker_new_client_filter ,
w ) ;
if ( tevent_req_nomem ( state - > new_client_req , req ) ) {
return tevent_req_post ( req , ev ) ;
}
/* Wait for report your status messages. */
state - > status_req = messaging_filtered_read_send (
w ,
messaging_tevent_context ( w - > msg_ctx ) ,
w - > msg_ctx ,
rpc_worker_status_filter ,
w ) ;
if ( tevent_req_nomem ( state - > status_req , req ) ) {
return tevent_req_post ( req , ev ) ;
}
/* Wait for shutdown messages. */
status = messaging_register (
w - > msg_ctx , req , MSG_SHUTDOWN , rpc_worker_shutdown ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " messaging_register failed: %s \n " ,
nt_errstr ( status ) ) ;
tevent_req_error ( req , map_errno_from_nt_status ( status ) ) ;
return tevent_req_post ( req , ev ) ;
}
state - > finish_req = wait_for_read_send ( state , ev , 0 , false ) ;
if ( tevent_req_nomem ( state - > finish_req , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( state - > finish_req , rpc_worker_done , req ) ;
rpc_worker_report_status ( w ) ;
return req ;
}
static void rpc_worker_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
int err = 0 ;
bool ok ;
ok = wait_for_read_recv ( subreq , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( ! ok ) {
tevent_req_error ( req , err ) ;
return ;
}
tevent_req_done ( req ) ;
}
static void rpc_worker_shutdown (
struct messaging_context * msg ,
void * private_data ,
uint32_t msg_type ,
struct server_id server_id ,
DATA_BLOB * data )
{
struct tevent_req * req = talloc_get_type_abort (
private_data , struct tevent_req ) ;
tevent_req_done ( req ) ;
}
static int rpc_worker_recv ( struct tevent_req * req )
{
return tevent_req_simple_recv_unix ( req ) ;
}
static void sig_term_handler (
struct tevent_context * ev ,
struct tevent_signal * se ,
int signum ,
int count ,
void * siginfo ,
void * private_data )
{
exit ( 0 ) ;
}
static void sig_hup_handler (
struct tevent_context * ev ,
struct tevent_signal * se ,
int signum ,
int count ,
void * siginfo ,
void * private_data )
{
change_to_root_user ( ) ;
lp_load_with_shares ( get_dyn_CONFIGFILE ( ) ) ;
}
static NTSTATUS register_ep_server (
struct dcesrv_context * dce_ctx ,
const struct dcesrv_endpoint_server * ep_server )
{
NTSTATUS status ;
DBG_DEBUG ( " Registering server %s \n " , ep_server - > name ) ;
status = dcerpc_register_ep_server ( ep_server ) ;
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_COLLISION ) ) {
DBG_ERR ( " Failed to register '%s' endpoint server: %s \n " ,
ep_server - > name ,
nt_errstr ( status ) ) ;
return status ;
}
status = dcesrv_init_ep_server ( dce_ctx , ep_server - > name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " dcesrv_init_ep_server(%s) failed: %s \n " ,
ep_server - > name ,
nt_errstr ( status ) ) ;
return status ;
}
return NT_STATUS_OK ;
}
/**
* @ brief Main function for RPC server implementations
*
* This function provides all that is necessary to run a RPC server
* inside the samba - dcerpcd framework . Just pass argv and argc on to
* this function .
*
* The get_interfaces ( ) callback provides the information that is
* passed to samba - dcerpcd via - - list - interfaces , it should not do any
* real RPC server initialization work . Quickly after this function is
* called by rpc_worker_main , the process exits again . It should
* return the number of interfaces provided .
*
* get_servers ( ) is called when the process is about to do the real
* work . So more heavy - weight initialization should happen here . It
* should return the number of server implementations provided .
*
* @ param [ in ] argc argc from main ( )
* @ param [ in ] argv argv from main ( )
* @ param [ in ] get_interfaces List all interfaces that this server provides
* @ param [ in ] get_servers Provide the RPC server implementations
* @ param [ in ] private_data Passed to the callback functions
* @ return 0 It should never return except on successful process exit
*/
int rpc_worker_main (
int argc ,
const char * argv [ ] ,
const char * daemon_config_name ,
int num_workers ,
int idle_seconds ,
size_t ( * get_interfaces ) (
const struct ndr_interface_table * * * ifaces ,
void * private_data ) ,
size_t ( * get_servers ) (
struct dcesrv_context * dce_ctx ,
const struct dcesrv_endpoint_server * * * ep_servers ,
void * private_data ) ,
void * private_data )
{
const struct loadparm_substitution * lp_sub =
loadparm_s3_global_substitution ( ) ;
const char * progname = getprogname ( ) ;
TALLOC_CTX * frame = NULL ;
struct tevent_context * ev_ctx = NULL ;
struct tevent_req * req = NULL ;
struct messaging_context * msg_ctx = NULL ;
struct dcesrv_context * dce_ctx = NULL ;
struct tevent_signal * se = NULL ;
poptContext pc ;
int opt ;
NTSTATUS status ;
int ret ;
int worker_group = - 1 ;
int worker_index = - 1 ;
bool log_stdout ;
int list_interfaces = 0 ;
struct rpc_worker * worker = NULL ;
const struct dcesrv_endpoint_server * * ep_servers ;
size_t i , num_servers ;
bool ok ;
struct poptOption long_options [ ] = {
POPT_AUTOHELP
{
. longName = " list-interfaces " ,
. argInfo = POPT_ARG_NONE ,
. arg = & list_interfaces ,
. descrip = " List the interfaces provided " ,
} ,
{
. longName = " worker-group " ,
. argInfo = POPT_ARG_INT ,
. arg = & worker_group ,
. descrip = " Group index in status message " ,
} ,
{
. longName = " worker-index " ,
. argInfo = POPT_ARG_INT ,
. arg = & worker_index ,
. descrip = " Worker index in status message " ,
} ,
POPT_COMMON_SAMBA
POPT_TABLEEND
} ;
static const struct smbd_shim smbd_shim_fns = {
. become_authenticated_pipe_user =
smbd_become_authenticated_pipe_user ,
. unbecome_authenticated_pipe_user =
smbd_unbecome_authenticated_pipe_user ,
. become_root = smbd_become_root ,
. unbecome_root = smbd_unbecome_root ,
} ;
closefrom ( 3 ) ;
talloc_enable_null_tracking ( ) ;
frame = talloc_stackframe ( ) ;
umask ( 0 ) ;
smb_init_locale ( ) ;
ok = samba_cmdline_init ( frame ,
SAMBA_CMDLINE_CONFIG_SERVER ,
true /* require_smbconf */ ) ;
if ( ! ok ) {
DBG_ERR ( " Failed to init cmdline parser! \n " ) ;
TALLOC_FREE ( frame ) ;
exit ( ENOMEM ) ;
}
pc = samba_popt_get_context ( progname , argc , argv , long_options , 0 ) ;
if ( pc = = NULL ) {
DBG_ERR ( " Failed to setup popt context! \n " ) ;
TALLOC_FREE ( frame ) ;
exit ( 1 ) ;
}
while ( ( opt = poptGetNextOpt ( pc ) ) ! = - 1 ) {
d_fprintf ( stderr ,
" \n Invalid option %s: %s \n \n " ,
poptBadOption ( pc , 0 ) ,
poptStrerror ( opt ) ) ;
poptPrintUsage ( pc , stderr , 0 ) ;
TALLOC_FREE ( frame ) ;
exit ( 1 ) ;
} ;
poptFreeContext ( pc ) ;
if ( list_interfaces ! = 0 ) {
const struct ndr_interface_table * * ifaces = NULL ;
size_t num_ifaces ;
num_workers = lp_parm_int (
- 1 , daemon_config_name , " num_workers " , num_workers ) ;
idle_seconds = lp_parm_int (
- 1 , daemon_config_name , " idle_seconds " , idle_seconds ) ;
DBG_DEBUG ( " daemon=%s, num_workers=%d, idle_seconds=%d \n " ,
daemon_config_name ,
num_workers ,
idle_seconds ) ;
fprintf ( stdout , " %d \n %d \n " , num_workers , idle_seconds ) ;
num_ifaces = get_interfaces ( & ifaces , private_data ) ;
for ( i = 0 ; i < num_ifaces ; i + + ) {
rpc_worker_print_interface ( stdout , ifaces [ i ] ) ;
}
TALLOC_FREE ( frame ) ;
exit ( 0 ) ;
}
log_stdout = ( debug_get_log_type ( ) = = DEBUG_STDOUT ) ;
if ( log_stdout ! = 0 ) {
setup_logging ( argv [ 0 ] , DEBUG_STDOUT ) ;
} else {
setup_logging ( argv [ 0 ] , DEBUG_FILE ) ;
}
set_smbd_shim ( & smbd_shim_fns ) ;
dump_core_setup ( progname , lp_logfile ( talloc_tos ( ) , lp_sub ) ) ;
/* POSIX demands that signals are inherited. If the invoking
* process has these signals masked , we will have problems , as
* we won ' t receive them . */
BlockSignals ( False , SIGHUP ) ;
BlockSignals ( False , SIGUSR1 ) ;
BlockSignals ( False , SIGTERM ) ;
# if defined(SIGFPE)
/* we are never interested in SIGFPE */
BlockSignals ( True , SIGFPE ) ;
# endif
/* We no longer use USR2... */
# if defined(SIGUSR2)
BlockSignals ( True , SIGUSR2 ) ;
# endif
/* Ignore children - no zombies. */
CatchChild ( ) ;
DEBUG ( 0 , ( " %s version %s started. \n " ,
progname ,
samba_version_string ( ) ) ) ;
DEBUGADD ( 0 , ( " %s \n " , COPYRIGHT_STARTUP_MESSAGE ) ) ;
msg_ctx = global_messaging_context ( ) ;
if ( msg_ctx = = NULL ) {
DBG_ERR ( " global_messaging_context() failed \n " ) ;
TALLOC_FREE ( frame ) ;
exit ( 1 ) ;
}
ev_ctx = messaging_tevent_context ( msg_ctx ) ;
worker = rpc_worker_new ( ev_ctx , msg_ctx ) ;
if ( worker = = NULL ) {
DBG_ERR ( " rpc_worker_new failed \n " ) ;
global_messaging_context_free ( ) ;
TALLOC_FREE ( frame ) ;
exit ( 1 ) ;
}
dce_ctx = rpc_worker_dce_ctx ( worker ) ;
se = tevent_add_signal (
ev_ctx , ev_ctx , SIGTERM , 0 , sig_term_handler , NULL ) ;
if ( se = = NULL ) {
DBG_ERR ( " tevent_add_signal failed \n " ) ;
global_messaging_context_free ( ) ;
TALLOC_FREE ( frame ) ;
exit ( 1 ) ;
}
BlockSignals ( false , SIGTERM ) ;
se = tevent_add_signal (
ev_ctx , ev_ctx , SIGHUP , 0 , sig_hup_handler , NULL ) ;
if ( se = = NULL ) {
DBG_ERR ( " tevent_add_signal failed \n " ) ;
global_messaging_context_free ( ) ;
TALLOC_FREE ( frame ) ;
exit ( 1 ) ;
}
BlockSignals ( false , SIGHUP ) ;
( void ) winbind_off ( ) ;
ok = init_guest_session_info ( NULL ) ;
( void ) winbind_on ( ) ;
if ( ! ok ) {
DBG_WARNING ( " init_guest_session_info failed \n " ) ;
global_messaging_context_free ( ) ;
TALLOC_FREE ( frame ) ;
exit ( 1 ) ;
}
status = init_system_session_info ( NULL ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " init_system_session_info failed: %s \n " ,
nt_errstr ( status ) ) ;
global_messaging_context_free ( ) ;
TALLOC_FREE ( frame ) ;
exit ( 1 ) ;
}
DBG_INFO ( " Initializing DCE/RPC registered endpoint servers \n " ) ;
num_servers = get_servers ( dce_ctx , & ep_servers , private_data ) ;
DBG_DEBUG ( " get_servers() returned %zu servers \n " , num_servers ) ;
for ( i = 0 ; i < num_servers ; i + + ) {
status = register_ep_server ( dce_ctx , ep_servers [ i ] ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " register_ep_server failed: %s \n " ,
nt_errstr ( status ) ) ;
global_messaging_context_free ( ) ;
TALLOC_FREE ( frame ) ;
exit ( 1 ) ;
}
}
req = rpc_worker_send (
ev_ctx , ev_ctx , worker , getppid ( ) , worker_group , worker_index ) ;
if ( req = = NULL ) {
DBG_ERR ( " rpc_worker_send failed \n " ) ;
global_messaging_context_free ( ) ;
TALLOC_FREE ( frame ) ;
exit ( 1 ) ;
}
DBG_DEBUG ( " %s worker running \n " , progname ) ;
while ( tevent_req_is_in_progress ( req ) ) {
TALLOC_CTX * loop_frame = NULL ;
loop_frame = talloc_stackframe ( ) ;
ret = tevent_loop_once ( ev_ctx ) ;
TALLOC_FREE ( loop_frame ) ;
if ( ret ! = 0 ) {
DBG_WARNING ( " tevent_req_once() failed: %s \n " ,
strerror ( errno ) ) ;
global_messaging_context_free ( ) ;
TALLOC_FREE ( frame ) ;
exit ( 1 ) ;
}
}
status = dcesrv_shutdown_registered_ep_servers ( dce_ctx ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " Shutdown failed with: %s \n " ,
nt_errstr ( status ) ) ;
}
ret = rpc_worker_recv ( req ) ;
if ( ret ! = 0 ) {
DBG_DEBUG ( " rpc_worker_recv returned %s \n " , strerror ( ret ) ) ;
global_messaging_context_free ( ) ;
TALLOC_FREE ( frame ) ;
exit ( 1 ) ;
}
TALLOC_FREE ( frame ) ;
return 0 ;
}