#include "lib/util/samba_modules.h"
#include "librpc/gen_ndr/ndr_dcerpc.h"
-extern const struct dcesrv_interface dcesrv_mgmt_interface;
-
static NTSTATUS dcesrv_negotiate_contexts(struct dcesrv_call_state *call,
const struct dcerpc_bind *b,
struct dcerpc_ack_ctx *ack_ctx_list);
}
/*
- find the interface operations on an endpoint
+ find the interface operations on any endpoint with this binding
*/
-static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoint *endpoint,
- const struct dcesrv_interface *iface)
+static const struct dcesrv_interface *find_interface_by_binding(struct dcesrv_context *dce_ctx,
+ struct dcerpc_binding *binding,
+ const struct dcesrv_interface *iface)
{
- struct dcesrv_if_list *ifl;
- for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
- if (interface_match(&(ifl->iface), iface)) {
- return &(ifl->iface);
+ struct dcesrv_endpoint *ep;
+ for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) {
+ if (endpoints_match(ep->ep_description, binding)) {
+ struct dcesrv_if_list *ifl;
+ for (ifl=ep->interface_list; ifl; ifl=ifl->next) {
+ if (interface_match(&(ifl->iface), iface)) {
+ return &(ifl->iface);
+ }
+ }
}
}
return NULL;
/*
find the interface operations on an endpoint by uuid
*/
-static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
- const struct GUID *uuid, uint32_t if_version)
+const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint,
+ const struct GUID *uuid, uint32_t if_version)
{
struct dcesrv_if_list *ifl;
for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) {
/*
register an interface on an endpoint
+
+ An endpoint is one unix domain socket (for ncalrpc), one TCP port
+ (for ncacn_ip_tcp) or one (forwarded) named pipe (for ncacn_np).
+
+ Each endpoint can have many interfaces such as netlogon, lsa or
+ samr. Some have essentially the full set.
+
+ This is driven from the set of interfaces listed in each IDL file
+ via the PIDL generated *__op_init_server() functions.
*/
_PUBLIC_ NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx,
const char *ep_name,
struct dcerpc_binding *binding;
bool add_ep = false;
NTSTATUS status;
+ enum dcerpc_transport_t transport;
+ char *ep_string = NULL;
+ bool use_single_process = true;
+ /*
+ * If we are not using handles, there is no need for force
+ * this service into using a single process.
+ *
+ * However, due to the way we listen for RPC packets, we can
+ * only do this if we have a single service per pipe or TCP
+ * port, so we still force a single combined process for
+ * ncalrpc.
+ */
+ if (iface->flags & DCESRV_INTERFACE_FLAGS_HANDLES_NOT_USED) {
+ use_single_process = false;
+ }
+
status = dcerpc_parse_binding(dce_ctx, ep_name, &binding);
if (NT_STATUS_IS_ERR(status)) {
return status;
}
+ transport = dcerpc_binding_get_transport(binding);
+ if (transport == NCACN_IP_TCP) {
+ int port;
+ char port_str[6];
+
+ /*
+ * First check if there is already a port specified, eg
+ * for epmapper on ncacn_ip_tcp:[135]
+ */
+ const char *endpoint
+ = dcerpc_binding_get_string_option(binding,
+ "endpoint");
+ if (endpoint == NULL) {
+ port = lpcfg_parm_int(dce_ctx->lp_ctx, NULL,
+ "rpc server port", iface->name, 0);
+
+ /*
+ * For RPC services that are not set to use a single
+ * process, we do not default to using the 'rpc server
+ * port' because that would cause a double-bind on
+ * that port.
+ */
+ if (port == 0 && !use_single_process) {
+ port = lpcfg_rpc_server_port(dce_ctx->lp_ctx);
+ }
+ if (port != 0) {
+ snprintf(port_str, sizeof(port_str), "%u", port);
+ status = dcerpc_binding_set_string_option(binding,
+ "endpoint",
+ port_str);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ }
+ }
+
+ /* see if the interface is already registered on the endpoint */
+ if (find_interface_by_binding(dce_ctx, binding, iface)!=NULL) {
+ DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
+ iface->name, ep_name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
/* check if this endpoint exists
*/
- if ((ep=find_endpoint(dce_ctx, binding))==NULL) {
+ ep = find_endpoint(dce_ctx, binding);
+
+ if (ep != NULL) {
+ /*
+ * We want a new port on ncacn_ip_tcp for NETLOGON, so
+ * it can be multi-process. Other processes can also
+ * listen on distinct ports, if they have one forced
+ * in the code above with eg 'rpc server port:drsuapi = 1027'
+ *
+ * If we have mulitiple endpoints on port 0, they each
+ * get an epemeral port (currently by walking up from
+ * 1024).
+ */
+ if (!use_single_process && transport == NCACN_IP_TCP) {
+ add_ep = true;
+ }
+ }
+
+ if (ep == NULL || add_ep) {
ep = talloc_zero(dce_ctx, struct dcesrv_endpoint);
if (!ep) {
return NT_STATUS_NO_MEMORY;
return NT_STATUS_NO_MEMORY;
}
- memcpy(&(ifl->iface), &dcesrv_mgmt_interface,
- sizeof(struct dcesrv_interface));
+ ifl->iface = dcesrv_get_mgmt_interface();
DLIST_ADD(ep->interface_list, ifl);
}
- /* see if the interface is already registered on te endpoint */
- if (find_interface(ep, iface)!=NULL) {
- DEBUG(0,("dcesrv_interface_register: interface '%s' already registered on endpoint '%s'\n",
- iface->name, ep_name));
- return NT_STATUS_OBJECT_NAME_COLLISION;
+ /*
+ * By default don't force into a single process, but if any
+ * interface on this endpoint on this service uses handles
+ * (most do), then we must force into single process mode
+ *
+ * By overwriting this each time a new interface is added to
+ * this endpoint, we end up with the most restrictive setting.
+ */
+ if (use_single_process) {
+ ep->use_single_process = true;
}
/* talloc a new interface list element */
DLIST_ADD(dce_ctx->endpoint_list, ep);
}
+ /* Re-get the string as we may have set a port */
+ ep_string = dcerpc_binding_string(dce_ctx, ep->ep_description);
+
DEBUG(4,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n",
- iface->name, ep_name));
+ iface->name, ep_string));
+ TALLOC_FREE(ep_string);
return NT_STATUS_OK;
}
call->conn->assoc_group = dcesrv_assoc_group_new(call->conn,
call->conn->dce_ctx);
}
+
+ /*
+ * The NETLOGON server does not use handles and so
+ * there is no need to support association groups, but
+ * we need to give back a number regardless.
+ *
+ * We have to do this when it is not run as a single process,
+ * because then it can't see the other valid association
+ * groups. We handle this genericly for all endpoints not
+ * running in single process mode.
+ *
+ * We know which endpoint we are on even before checking the
+ * iface UUID, so for simplicity we enforce the same policy
+ * for all interfaces on the endpoint.
+ *
+ * This means that where NETLOGON
+ * shares an endpoint (such as ncalrpc or of 'lsa over
+ * netlogon' is set) we will still check association groups.
+ *
+ */
+
+ if (call->conn->assoc_group == NULL &&
+ !call->conn->endpoint->use_single_process) {
+ call->conn->assoc_group
+ = dcesrv_assoc_group_new(call->conn,
+ call->conn->dce_ctx);
+ }
if (call->conn->assoc_group == NULL) {
return dcesrv_bind_nak(call, 0);
}
/* not supported yet */
}
if (features & DCERPC_BIND_TIME_KEEP_CONNECTION_ON_ORPHAN) {
- /* not supported yet */
+ a->reason.negotiate |=
+ DCERPC_BIND_TIME_KEEP_CONNECTION_ON_ORPHAN;
}
call->conn->bind_time_features = a->reason.negotiate;
/*
* Try to negotiate one new presentation context.
+ *
+ * Deep in here we locate the iface (by uuid) that the client
+ * requested, from the list of interfaces on the
+ * call->conn->endpoint, and call iface->bind() on that iface.
+ *
+ * call->conn was set up at the accept() of the socket, and
+ * call->conn->endpoint has a list of interfaces restricted to
+ * this port or pipe.
*/
status = dcesrv_negotiate_contexts(call, &call->pkt.u.bind, ack_ctx_list);
if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTOCOL_ERROR)) {
return status;
}
+ /*
+ * At this point we still don't know which interface (eg
+ * netlogon, lsa, drsuapi) the caller requested in this bind!
+ * The most recently added context is available as the first
+ * element in the linked list at call->conn->contexts, that is
+ * call->conn->contexts->iface, but they may not have
+ * requested one at all!
+ */
+
if ((call->pkt.pfc_flags & DCERPC_PFC_FLAG_CONC_MPX) &&
(call->state_flags & DCESRV_CALL_STATE_FLAG_MULTIPLEXED)) {
call->conn->state_flags |= DCESRV_CALL_STATE_FLAG_MULTIPLEXED;
call->conn->state_flags |= DCESRV_CALL_STATE_FLAG_PROCESS_PENDING_CALL;
}
- /* handle any authentication that is being requested */
+ /*
+ * After finding the interface and setting up the NDR
+ * transport negotiation etc, handle any authentication that
+ * is being requested.
+ */
if (!dcesrv_auth_bind(call)) {
struct dcesrv_auth *auth = &call->conn->auth_state;
dcesrv_prepare_context_auth(call);
+ /*
+ * Multiplex is supported by default
+ */
+ call->state_flags |= DCESRV_CALL_STATE_FLAG_MULTIPLEXED;
+
status = iface->bind(call, iface, if_version);
call->context = NULL;
if (!NT_STATUS_IS_OK(status)) {
dcerpc_binding_get_transport(endpoint->ep_description);
struct ndr_pull *pull;
NTSTATUS status;
- struct dcesrv_connection_context *context;
if (!call->conn->allow_request) {
return dcesrv_fault_disconnect(call, DCERPC_NCA_S_PROTO_ERROR);
call->state_flags &= ~DCESRV_CALL_STATE_FLAG_MAY_ASYNC;
}
- context = dcesrv_find_context(call->conn, call->pkt.u.request.context_id);
- if (context == NULL) {
+ if (call->context == NULL) {
return dcesrv_fault_with_flags(call, DCERPC_NCA_S_UNKNOWN_IF,
DCERPC_PFC_FLAG_DID_NOT_EXECUTE);
}
case DCERPC_AUTH_LEVEL_PRIVACY:
break;
default:
- if (!context->allow_connect) {
+ if (!call->context->allow_connect) {
char *addr;
addr = tsocket_address_string(call->conn->remote_address,
DEBUG(2, ("%s: restrict auth_level_connect access "
"to [%s] with auth[type=0x%x,level=0x%x] "
"on [%s] from [%s]\n",
- __func__, context->iface->name,
+ __func__, call->context->iface->name,
call->conn->auth_state.auth_type,
call->conn->auth_state.auth_level,
derpc_transport_string_by_transport(transport),
break;
}
- if (call->conn->auth_state.auth_level < context->min_auth_level) {
+ if (call->conn->auth_state.auth_level < call->context->min_auth_level) {
char *addr;
addr = tsocket_address_string(call->conn->remote_address, call);
"to [%s] with auth[type=0x%x,level=0x%x] "
"on [%s] from [%s]\n",
__func__,
- context->min_auth_level,
- context->iface->name,
+ call->context->min_auth_level,
+ call->context->iface->name,
call->conn->auth_state.auth_type,
call->conn->auth_state.auth_level,
derpc_transport_string_by_transport(transport),
pull->flags |= LIBNDR_FLAG_REF_ALLOC;
- call->context = context;
call->ndr_pull = pull;
if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) {
}
/* unravel the NDR for the packet */
- status = context->iface->ndr_pull(call, call, pull, &call->r);
+ status = call->context->iface->ndr_pull(call, call, pull, &call->r);
if (!NT_STATUS_IS_OK(status)) {
uint8_t extra_flags = 0;
if (call->fault_code == DCERPC_FAULT_OP_RNG_ERROR) {
/* we got an unknown call */
DEBUG(3,(__location__ ": Unknown RPC call %u on %s\n",
- call->pkt.u.request.opnum, context->iface->name));
+ call->pkt.u.request.opnum,
+ call->context->iface->name));
dcesrv_save_call(call, "unknown");
extra_flags |= DCERPC_PFC_FLAG_DID_NOT_EXECUTE;
} else {
}
/* call the dispatch function */
- status = context->iface->dispatch(call, call, call->r);
+ status = call->context->iface->dispatch(call, call, call->r);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(5,("dcerpc fault in call %s:%02x - %s\n",
- context->iface->name,
+ call->context->iface->name,
call->pkt.u.request.opnum,
dcerpc_errstr(pull, call->fault_code)));
return dcesrv_fault(call, call->fault_code);
}
if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST) {
+ if (dce_conn->pending_call_list != NULL) {
+ /*
+ * concurrent requests are only allowed
+ * if DCERPC_PFC_FLAG_CONC_MPX was negotiated.
+ */
+ if (!(dce_conn->state_flags & DCESRV_CALL_STATE_FLAG_MULTIPLEXED)) {
+ dcesrv_call_disconnect_after(call,
+ "dcesrv_auth_request - "
+ "existing pending call without CONN_MPX");
+ return dcesrv_fault(call,
+ DCERPC_NCA_S_PROTO_ERROR);
+ }
+ }
/* only one request is possible in the fragmented list */
if (dce_conn->incoming_fragmented_call_list != NULL) {
- TALLOC_FREE(call);
- call = dce_conn->incoming_fragmented_call_list;
+ if (!(dce_conn->state_flags & DCESRV_CALL_STATE_FLAG_MULTIPLEXED)) {
+ /*
+ * Without DCERPC_PFC_FLAG_CONC_MPX
+ * we need to return the FAULT on the
+ * already existing call.
+ *
+ * This is important to get the
+ * call_id and context_id right.
+ */
+ TALLOC_FREE(call);
+ call = dce_conn->incoming_fragmented_call_list;
+ }
dcesrv_call_disconnect_after(call,
"dcesrv_auth_request - "
"existing fragmented call");
return dcesrv_fault_disconnect(call,
DCERPC_FAULT_NO_CALL_ACTIVE);
}
+ call->context = dcesrv_find_context(call->conn,
+ call->pkt.u.request.context_id);
+ if (call->context == NULL) {
+ return dcesrv_fault_with_flags(call, DCERPC_NCA_S_UNKNOWN_IF,
+ DCERPC_PFC_FLAG_DID_NOT_EXECUTE);
+ }
} else {
const struct dcerpc_request *nr = &call->pkt.u.request;
const struct dcerpc_request *er = NULL;
The 'name' can be later used by other backends to find the operations
structure for this backend.
- The 'type' is used to specify whether this is for a disk, printer or IPC$ share
*/
-_PUBLIC_ NTSTATUS dcerpc_register_ep_server(const void *_ep_server)
+_PUBLIC_ NTSTATUS dcerpc_register_ep_server(const struct dcesrv_endpoint_server *ep_server)
{
- const struct dcesrv_endpoint_server *ep_server = _ep_server;
if (dcesrv_ep_server_byname(ep_server->name) != NULL) {
/* its already registered! */
void dcerpc_server_init(struct loadparm_context *lp_ctx)
{
static bool initialized;
-#define _MODULE_PROTO(init) extern NTSTATUS init(void);
+#define _MODULE_PROTO(init) extern NTSTATUS init(TALLOC_CTX *);
STATIC_dcerpc_server_MODULES_PROTO;
init_module_fn static_init[] = { STATIC_dcerpc_server_MODULES };
init_module_fn *shared_init;
shared_init = load_samba_modules(NULL, "dcerpc_server");
- run_init_functions(static_init);
- run_init_functions(shared_init);
+ run_init_functions(NULL, static_init);
+ run_init_functions(NULL, shared_init);
talloc_free(shared_init);
}
return;
}
- DEBUG(3,("dcesrv: terminating connection due to '%s' defered due to pending calls\n",
+ DEBUG(3,("dcesrv: terminating connection due to '%s' deferred due to pending calls\n",
reason));
dce_conn->terminate = talloc_strdup(dce_conn, reason);
if (dce_conn->terminate == NULL) {
- dce_conn->terminate = "dcesrv: defered terminating connection - no memory";
+ dce_conn->terminate = "dcesrv: deferred terminating connection - no memory";
}
DLIST_ADD_END(dce_ctx->broken_connections, dce_conn);
}
}
}
+ /*
+ * This fills in dcesrv_conn->endpoint with the endpoint
+ * associated with the socket. From this point on we know
+ * which (group of) services we are handling, but not the
+ * specific interface.
+ */
+
status = dcesrv_endpoint_connect(dcesrv_sock->dcesrv_ctx,
srv_conn,
dcesrv_sock->endpoint,
lpcfg_socket_options(dce_ctx->lp_ctx),
dcesrv_sock);
if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0,("service_setup_stream_socket(address=%s,port=%u) failed - %s\n",
- address, port, nt_errstr(status)));
+ struct dcesrv_if_list *iface;
+ DEBUG(0,("service_setup_stream_socket(address=%s,port=%u) for ",
+ address, port));
+ for (iface = e->interface_list; iface; iface = iface->next) {
+ DEBUGADD(0, ("%s ", iface->iface.name));
+ }
+ DEBUGADD(0, ("failed - %s",
+ nt_errstr(status)));
return status;
}
DEBUG(0,("dcerpc_binding_set_string_option(endpoint, %s) failed - %s\n",
port_str, nt_errstr(status)));
return status;
+ } else {
+ struct dcesrv_if_list *iface;
+ DEBUG(4,("Successfully listening on ncacn_ip_tcp endpoint [%s]:[%s] for ",
+ address, port_str));
+ for (iface = e->interface_list; iface; iface = iface->next) {
+ DEBUGADD(4, ("%s ", iface->iface.name));
+ }
+ DEBUGADD(4, ("\n"));
}
return NT_STATUS_OK;