2010-10-30 04:33:53 +04:00
/*
Unix SMB / CIFS implementation .
server side dcerpc common code
Copyright ( C ) Andrew Tridgell 2003 - 2010
Copyright ( C ) Stefan ( metze ) Metzmacher 2004 - 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
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 "includes.h"
2019-10-03 20:38:31 +03:00
# include "librpc/rpc/dcesrv_core.h"
# include "librpc/rpc/dcesrv_core_proto.h"
2021-04-02 13:20:38 +03:00
# include "librpc/rpc/dcerpc_util.h"
2010-10-30 04:33:53 +04:00
# include "auth/gensec/gensec.h"
2019-10-01 00:35:55 +03:00
# include "lib/util/dlinklist.h"
2010-10-30 04:33:53 +04:00
# include "param/param.h"
/*
move a call from an existing linked list to the specified list . This
prevents bugs where we forget to remove the call from a previous
list when moving it .
*/
static void dcesrv_call_set_list ( struct dcesrv_call_state * call ,
enum dcesrv_call_list list )
{
switch ( call - > list ) {
case DCESRV_LIST_NONE :
break ;
case DCESRV_LIST_CALL_LIST :
DLIST_REMOVE ( call - > conn - > call_list , call ) ;
break ;
case DCESRV_LIST_FRAGMENTED_CALL_LIST :
DLIST_REMOVE ( call - > conn - > incoming_fragmented_call_list , call ) ;
break ;
case DCESRV_LIST_PENDING_CALL_LIST :
DLIST_REMOVE ( call - > conn - > pending_call_list , call ) ;
break ;
}
call - > list = list ;
switch ( list ) {
case DCESRV_LIST_NONE :
break ;
case DCESRV_LIST_CALL_LIST :
2016-02-05 13:32:18 +03:00
DLIST_ADD_END ( call - > conn - > call_list , call ) ;
2010-10-30 04:33:53 +04:00
break ;
case DCESRV_LIST_FRAGMENTED_CALL_LIST :
2016-02-05 13:32:18 +03:00
DLIST_ADD_END ( call - > conn - > incoming_fragmented_call_list , call ) ;
2010-10-30 04:33:53 +04:00
break ;
case DCESRV_LIST_PENDING_CALL_LIST :
2016-02-05 13:32:18 +03:00
DLIST_ADD_END ( call - > conn - > pending_call_list , call ) ;
2010-10-30 04:33:53 +04:00
break ;
}
}
void dcesrv_init_hdr ( struct ncacn_packet * pkt , bool bigendian )
{
pkt - > rpc_vers = 5 ;
pkt - > rpc_vers_minor = 0 ;
if ( bigendian ) {
pkt - > drep [ 0 ] = 0 ;
} else {
pkt - > drep [ 0 ] = DCERPC_DREP_LE ;
}
pkt - > drep [ 1 ] = 0 ;
pkt - > drep [ 2 ] = 0 ;
pkt - > drep [ 3 ] = 0 ;
}
/*
return a dcerpc fault
*/
2015-06-26 09:10:46 +03:00
NTSTATUS dcesrv_fault_with_flags ( struct dcesrv_call_state * call ,
uint32_t fault_code ,
uint8_t extra_flags )
2010-10-30 04:33:53 +04:00
{
struct ncacn_packet pkt ;
struct data_blob_list_item * rep ;
NTSTATUS status ;
2023-11-24 16:42:35 +03:00
if ( call - > conn - > terminate ! = NULL ) {
/*
* If we ' re already disconnecting
* we should just drop a possible
* response
*/
talloc_free ( call ) ;
return NT_STATUS_OK ;
}
2015-06-26 09:10:46 +03:00
/* setup a fault */
2010-10-30 04:33:53 +04:00
dcesrv_init_hdr ( & pkt , lpcfg_rpc_big_endian ( call - > conn - > dce_ctx - > lp_ctx ) ) ;
pkt . auth_length = 0 ;
pkt . call_id = call - > pkt . call_id ;
pkt . ptype = DCERPC_PKT_FAULT ;
2015-06-26 09:10:46 +03:00
pkt . pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags ;
2015-06-26 09:10:46 +03:00
pkt . u . fault . alloc_hint = 24 ;
2016-09-14 02:17:19 +03:00
if ( call - > context ! = NULL ) {
pkt . u . fault . context_id = call - > context - > context_id ;
} else {
2015-06-26 09:10:46 +03:00
pkt . u . fault . context_id = 0 ;
}
2010-10-30 04:33:53 +04:00
pkt . u . fault . cancel_count = 0 ;
2015-10-09 07:51:16 +03:00
pkt . u . fault . flags = 0 ;
2010-10-30 04:33:53 +04:00
pkt . u . fault . status = fault_code ;
2015-10-09 07:51:16 +03:00
pkt . u . fault . reserved = 0 ;
pkt . u . fault . error_and_verifier = data_blob_null ;
2010-10-30 04:33:53 +04:00
2015-06-26 09:10:46 +03:00
rep = talloc_zero ( call , struct data_blob_list_item ) ;
2010-10-30 04:33:53 +04:00
if ( ! rep ) {
return NT_STATUS_NO_MEMORY ;
}
2019-10-03 17:40:53 +03:00
status = dcerpc_ncacn_push_auth ( & rep - > blob , call , & pkt , NULL ) ;
2010-10-30 04:33:53 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dcerpc_set_frag_length ( & rep - > blob , rep - > blob . length ) ;
2016-02-05 13:32:18 +03:00
DLIST_ADD_END ( call - > replies , rep ) ;
2010-10-30 04:33:53 +04:00
dcesrv_call_set_list ( call , DCESRV_LIST_CALL_LIST ) ;
if ( call - > conn - > call_list & & call - > conn - > call_list - > replies ) {
if ( call - > conn - > transport . report_output_data ) {
call - > conn - > transport . report_output_data ( call - > conn ) ;
}
}
return NT_STATUS_OK ;
}
2015-06-26 09:10:46 +03:00
NTSTATUS dcesrv_fault ( struct dcesrv_call_state * call , uint32_t fault_code )
{
return dcesrv_fault_with_flags ( call , fault_code , 0 ) ;
}
2010-10-30 04:33:53 +04:00
_PUBLIC_ NTSTATUS dcesrv_reply ( struct dcesrv_call_state * call )
{
struct ndr_push * push ;
NTSTATUS status ;
DATA_BLOB stub ;
uint32_t total_length , chunk_size ;
struct dcesrv_connection_context * context = call - > context ;
2018-10-31 16:44:33 +03:00
struct dcesrv_auth * auth = call - > auth_state ;
2010-10-30 04:33:53 +04:00
size_t sig_size = 0 ;
2023-11-24 16:42:35 +03:00
/*
* call the reply function ,
* it ' s mostly for debug messages
* and dcesrv_fault ( ) also checks for
* ( call - > conn - > terminate ! = NULL ) internally .
*/
2010-10-30 04:33:53 +04:00
status = context - > iface - > reply ( call , call , call - > r ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return dcesrv_fault ( call , call - > fault_code ) ;
}
2023-11-24 16:42:35 +03:00
if ( call - > conn - > terminate ! = NULL ) {
/*
* If we ' re already disconnecting
* we should just drop a possible
* response
*/
talloc_free ( call ) ;
return NT_STATUS_OK ;
}
2010-10-30 04:33:53 +04:00
/* form the reply NDR */
push = ndr_push_init_ctx ( call ) ;
NT_STATUS_HAVE_NO_MEMORY ( push ) ;
/* carry over the pointer count to the reply in case we are
using full pointer . See NDR specification for full
pointers */
push - > ptr_count = call - > ndr_pull - > ptr_count ;
if ( lpcfg_rpc_big_endian ( call - > conn - > dce_ctx - > lp_ctx ) ) {
push - > flags | = LIBNDR_FLAG_BIGENDIAN ;
}
2023-08-14 13:48:28 +03:00
if ( context - > ndr64 ) {
push - > flags | = LIBNDR_FLAG_NDR64 ;
}
2010-10-30 04:33:53 +04:00
status = context - > iface - > ndr_push ( call , call , push , call - > r ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return dcesrv_fault ( call , call - > fault_code ) ;
}
stub = ndr_push_blob ( push ) ;
2019-11-06 07:27:08 +03:00
dcesrv_save_ndr_fuzz_seed ( stub ,
call ,
NDR_OUT ) ;
2010-10-30 04:33:53 +04:00
total_length = stub . length ;
/* we can write a full max_recv_frag size, minus the dcerpc
request header size */
2015-06-26 09:10:46 +03:00
chunk_size = call - > conn - > max_xmit_frag ;
2010-10-30 04:33:53 +04:00
chunk_size - = DCERPC_REQUEST_LENGTH ;
2018-10-31 19:12:02 +03:00
if ( auth - > auth_finished & & auth - > gensec_security ! = NULL ) {
2015-06-19 23:35:44 +03:00
size_t max_payload = chunk_size ;
max_payload - = DCERPC_AUTH_TRAILER_LENGTH ;
max_payload - = ( max_payload % DCERPC_AUTH_PAD_ALIGNMENT ) ;
2018-10-31 19:12:02 +03:00
sig_size = gensec_sig_size ( auth - > gensec_security ,
2015-06-19 23:35:44 +03:00
max_payload ) ;
2010-10-30 04:33:53 +04:00
if ( sig_size ) {
chunk_size - = DCERPC_AUTH_TRAILER_LENGTH ;
chunk_size - = sig_size ;
}
}
2015-06-19 23:35:44 +03:00
chunk_size - = ( chunk_size % DCERPC_AUTH_PAD_ALIGNMENT ) ;
2010-10-30 04:33:53 +04:00
do {
uint32_t length ;
struct data_blob_list_item * rep ;
struct ncacn_packet pkt ;
2015-10-23 17:06:17 +03:00
bool ok ;
2010-10-30 04:33:53 +04:00
2015-06-26 09:10:46 +03:00
rep = talloc_zero ( call , struct data_blob_list_item ) ;
2010-10-30 04:33:53 +04:00
NT_STATUS_HAVE_NO_MEMORY ( rep ) ;
length = MIN ( chunk_size , stub . length ) ;
/* form the dcerpc response packet */
2010-11-03 11:23:00 +03:00
dcesrv_init_hdr ( & pkt ,
lpcfg_rpc_big_endian ( call - > conn - > dce_ctx - > lp_ctx ) ) ;
2010-10-30 04:33:53 +04:00
pkt . auth_length = 0 ;
pkt . call_id = call - > pkt . call_id ;
pkt . ptype = DCERPC_PKT_RESPONSE ;
pkt . pfc_flags = 0 ;
if ( stub . length = = total_length ) {
pkt . pfc_flags | = DCERPC_PFC_FLAG_FIRST ;
}
if ( length = = stub . length ) {
pkt . pfc_flags | = DCERPC_PFC_FLAG_LAST ;
}
pkt . u . response . alloc_hint = stub . length ;
2016-09-14 02:17:19 +03:00
/*
* bug for bug , feature for feature . . .
*
* Windows truncates the context_id with & 0xFF ,
* so we do .
*/
pkt . u . response . context_id = context - > context_id & 0xFF ;
2010-10-30 04:33:53 +04:00
pkt . u . response . cancel_count = 0 ;
pkt . u . response . stub_and_verifier . data = stub . data ;
pkt . u . response . stub_and_verifier . length = length ;
2015-10-23 17:06:17 +03:00
ok = dcesrv_auth_pkt_push ( call , & rep - > blob , sig_size ,
DCERPC_RESPONSE_LENGTH ,
& pkt . u . response . stub_and_verifier ,
& pkt ) ;
if ( ! ok ) {
2010-10-30 04:33:53 +04:00
return dcesrv_fault ( call , DCERPC_FAULT_OTHER ) ;
}
dcerpc_set_frag_length ( & rep - > blob , rep - > blob . length ) ;
2016-02-05 13:32:18 +03:00
DLIST_ADD_END ( call - > replies , rep ) ;
2010-10-30 04:33:53 +04:00
stub . data + = length ;
stub . length - = length ;
} while ( stub . length ! = 0 ) ;
/* move the call from the pending to the finished calls list */
dcesrv_call_set_list ( call , DCESRV_LIST_CALL_LIST ) ;
if ( call - > conn - > call_list & & call - > conn - > call_list - > replies ) {
if ( call - > conn - > transport . report_output_data ) {
call - > conn - > transport . report_output_data ( call - > conn ) ;
}
}
return NT_STATUS_OK ;
}
2023-08-14 13:58:14 +03:00
_PUBLIC_ void _dcesrv_async_reply ( struct dcesrv_call_state * call ,
const char * func ,
const char * location )
{
struct dcesrv_connection * conn = call - > conn ;
NTSTATUS status ;
status = dcesrv_reply ( call ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
D_ERR ( " %s: %s: dcesrv_async_reply() failed - %s \n " ,
func , location , nt_errstr ( status ) ) ;
dcesrv_terminate_connection ( conn , nt_errstr ( status ) ) ;
}
}