From 0563c5bacf96d1b6759acd78377c417715f30fff Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sat, 30 Oct 2010 11:33:53 +1100 Subject: s4-rpc: split the dcesrv reply code out of dcerpc_server this allows us to remove a dependency on the dcerpc_server from code that uses rpc forwarding Pair-Programmed-With: Andrew Bartlett --- source4/ntptr/simple_ldb/ntptr_simple_ldb.c | 2 + source4/ntvfs/ipc/rap_server.c | 1 + source4/rpc_server/common/forward.c | 1 + source4/rpc_server/common/reply.c | 254 ++++++++++++++++++++++++++++ source4/rpc_server/dcerpc_server.c | 172 +------------------ source4/rpc_server/dcesrv_auth.c | 1 + source4/rpc_server/wscript_build | 8 +- 7 files changed, 264 insertions(+), 175 deletions(-) create mode 100644 source4/rpc_server/common/reply.c diff --git a/source4/ntptr/simple_ldb/ntptr_simple_ldb.c b/source4/ntptr/simple_ldb/ntptr_simple_ldb.c index 2790f8359d..641f5d3959 100644 --- a/source4/ntptr/simple_ldb/ntptr_simple_ldb.c +++ b/source4/ntptr/simple_ldb/ntptr_simple_ldb.c @@ -35,6 +35,8 @@ #include "dsdb/samdb/samdb.h" #include "ldb_wrap.h" #include "../lib/util/util_ldb.h" +#include "librpc/gen_ndr/dcerpc.h" +#include "rpc_server/dcerpc_server.h" #include "rpc_server/common/common.h" #include "param/param.h" diff --git a/source4/ntvfs/ipc/rap_server.c b/source4/ntvfs/ipc/rap_server.c index dbd1fccfea..8f36c09ebc 100644 --- a/source4/ntvfs/ipc/rap_server.c +++ b/source4/ntvfs/ipc/rap_server.c @@ -23,6 +23,7 @@ #include "../librpc/gen_ndr/rap.h" #include "libcli/raw/interfaces.h" #include "librpc/gen_ndr/srvsvc.h" +#include "librpc/gen_ndr/dcerpc.h" #include "rpc_server/common/common.h" #include "rpc_server/common/share.h" #include "param/param.h" diff --git a/source4/rpc_server/common/forward.c b/source4/rpc_server/common/forward.c index 113ac5f6c4..45ce6dcac9 100644 --- a/source4/rpc_server/common/forward.c +++ b/source4/rpc_server/common/forward.c @@ -22,6 +22,7 @@ #include "includes.h" #include #include "rpc_server/dcerpc_server.h" +#include "librpc/gen_ndr/dcerpc.h" #include "rpc_server/common/common.h" #include "messaging/irpc.h" #include "auth/auth.h" diff --git a/source4/rpc_server/common/reply.c b/source4/rpc_server/common/reply.c new file mode 100644 index 0000000000..d883317d6e --- /dev/null +++ b/source4/rpc_server/common/reply.c @@ -0,0 +1,254 @@ +/* + 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 . +*/ + +#include "includes.h" +#include "auth/auth.h" +#include "auth/gensec/gensec.h" +#include "../lib/util/dlinklist.h" +#include "rpc_server/dcerpc_server.h" +#include "rpc_server/dcerpc_server_proto.h" +#include "rpc_server/common/proto.h" +#include "librpc/rpc/dcerpc_proto.h" +#include "system/filesys.h" +#include "libcli/security/security.h" +#include "param/param.h" +#include "../lib/tsocket/tsocket.h" +#include "../libcli/named_pipe_auth/npa_tstream.h" +#include "smbd/service_stream.h" +#include "../lib/tsocket/tsocket.h" +#include "lib/socket/socket.h" +#include "smbd/process_model.h" +#include "lib/messaging/irpc.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: + DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *); + break; + case DCESRV_LIST_FRAGMENTED_CALL_LIST: + DLIST_ADD_END(call->conn->incoming_fragmented_call_list, call, struct dcesrv_call_state *); + break; + case DCESRV_LIST_PENDING_CALL_LIST: + DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *); + 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 +*/ +NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code) +{ + struct ncacn_packet pkt; + struct data_blob_list_item *rep; + uint8_t zeros[4]; + NTSTATUS status; + + /* setup a bind_ack */ + 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; + 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; + + ZERO_STRUCT(zeros); + pkt.u.fault._pad = data_blob_const(zeros, sizeof(zeros)); + + rep = talloc(call, struct data_blob_list_item); + if (!rep) { + return NT_STATUS_NO_MEMORY; + } + + status = ncacn_push_auth(&rep->blob, call, &pkt, NULL); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + dcerpc_set_frag_length(&rep->blob, rep->blob.length); + + DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *); + 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; +} + + + +_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; + 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); + + total_length = stub.length; + + /* we can write a full max_recv_frag size, minus the dcerpc + request header size */ + chunk_size = call->conn->cli_max_recv_frag; + chunk_size -= DCERPC_REQUEST_LENGTH; + if (call->conn->auth_state.auth_info && + call->conn->auth_state.gensec_security) { + sig_size = gensec_sig_size(call->conn->auth_state.gensec_security, + call->conn->cli_max_recv_frag); + if (sig_size) { + chunk_size -= DCERPC_AUTH_TRAILER_LENGTH; + chunk_size -= sig_size; + } + } + chunk_size -= (chunk_size % 16); + + do { + uint32_t length; + struct data_blob_list_item *rep; + struct ncacn_packet pkt; + + rep = talloc(call, struct data_blob_list_item); + NT_STATUS_HAVE_NO_MEMORY(rep); + + length = MIN(chunk_size, stub.length); + + /* form the dcerpc response packet */ + 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_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; + 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; + + if (!dcesrv_auth_response(call, &rep->blob, sig_size, &pkt)) { + return dcesrv_fault(call, DCERPC_FAULT_OTHER); + } + + dcerpc_set_frag_length(&rep->blob, rep->blob.length); + + DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *); + + 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; +} + +NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *c, + DATA_BLOB *session_key) +{ + return dcerpc_generic_session_key(NULL, session_key); +} diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index dc3b9bcfd6..1741b59929 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -26,6 +26,7 @@ #include "../lib/util/dlinklist.h" #include "rpc_server/dcerpc_server.h" #include "rpc_server/dcerpc_server_proto.h" +#include "rpc_server/common/proto.h" #include "librpc/rpc/dcerpc_proto.h" #include "system/filesys.h" #include "libcli/security/security.h" @@ -338,12 +339,6 @@ NTSTATUS dcesrv_inherited_session_key(struct dcesrv_connection *p, return NT_STATUS_NO_USER_SESSION_KEY; } -NTSTATUS dcesrv_generic_session_key(struct dcesrv_connection *c, - DATA_BLOB *session_key) -{ - return dcerpc_generic_session_key(NULL, session_key); -} - /* fetch the user session key - may be default (above) or the SMB session key @@ -413,20 +408,6 @@ _PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx, return NT_STATUS_OK; } -static 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; -} - /* 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 @@ -464,54 +445,6 @@ static void dcesrv_call_set_list(struct dcesrv_call_state *call, } } -/* - return a dcerpc fault -*/ -static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32_t fault_code) -{ - struct ncacn_packet pkt; - struct data_blob_list_item *rep; - uint8_t zeros[4]; - NTSTATUS status; - - /* setup a bind_ack */ - 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; - 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; - - ZERO_STRUCT(zeros); - pkt.u.fault._pad = data_blob_const(zeros, sizeof(zeros)); - - rep = talloc(call, struct data_blob_list_item); - if (!rep) { - return NT_STATUS_NO_MEMORY; - } - - status = ncacn_push_auth(&rep->blob, call, &pkt, NULL); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - dcerpc_set_frag_length(&rep->blob, rep->blob.length); - - DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *); - 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; -} - /* return a dcerpc bind_nak @@ -1046,109 +979,6 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) return dcesrv_reply(call); } -_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; - 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); - - total_length = stub.length; - - /* we can write a full max_recv_frag size, minus the dcerpc - request header size */ - chunk_size = call->conn->cli_max_recv_frag; - chunk_size -= DCERPC_REQUEST_LENGTH; - if (call->conn->auth_state.auth_info && - call->conn->auth_state.gensec_security) { - sig_size = gensec_sig_size(call->conn->auth_state.gensec_security, - call->conn->cli_max_recv_frag); - if (sig_size) { - chunk_size -= DCERPC_AUTH_TRAILER_LENGTH; - chunk_size -= sig_size; - } - } - chunk_size -= (chunk_size % 16); - - do { - uint32_t length; - struct data_blob_list_item *rep; - struct ncacn_packet pkt; - - rep = talloc(call, struct data_blob_list_item); - NT_STATUS_HAVE_NO_MEMORY(rep); - - length = MIN(chunk_size, stub.length); - - /* form the dcerpc response packet */ - 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_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; - 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; - - if (!dcesrv_auth_response(call, &rep->blob, sig_size, &pkt)) { - return dcesrv_fault(call, DCERPC_FAULT_OTHER); - } - - dcerpc_set_frag_length(&rep->blob, rep->blob.length); - - DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *); - - 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; -} /* remove the call from the right list when freed diff --git a/source4/rpc_server/dcesrv_auth.c b/source4/rpc_server/dcesrv_auth.c index 3140598fec..c7f3cdecc1 100644 --- a/source4/rpc_server/dcesrv_auth.c +++ b/source4/rpc_server/dcesrv_auth.c @@ -23,6 +23,7 @@ #include "includes.h" #include "rpc_server/dcerpc_server.h" #include "rpc_server/dcerpc_server_proto.h" +#include "rpc_server/common/proto.h" #include "librpc/rpc/dcerpc_proto.h" #include "librpc/gen_ndr/ndr_dcerpc.h" #include "auth/credentials/credentials.h" diff --git a/source4/rpc_server/wscript_build b/source4/rpc_server/wscript_build index 81a6a9f8c2..742982edfb 100644 --- a/source4/rpc_server/wscript_build +++ b/source4/rpc_server/wscript_build @@ -7,7 +7,7 @@ bld.SAMBA_SUBSYSTEM('DCERPC_SHARE', ) bld.SAMBA_SUBSYSTEM('DCERPC_COMMON', - source='common/forward.c', + source='common/forward.c common/reply.c dcesrv_auth.c', autoproto='common/proto.h', public_headers='common/common.h', header_path='dcerpc_server', @@ -131,7 +131,7 @@ bld.SAMBA_MODULE('dcerpc_eventlog', ) bld.SAMBA_LIBRARY('dcerpc_server', - source='dcerpc_server.c dcesrv_auth.c dcesrv_mgmt.c handles.c', + source='dcerpc_server.c dcesrv_mgmt.c handles.c', pc_files='dcerpc_server.pc', deps='LIBCLI_AUTH ndr samba_server_gensec dcerpc_remote service', public_deps='dcerpc', @@ -140,12 +140,12 @@ bld.SAMBA_LIBRARY('dcerpc_server', vnum='0.0.1' ) -bld.SAMBA_MODULE('DCESRV', +bld.SAMBA_MODULE('dcesrv', source='service_rpc.c', autoproto='service_rpc.h', subsystem='service', init_function='server_service_rpc_init', - internal_module=True, + internal_module=False, deps='dcerpc_server' ) -- cgit