2010-10-30 11:33:53 +11: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 19:38:31 +02:00
# include "librpc/rpc/dcesrv_core.h"
# include "librpc/rpc/dcesrv_core_proto.h"
2021-04-02 12:20:38 +02:00
# include "librpc/rpc/dcerpc_util.h"
2010-10-30 11:33:53 +11:00
# include "auth/gensec/gensec.h"
2019-09-30 23:35:55 +02:00
# include "lib/util/dlinklist.h"
2010-10-30 11:33:53 +11: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 11:32:18 +01:00
DLIST_ADD_END ( call - > conn - > call_list , call ) ;
2010-10-30 11:33:53 +11:00
break ;
case DCESRV_LIST_FRAGMENTED_CALL_LIST :
2016-02-05 11:32:18 +01:00
DLIST_ADD_END ( call - > conn - > incoming_fragmented_call_list , call ) ;
2010-10-30 11:33:53 +11:00
break ;
case DCESRV_LIST_PENDING_CALL_LIST :
2016-02-05 11:32:18 +01:00
DLIST_ADD_END ( call - > conn - > pending_call_list , call ) ;
2010-10-30 11:33:53 +11: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 08:10:46 +02:00
NTSTATUS dcesrv_fault_with_flags ( struct dcesrv_call_state * call ,
uint32_t fault_code ,
uint8_t extra_flags )
2010-10-30 11:33:53 +11:00
{
struct ncacn_packet pkt ;
struct data_blob_list_item * rep ;
NTSTATUS status ;
2015-06-26 08:10:46 +02:00
/* setup a fault */
2010-10-30 11:33:53 +11: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 08:10:46 +02:00
pkt . pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST | extra_flags ;
2015-06-26 08:10:46 +02:00
pkt . u . fault . alloc_hint = 24 ;
2016-09-14 01:17:19 +02:00
if ( call - > context ! = NULL ) {
pkt . u . fault . context_id = call - > context - > context_id ;
} else {
2015-06-26 08:10:46 +02:00
pkt . u . fault . context_id = 0 ;
}
2010-10-30 11:33:53 +11:00
pkt . u . fault . cancel_count = 0 ;
2015-10-09 06:51:16 +02:00
pkt . u . fault . flags = 0 ;
2010-10-30 11:33:53 +11:00
pkt . u . fault . status = fault_code ;
2015-10-09 06:51:16 +02:00
pkt . u . fault . reserved = 0 ;
pkt . u . fault . error_and_verifier = data_blob_null ;
2010-10-30 11:33:53 +11:00
2015-06-26 08:10:46 +02:00
rep = talloc_zero ( call , struct data_blob_list_item ) ;
2010-10-30 11:33:53 +11:00
if ( ! rep ) {
return NT_STATUS_NO_MEMORY ;
}
2019-10-03 16:40:53 +02:00
status = dcerpc_ncacn_push_auth ( & rep - > blob , call , & pkt , NULL ) ;
2010-10-30 11:33:53 +11:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
dcerpc_set_frag_length ( & rep - > blob , rep - > blob . length ) ;
2016-02-05 11:32:18 +01:00
DLIST_ADD_END ( call - > replies , rep ) ;
2010-10-30 11:33:53 +11: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 08:10:46 +02: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 11:33:53 +11: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 14:44:33 +01:00
struct dcesrv_auth * auth = call - > auth_state ;
2010-10-30 11:33:53 +11:00
size_t sig_size = 0 ;
/* call the reply function */
status = context - > iface - > reply ( call , call , call - > r ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return dcesrv_fault ( call , call - > fault_code ) ;
}
/* 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 ;
}
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 17:27:08 +13:00
dcesrv_save_ndr_fuzz_seed ( stub ,
call ,
NDR_OUT ) ;
2010-10-30 11:33:53 +11:00
total_length = stub . length ;
/* we can write a full max_recv_frag size, minus the dcerpc
request header size */
2015-06-26 08:10:46 +02:00
chunk_size = call - > conn - > max_xmit_frag ;
2010-10-30 11:33:53 +11:00
chunk_size - = DCERPC_REQUEST_LENGTH ;
2018-10-31 17:12:02 +01:00
if ( auth - > auth_finished & & auth - > gensec_security ! = NULL ) {
2015-06-19 22:35:44 +02:00
size_t max_payload = chunk_size ;
max_payload - = DCERPC_AUTH_TRAILER_LENGTH ;
max_payload - = ( max_payload % DCERPC_AUTH_PAD_ALIGNMENT ) ;
2018-10-31 17:12:02 +01:00
sig_size = gensec_sig_size ( auth - > gensec_security ,
2015-06-19 22:35:44 +02:00
max_payload ) ;
2010-10-30 11:33:53 +11:00
if ( sig_size ) {
chunk_size - = DCERPC_AUTH_TRAILER_LENGTH ;
chunk_size - = sig_size ;
}
}
2015-06-19 22:35:44 +02:00
chunk_size - = ( chunk_size % DCERPC_AUTH_PAD_ALIGNMENT ) ;
2010-10-30 11:33:53 +11:00
do {
uint32_t length ;
struct data_blob_list_item * rep ;
struct ncacn_packet pkt ;
2015-10-23 16:06:17 +02:00
bool ok ;
2010-10-30 11:33:53 +11:00
2015-06-26 08:10:46 +02:00
rep = talloc_zero ( call , struct data_blob_list_item ) ;
2010-10-30 11:33:53 +11:00
NT_STATUS_HAVE_NO_MEMORY ( rep ) ;
length = MIN ( chunk_size , stub . length ) ;
/* form the dcerpc response packet */
2010-11-03 09:23:00 +01:00
dcesrv_init_hdr ( & pkt ,
lpcfg_rpc_big_endian ( call - > conn - > dce_ctx - > lp_ctx ) ) ;
2010-10-30 11:33:53 +11: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 01:17:19 +02: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 11:33:53 +11: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 16:06:17 +02: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 11:33:53 +11:00
return dcesrv_fault ( call , DCERPC_FAULT_OTHER ) ;
}
dcerpc_set_frag_length ( & rep - > blob , rep - > blob . length ) ;
2016-02-05 11:32:18 +01:00
DLIST_ADD_END ( call - > replies , rep ) ;
2010-10-30 11:33:53 +11: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 ;
}