2003-12-11 01:21:04 +03:00
/*
Unix SMB / CIFS implementation .
server side dcerpc core code
Copyright ( C ) Andrew Tridgell 2003
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 2 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 , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
/*
find the set of endpoint operations for an endpoint server
*/
2003-12-13 13:58:48 +03:00
static const struct dcesrv_endpoint_ops * find_endpoint ( struct dcesrv_context * dce ,
2003-12-11 01:21:04 +03:00
const struct dcesrv_endpoint * endpoint )
{
struct dce_endpoint * ep ;
2003-12-13 13:58:48 +03:00
for ( ep = dce - > endpoint_list ; ep ; ep = ep - > next ) {
2003-12-12 06:59:09 +03:00
if ( ep - > endpoint_ops - > query_endpoint ( endpoint ) ) {
2003-12-11 01:21:04 +03:00
return ep - > endpoint_ops ;
}
}
return NULL ;
}
2003-12-12 08:01:41 +03:00
/*
find a call that is pending in our call list
*/
static struct dcesrv_call_state * dcesrv_find_call ( struct dcesrv_state * dce , uint16 call_id )
{
struct dcesrv_call_state * c ;
for ( c = dce - > call_list ; c ; c = c - > next ) {
if ( c - > pkt . call_id = = call_id ) {
return c ;
}
}
return NULL ;
}
2003-12-11 01:21:04 +03:00
/*
register an endpoint server
*/
2003-12-13 13:58:48 +03:00
BOOL dcesrv_endpoint_register ( struct dcesrv_context * dce ,
2003-12-11 01:21:04 +03:00
const struct dcesrv_endpoint_ops * ops )
{
struct dce_endpoint * ep ;
ep = malloc ( sizeof ( * ep ) ) ;
if ( ! ep ) {
return False ;
}
ep - > endpoint_ops = ops ;
2003-12-13 13:58:48 +03:00
DLIST_ADD ( dce - > endpoint_list , ep ) ;
2003-12-11 01:21:04 +03:00
return True ;
}
/*
connect to a dcerpc endpoint
*/
2003-12-13 13:58:48 +03:00
NTSTATUS dcesrv_endpoint_connect_ops ( struct dcesrv_context * dce ,
const struct dcesrv_endpoint * endpoint ,
const struct dcesrv_endpoint_ops * ops ,
struct dcesrv_state * * p )
2003-12-11 01:21:04 +03:00
{
TALLOC_CTX * mem_ctx ;
NTSTATUS status ;
mem_ctx = talloc_init ( " dcesrv_endpoint_connect " ) ;
if ( ! mem_ctx ) {
return NT_STATUS_NO_MEMORY ;
}
2003-12-13 05:20:40 +03:00
* p = talloc_p ( mem_ctx , struct dcesrv_state ) ;
2003-12-11 01:21:04 +03:00
if ( ! * p ) {
talloc_destroy ( mem_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
2003-12-13 13:58:48 +03:00
( * p ) - > dce = dce ;
2003-12-11 01:21:04 +03:00
( * p ) - > mem_ctx = mem_ctx ;
( * p ) - > endpoint = * endpoint ;
( * p ) - > ops = ops ;
( * p ) - > private = NULL ;
2003-12-12 06:59:09 +03:00
( * p ) - > call_list = NULL ;
2003-12-12 08:30:00 +03:00
( * p ) - > cli_max_recv_frag = 0 ;
2003-12-12 09:12:41 +03:00
( * p ) - > ndr = NULL ;
( * p ) - > dispatch = NULL ;
2003-12-13 05:20:40 +03:00
( * p ) - > handles = NULL ;
2003-12-13 06:23:41 +03:00
( * p ) - > next_handle = 0 ;
2003-12-11 01:21:04 +03:00
/* make sure the endpoint server likes the connection */
status = ops - > connect ( * p ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_destroy ( mem_ctx ) ;
return status ;
}
return NT_STATUS_OK ;
}
2003-12-13 13:58:48 +03:00
/*
connect to a dcerpc endpoint
*/
NTSTATUS dcesrv_endpoint_connect ( struct dcesrv_context * dce ,
const struct dcesrv_endpoint * endpoint ,
struct dcesrv_state * * p )
{
const struct dcesrv_endpoint_ops * ops ;
/* make sure this endpoint exists */
ops = find_endpoint ( dce , endpoint ) ;
if ( ! ops ) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
return dcesrv_endpoint_connect_ops ( dce , endpoint , ops , p ) ;
}
2003-12-11 01:21:04 +03:00
/*
disconnect a link to an endpoint
*/
void dcesrv_endpoint_disconnect ( struct dcesrv_state * p )
{
p - > ops - > disconnect ( p ) ;
2003-12-13 05:20:40 +03:00
/* destroy any handles */
while ( p - > handles ) {
TALLOC_CTX * m = p - > handles - > mem_ctx ;
DLIST_REMOVE ( p - > handles , p - > handles ) ;
talloc_destroy ( m ) ;
}
2003-12-11 01:21:04 +03:00
talloc_destroy ( p - > mem_ctx ) ;
}
2003-12-12 06:59:09 +03:00
/*
return a dcerpc fault
*/
static NTSTATUS dcesrv_fault ( struct dcesrv_call_state * call , uint32 fault_code )
{
struct ndr_push * push ;
struct dcerpc_packet pkt ;
2003-12-12 08:30:00 +03:00
struct dcesrv_call_reply * rep ;
2003-12-12 06:59:09 +03:00
NTSTATUS status ;
/* setup a bind_ack */
pkt . rpc_vers = 5 ;
pkt . rpc_vers_minor = 0 ;
pkt . drep [ 0 ] = 0x10 ; /* Little endian */
pkt . drep [ 1 ] = 0 ;
pkt . drep [ 2 ] = 0 ;
pkt . drep [ 3 ] = 0 ;
pkt . auth_length = 0 ;
pkt . call_id = call - > pkt . call_id ;
pkt . ptype = DCERPC_PKT_FAULT ;
pkt . pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST ;
pkt . u . fault . alloc_hint = 0 ;
pkt . u . fault . context_id = 0 ;
pkt . u . fault . cancel_count = 0 ;
pkt . u . fault . status = fault_code ;
2003-12-12 09:12:41 +03:00
/* now form the NDR for the fault */
push = ndr_push_init_ctx ( call - > mem_ctx ) ;
if ( ! push ) {
return NT_STATUS_NO_MEMORY ;
}
status = ndr_push_dcerpc_packet ( push , NDR_SCALARS | NDR_BUFFERS , & pkt ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2003-12-13 05:20:40 +03:00
rep = talloc_p ( call - > mem_ctx , struct dcesrv_call_reply ) ;
2003-12-12 09:12:41 +03:00
if ( ! rep ) {
return NT_STATUS_NO_MEMORY ;
}
rep - > data = ndr_push_blob ( push ) ;
SSVAL ( rep - > data . data , DCERPC_FRAG_LEN_OFFSET , rep - > data . length ) ;
DLIST_ADD_END ( call - > replies , rep , struct dcesrv_call_reply * ) ;
return NT_STATUS_OK ;
}
2003-12-13 05:20:40 +03:00
/*
return a dcerpc fault from a ntstatus code
*/
static NTSTATUS dcesrv_fault_nt ( struct dcesrv_call_state * call , NTSTATUS status )
{
uint32 fault_code = DCERPC_FAULT_OTHER ;
/* TODO: we need to expand this table to include more mappings */
if ( NT_STATUS_EQUAL ( status , NT_STATUS_INVALID_HANDLE ) ) {
fault_code = DCERPC_FAULT_CONTEXT_MISMATCH ;
}
return dcesrv_fault ( call , fault_code ) ;
}
2003-12-12 09:12:41 +03:00
/*
return a dcerpc bind_nak
*/
static NTSTATUS dcesrv_bind_nak ( struct dcesrv_call_state * call , uint32 reason )
{
struct ndr_push * push ;
struct dcerpc_packet pkt ;
struct dcesrv_call_reply * rep ;
NTSTATUS status ;
/* setup a bind_ack */
pkt . rpc_vers = 5 ;
pkt . rpc_vers_minor = 0 ;
pkt . drep [ 0 ] = 0x10 ; /* Little endian */
pkt . drep [ 1 ] = 0 ;
pkt . drep [ 2 ] = 0 ;
pkt . drep [ 3 ] = 0 ;
pkt . auth_length = 0 ;
pkt . call_id = call - > pkt . call_id ;
pkt . ptype = DCERPC_PKT_BIND_NAK ;
pkt . pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST ;
pkt . u . bind_nak . reject_reason = reason ;
pkt . u . bind_nak . num_versions = 0 ;
/* now form the NDR for the bind_nak */
2003-12-12 06:59:09 +03:00
push = ndr_push_init_ctx ( call - > mem_ctx ) ;
if ( ! push ) {
return NT_STATUS_NO_MEMORY ;
}
status = ndr_push_dcerpc_packet ( push , NDR_SCALARS | NDR_BUFFERS , & pkt ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2003-12-13 05:20:40 +03:00
rep = talloc_p ( call - > mem_ctx , struct dcesrv_call_reply ) ;
2003-12-12 08:30:00 +03:00
if ( ! rep ) {
return NT_STATUS_NO_MEMORY ;
}
rep - > data = ndr_push_blob ( push ) ;
SSVAL ( rep - > data . data , DCERPC_FRAG_LEN_OFFSET , rep - > data . length ) ;
DLIST_ADD_END ( call - > replies , rep , struct dcesrv_call_reply * ) ;
2003-12-12 06:59:09 +03:00
return NT_STATUS_OK ;
}
/*
handle a bind request
*/
static NTSTATUS dcesrv_bind ( struct dcesrv_call_state * call )
{
const char * uuid , * transfer_syntax ;
uint32 if_version , transfer_syntax_version ;
struct dcerpc_packet pkt ;
struct ndr_push * push ;
2003-12-12 08:30:00 +03:00
struct dcesrv_call_reply * rep ;
2003-12-12 06:59:09 +03:00
NTSTATUS status ;
2003-12-12 09:12:41 +03:00
uint32 result = 0 , reason = 0 ;
2003-12-12 06:59:09 +03:00
if ( call - > pkt . u . bind . num_contexts ! = 1 | |
call - > pkt . u . bind . ctx_list [ 0 ] . num_transfer_syntaxes < 1 ) {
2003-12-12 09:12:41 +03:00
return dcesrv_bind_nak ( call , 0 ) ;
2003-12-12 06:59:09 +03:00
}
if_version = call - > pkt . u . bind . ctx_list [ 0 ] . abstract_syntax . major_version ;
uuid = GUID_string ( call - > mem_ctx , & call - > pkt . u . bind . ctx_list [ 0 ] . abstract_syntax . uuid ) ;
if ( ! uuid ) {
2003-12-12 09:12:41 +03:00
return dcesrv_bind_nak ( call , 0 ) ;
2003-12-12 06:59:09 +03:00
}
transfer_syntax_version = call - > pkt . u . bind . ctx_list [ 0 ] . transfer_syntaxes [ 0 ] . major_version ;
transfer_syntax = GUID_string ( call - > mem_ctx ,
& call - > pkt . u . bind . ctx_list [ 0 ] . transfer_syntaxes [ 0 ] . uuid ) ;
if ( ! transfer_syntax | |
strcasecmp ( NDR_GUID , transfer_syntax ) ! = 0 | |
NDR_GUID_VERSION ! = transfer_syntax_version ) {
/* we only do NDR encoded dcerpc */
2003-12-12 09:12:41 +03:00
return dcesrv_bind_nak ( call , 0 ) ;
2003-12-12 06:59:09 +03:00
}
if ( ! call - > dce - > ops - > set_interface ( call - > dce , uuid , if_version ) ) {
DEBUG ( 2 , ( " Request for unknown dcerpc interface %s/%d \n " , uuid , if_version ) ) ;
/* we don't know about that interface */
2003-12-12 09:12:41 +03:00
result = DCERPC_BIND_PROVIDER_REJECT ;
reason = DCERPC_BIND_REASON_ASYNTAX ;
2003-12-12 06:59:09 +03:00
}
2003-12-12 08:30:00 +03:00
if ( call - > dce - > cli_max_recv_frag = = 0 ) {
call - > dce - > cli_max_recv_frag = call - > pkt . u . bind . max_recv_frag ;
}
2003-12-12 06:59:09 +03:00
/* setup a bind_ack */
pkt . rpc_vers = 5 ;
pkt . rpc_vers_minor = 0 ;
pkt . drep [ 0 ] = 0x10 ; /* Little endian */
pkt . drep [ 1 ] = 0 ;
pkt . drep [ 2 ] = 0 ;
pkt . drep [ 3 ] = 0 ;
pkt . auth_length = 0 ;
pkt . call_id = call - > pkt . call_id ;
pkt . ptype = DCERPC_PKT_BIND_ACK ;
pkt . pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST ;
pkt . u . bind_ack . max_xmit_frag = 0x2000 ;
pkt . u . bind_ack . max_recv_frag = 0x2000 ;
pkt . u . bind_ack . assoc_group_id = call - > pkt . u . bind . assoc_group_id ;
2003-12-12 09:12:41 +03:00
if ( call - > dce - > ndr ) {
pkt . u . bind_ack . secondary_address = talloc_asprintf ( call - > mem_ctx , " \\ PIPE \\ %s " ,
call - > dce - > ndr - > name ) ;
} else {
pkt . u . bind_ack . secondary_address = " " ;
}
2003-12-12 06:59:09 +03:00
pkt . u . bind_ack . num_results = 1 ;
2003-12-13 05:20:40 +03:00
pkt . u . bind_ack . ctx_list = talloc_p ( call - > mem_ctx , struct dcerpc_ack_ctx ) ;
2003-12-12 06:59:09 +03:00
if ( ! pkt . u . bind_ack . ctx_list ) {
return NT_STATUS_NO_MEMORY ;
}
2003-12-12 09:12:41 +03:00
pkt . u . bind_ack . ctx_list [ 0 ] . result = result ;
pkt . u . bind_ack . ctx_list [ 0 ] . reason = reason ;
2003-12-12 06:59:09 +03:00
GUID_from_string ( uuid , & pkt . u . bind_ack . ctx_list [ 0 ] . syntax . uuid ) ;
pkt . u . bind_ack . ctx_list [ 0 ] . syntax . major_version = if_version ;
pkt . u . bind_ack . ctx_list [ 0 ] . syntax . minor_version = 0 ;
pkt . u . bind_ack . auth_info = data_blob ( NULL , 0 ) ;
/* now form the NDR for the bind_ack */
push = ndr_push_init_ctx ( call - > mem_ctx ) ;
if ( ! push ) {
return NT_STATUS_NO_MEMORY ;
}
status = ndr_push_dcerpc_packet ( push , NDR_SCALARS | NDR_BUFFERS , & pkt ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2003-12-13 05:20:40 +03:00
rep = talloc_p ( call - > mem_ctx , struct dcesrv_call_reply ) ;
2003-12-12 08:30:00 +03:00
if ( ! rep ) {
return NT_STATUS_NO_MEMORY ;
}
rep - > data = ndr_push_blob ( push ) ;
SSVAL ( rep - > data . data , DCERPC_FRAG_LEN_OFFSET , rep - > data . length ) ;
DLIST_ADD_END ( call - > replies , rep , struct dcesrv_call_reply * ) ;
2003-12-12 06:59:09 +03:00
return NT_STATUS_OK ;
}
/*
handle a dcerpc request packet
*/
static NTSTATUS dcesrv_request ( struct dcesrv_call_state * call )
{
struct ndr_pull * pull ;
struct ndr_push * push ;
uint16 opnum ;
void * r ;
NTSTATUS status ;
DATA_BLOB stub ;
opnum = call - > pkt . u . request . opnum ;
if ( opnum > = call - > dce - > ndr - > num_calls ) {
return dcesrv_fault ( call , DCERPC_FAULT_OP_RNG_ERROR ) ;
}
pull = ndr_pull_init_blob ( & call - > pkt . u . request . stub_and_verifier , call - > mem_ctx ) ;
if ( ! pull ) {
return NT_STATUS_NO_MEMORY ;
}
r = talloc ( call - > mem_ctx , call - > dce - > ndr - > calls [ opnum ] . struct_size ) ;
if ( ! r ) {
return NT_STATUS_NO_MEMORY ;
}
/* unravel the NDR for the packet */
status = call - > dce - > ndr - > calls [ opnum ] . ndr_pull ( pull , NDR_IN , r ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2003-12-12 09:12:41 +03:00
return dcesrv_fault ( call , DCERPC_FAULT_NDR ) ;
2003-12-12 06:59:09 +03:00
}
/* call the dispatch function */
status = call - > dce - > dispatch [ opnum ] ( call - > dce , call - > mem_ctx , r ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2003-12-13 05:20:40 +03:00
return dcesrv_fault_nt ( call , status ) ;
2003-12-12 06:59:09 +03:00
}
/* form the reply NDR */
push = ndr_push_init_ctx ( call - > mem_ctx ) ;
if ( ! push ) {
return NT_STATUS_NO_MEMORY ;
}
status = call - > dce - > ndr - > calls [ opnum ] . ndr_push ( push , NDR_OUT , r ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2003-12-12 09:12:41 +03:00
return dcesrv_fault ( call , DCERPC_FAULT_NDR ) ;
2003-12-12 06:59:09 +03:00
}
stub = ndr_push_blob ( push ) ;
2003-12-12 08:30:00 +03:00
do {
uint32 length ;
struct dcesrv_call_reply * rep ;
struct dcerpc_packet pkt ;
2003-12-12 06:59:09 +03:00
2003-12-13 05:20:40 +03:00
rep = talloc_p ( call - > mem_ctx , struct dcesrv_call_reply ) ;
2003-12-12 08:30:00 +03:00
if ( ! rep ) {
return NT_STATUS_NO_MEMORY ;
}
2003-12-12 06:59:09 +03:00
2003-12-12 08:30:00 +03:00
length = stub . length ;
if ( length + DCERPC_RESPONSE_LENGTH > call - > dce - > cli_max_recv_frag ) {
length = call - > dce - > cli_max_recv_frag - DCERPC_RESPONSE_LENGTH ;
}
2003-12-12 06:59:09 +03:00
2003-12-12 08:30:00 +03:00
/* form the dcerpc response packet */
pkt . rpc_vers = 5 ;
pkt . rpc_vers_minor = 0 ;
pkt . drep [ 0 ] = 0x10 ; /* Little endian */
pkt . drep [ 1 ] = 0 ;
pkt . drep [ 2 ] = 0 ;
pkt . drep [ 3 ] = 0 ;
pkt . auth_length = 0 ;
pkt . call_id = call - > pkt . call_id ;
pkt . ptype = DCERPC_PKT_RESPONSE ;
pkt . pfc_flags = 0 ;
if ( ! call - > replies ) {
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 ;
pkt . u . response . context_id = call - > pkt . u . request . context_id ;
pkt . u . response . cancel_count = 0 ;
pkt . u . response . stub_and_verifier . data = stub . data ;
pkt . u . response . stub_and_verifier . length = length ;
push = ndr_push_init_ctx ( call - > mem_ctx ) ;
if ( ! push ) {
return NT_STATUS_NO_MEMORY ;
}
status = ndr_push_dcerpc_packet ( push , NDR_SCALARS | NDR_BUFFERS , & pkt ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
rep - > data = ndr_push_blob ( push ) ;
SSVAL ( rep - > data . data , DCERPC_FRAG_LEN_OFFSET , rep - > data . length ) ;
DLIST_ADD_END ( call - > replies , rep , struct dcesrv_call_reply * ) ;
stub . data + = length ;
stub . length - = length ;
} while ( stub . length ! = 0 ) ;
2003-12-12 06:59:09 +03:00
return NT_STATUS_OK ;
}
2003-12-11 01:21:04 +03:00
2003-12-11 12:07:45 +03:00
/*
provide some input to a dcerpc endpoint server . This passes data
from a dcerpc client into the server
*/
2003-12-12 06:59:09 +03:00
NTSTATUS dcesrv_input ( struct dcesrv_state * dce , const DATA_BLOB * data )
2003-12-11 12:07:45 +03:00
{
2003-12-12 06:59:09 +03:00
struct ndr_pull * ndr ;
TALLOC_CTX * mem_ctx ;
NTSTATUS status ;
struct dcesrv_call_state * call ;
mem_ctx = talloc_init ( " dcesrv_input " ) ;
if ( ! mem_ctx ) {
return NT_STATUS_NO_MEMORY ;
}
2003-12-13 05:20:40 +03:00
call = talloc_p ( mem_ctx , struct dcesrv_call_state ) ;
2003-12-12 06:59:09 +03:00
if ( ! call ) {
talloc_destroy ( mem_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
call - > mem_ctx = mem_ctx ;
call - > dce = dce ;
2003-12-12 08:30:00 +03:00
call - > replies = NULL ;
2003-12-12 06:59:09 +03:00
ndr = ndr_pull_init_blob ( data , mem_ctx ) ;
if ( ! ndr ) {
talloc_destroy ( mem_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
status = ndr_pull_dcerpc_packet ( ndr , NDR_SCALARS | NDR_BUFFERS , & call - > pkt ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_destroy ( mem_ctx ) ;
return status ;
}
2003-12-12 08:01:41 +03:00
/* see if this is a continued packet */
if ( ! ( call - > pkt . pfc_flags & DCERPC_PFC_FLAG_FIRST ) ) {
struct dcesrv_call_state * call2 = call ;
uint32 alloc_size ;
/* we only allow fragmented requests, no other packet types */
if ( call - > pkt . ptype ! = DCERPC_PKT_REQUEST ) {
2003-12-12 09:12:41 +03:00
return dcesrv_fault ( call2 , DCERPC_FAULT_OTHER ) ;
2003-12-12 08:01:41 +03:00
}
2003-12-12 06:59:09 +03:00
2003-12-12 08:01:41 +03:00
/* this is a continuation of an existing call - find the call then
tack it on the end */
call = dcesrv_find_call ( dce , call2 - > pkt . call_id ) ;
if ( ! call ) {
2003-12-12 09:12:41 +03:00
return dcesrv_fault ( call2 , DCERPC_FAULT_OTHER ) ;
2003-12-12 08:01:41 +03:00
}
if ( call - > pkt . ptype ! = call2 - > pkt . ptype ) {
/* trying to play silly buggers are we? */
2003-12-12 09:12:41 +03:00
return dcesrv_fault ( call2 , DCERPC_FAULT_OTHER ) ;
2003-12-12 08:01:41 +03:00
}
alloc_size = call - > pkt . u . request . stub_and_verifier . length +
call2 - > pkt . u . request . stub_and_verifier . length ;
if ( call - > pkt . u . request . alloc_hint > alloc_size ) {
alloc_size = call - > pkt . u . request . alloc_hint ;
}
call - > pkt . u . request . stub_and_verifier . data =
talloc_realloc ( call - > mem_ctx ,
call - > pkt . u . request . stub_and_verifier . data , alloc_size ) ;
if ( ! call - > pkt . u . request . stub_and_verifier . data ) {
2003-12-12 09:12:41 +03:00
return dcesrv_fault ( call2 , DCERPC_FAULT_OTHER ) ;
2003-12-12 08:01:41 +03:00
}
memcpy ( call - > pkt . u . request . stub_and_verifier . data +
call - > pkt . u . request . stub_and_verifier . length ,
call2 - > pkt . u . request . stub_and_verifier . data ,
call2 - > pkt . u . request . stub_and_verifier . length ) ;
call - > pkt . u . request . stub_and_verifier . length + =
call2 - > pkt . u . request . stub_and_verifier . length ;
call - > pkt . pfc_flags | = ( call2 - > pkt . pfc_flags & DCERPC_PFC_FLAG_LAST ) ;
}
/* this may not be the last pdu in the chain - if its isn't then
just put it on the call_list and wait for the rest */
if ( ! ( call - > pkt . pfc_flags & DCERPC_PFC_FLAG_LAST ) ) {
DLIST_ADD_END ( dce - > call_list , call , struct dcesrv_call_state * ) ;
return NT_STATUS_OK ;
}
2003-12-12 06:59:09 +03:00
switch ( call - > pkt . ptype ) {
case DCERPC_PKT_BIND :
status = dcesrv_bind ( call ) ;
break ;
case DCERPC_PKT_REQUEST :
status = dcesrv_request ( call ) ;
break ;
default :
status = NT_STATUS_INVALID_PARAMETER ;
break ;
}
/* if we are going to be sending a reply then add
it to the list of pending calls . We add it to the end to keep the call
list in the order we will answer */
if ( NT_STATUS_IS_OK ( status ) ) {
DLIST_ADD_END ( dce - > call_list , call , struct dcesrv_call_state * ) ;
} else {
talloc_destroy ( mem_ctx ) ;
}
return status ;
2003-12-11 12:07:45 +03:00
}
/*
retrieve some output from a dcerpc server . The amount of data that
2003-12-12 06:59:09 +03:00
is wanted is in data - > length and data - > data is already allocated
to hold that much data .
2003-12-11 12:07:45 +03:00
*/
2003-12-12 06:59:09 +03:00
NTSTATUS dcesrv_output ( struct dcesrv_state * dce , DATA_BLOB * data )
2003-12-11 12:07:45 +03:00
{
2003-12-12 06:59:09 +03:00
struct dcesrv_call_state * call ;
2003-12-12 08:30:00 +03:00
struct dcesrv_call_reply * rep ;
2003-12-12 06:59:09 +03:00
call = dce - > call_list ;
2003-12-12 08:30:00 +03:00
if ( ! call | | ! call - > replies ) {
2003-12-12 06:59:09 +03:00
return NT_STATUS_FOOBAR ;
}
2003-12-12 08:30:00 +03:00
rep = call - > replies ;
if ( data - > length > = rep - > data . length ) {
data - > length = rep - > data . length ;
2003-12-12 06:59:09 +03:00
}
2003-12-12 08:30:00 +03:00
memcpy ( data - > data , rep - > data . data , data - > length ) ;
rep - > data . length - = data - > length ;
rep - > data . data + = data - > length ;
if ( rep - > data . length = = 0 ) {
/* we're done with this section of the call */
DLIST_REMOVE ( call - > replies , rep ) ;
}
2003-12-12 06:59:09 +03:00
2003-12-12 08:30:00 +03:00
if ( call - > replies = = NULL ) {
/* we're done with the whole call */
2003-12-12 06:59:09 +03:00
DLIST_REMOVE ( dce - > call_list , call ) ;
talloc_destroy ( call - > mem_ctx ) ;
}
return NT_STATUS_OK ;
2003-12-11 12:07:45 +03:00
}
2003-12-11 01:21:04 +03:00
/*
a useful function for implementing the query endpoint op
*/
BOOL dcesrv_table_query ( const struct dcerpc_interface_table * table ,
const struct dcesrv_endpoint * ep )
{
int i ;
const struct dcerpc_endpoint_list * endpoints = table - > endpoints ;
if ( ep - > type ! = ENDPOINT_SMB ) {
return False ;
}
for ( i = 0 ; i < endpoints - > count ; i + + ) {
if ( strcasecmp ( ep - > info . smb_pipe , endpoints - > names [ i ] ) = = 0 ) {
return True ;
}
}
return False ;
}
2003-12-13 05:20:40 +03:00
/*
a useful function for implementing the lookup_endpoints op
*/
int dcesrv_lookup_endpoints ( const struct dcerpc_interface_table * table ,
TALLOC_CTX * mem_ctx ,
struct dcesrv_ep_iface * * e )
{
2003-12-13 13:58:48 +03:00
int i ;
* e = talloc_array_p ( mem_ctx , struct dcesrv_ep_iface , table - > endpoints - > count ) ;
2003-12-13 05:20:40 +03:00
if ( ! * e ) {
return - 1 ;
}
2003-12-13 13:58:48 +03:00
for ( i = 0 ; i < table - > endpoints - > count ; i + + ) {
( * e ) [ i ] . name = table - > name ;
( * e ) [ i ] . uuid = table - > uuid ;
( * e ) [ i ] . if_version = table - > if_version ;
if ( strncmp ( table - > endpoints - > names [ i ] , " TCP- " , 4 ) = = 0 ) {
( * e ) [ i ] . endpoint . type = ENDPOINT_TCP ;
( * e ) [ i ] . endpoint . info . tcp_port = atoi ( table - > endpoints - > names [ i ] + 4 ) ;
} else {
( * e ) [ i ] . endpoint . type = ENDPOINT_SMB ;
( * e ) [ i ] . endpoint . info . smb_pipe = table - > endpoints - > names [ i ] ;
}
}
2003-12-13 05:20:40 +03:00
2003-12-13 13:58:48 +03:00
return i ;
2003-12-13 05:20:40 +03:00
}
2003-12-11 01:21:04 +03:00
/*
initialise the dcerpc server subsystem
*/
2003-12-13 13:58:48 +03:00
BOOL dcesrv_init ( struct dcesrv_context * dce )
2003-12-11 01:21:04 +03:00
{
2003-12-13 13:58:48 +03:00
rpc_echo_init ( dce ) ;
rpc_epmapper_init ( dce ) ;
2003-12-11 01:21:04 +03:00
return True ;
}