From 7e6cf43756b7643e2f0ee7ada5076f36f3a24bb7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 8 Jan 2004 22:55:27 +0000 Subject: This patch adds a better dcerpc server infastructure. 1.) We now register endpoint servers add startup via register_backend() and later use the smb.conf 'dcerpc endpoint servers' parameter to setup the dcesrv_context 2.) each endpoint server can register at context creation time as much interfaces as it wants (multiple interfaces on one endpoint are supported!) (NOTE: there's a difference between 'endpoint server' and 'endpoint'! for details look at rpc_server/dcesrv_server.h) 3.) one endpoint can have a security descriptor registered to it self this will be checked in the future when a client wants to connect to an smb pipe endpoint. 4.) we now have a 'remote' endpoint server, which works like the ntvfs_cifs module it takes this options in the [globals] section: dcerpc remote:interfaces = srvsvc, winreg, w32time, epmapper dcerpc remote:binding = ... dcerpc remote:user = ... dcerpc remote:password = ... 5.) we currently have tree endpoint servers: epmapper, rpcecho and remote the default for the 'dcerpc endpiont servers = epmapper, rpcecho' for testing you can also do dcerpc endpoint servers = rpcecho, remote, epmapper dcerpc remote:interfaces = srvsvc, samr, netlogon 6,) please notice the the epmapper now only returns NO_ENTRIES (but I think we'll find a solution for this too:-) 7.) also there're some other stuff left, but step by step :-) This patch also includes updates for the register_subsystem() , ntvfs_init(), and some other funtions to check for duplicate subsystem registration metze (hmmm, my first large commit...I hope it works as supposed :-) (This used to be commit 917e45dafd5be4c2cd90ff425b8d6f8403122349) --- source4/rpc_server/dcerpc_server.c | 529 ++++++++++++++++++++++++++----------- 1 file changed, 373 insertions(+), 156 deletions(-) (limited to 'source4/rpc_server/dcerpc_server.c') diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index d3e2f1917f..46341e6db1 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -4,6 +4,7 @@ server side dcerpc core code Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 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 @@ -23,15 +24,105 @@ #include "includes.h" /* - find the set of endpoint operations for an endpoint server + see if two endpoints match */ -static const struct dcesrv_endpoint_ops *find_endpoint(struct dcesrv_context *dce, - const struct dcesrv_endpoint *endpoint) +static BOOL endpoints_match(const struct dcesrv_ep_description *ep1, + const struct dcesrv_ep_description *ep2) { - struct dce_endpoint *ep; - for (ep=dce->endpoint_list; ep; ep=ep->next) { - if (ep->endpoint_ops->query_endpoint(endpoint)) { - return ep->endpoint_ops; + if (ep1->type != ep2->type) { + return False; + } + + switch (ep1->type) { + case ENDPOINT_SMB: + if (strcmp(ep1->info.smb_pipe,ep2->info.smb_pipe)==0) { + return True; + } + break; + case ENDPOINT_TCP: + if (ep1->info.tcp_port == ep2->info.tcp_port) { + return True; + } + break; + } + + return False; +} + +/* + find an endpoint in the dcesrv_context +*/ +static struct dcesrv_endpoint *find_endpoint(struct dcesrv_context *dce_ctx, + const struct dcesrv_ep_description *ep_description) +{ + struct dcesrv_endpoint *ep; + for (ep=dce_ctx->endpoint_list; ep; ep=ep->next) { + if (endpoints_match(&ep->ep_description, ep_description)) { + return ep; + } + } + return NULL; +} + +/* + see if a uuid and if_version match to an interface +*/ +static BOOL interface_match(const struct dcesrv_interface *if1, + const struct dcesrv_interface *if2) +{ + if (if1->ndr->if_version != if2->ndr->if_version) { + return False; + } + + if (strcmp(if1->ndr->uuid, if2->ndr->uuid)==0) { + return True; + } + + return False; +} + +/* + find the interface operations on an endpoint +*/ +static const struct dcesrv_interface *find_interface(const struct dcesrv_endpoint *endpoint, + 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); + } + } + return NULL; +} + +/* + see if a uuid and if_version match to an interface +*/ +static BOOL interface_match_by_uuid(const struct dcesrv_interface *iface, + const char *uuid, uint32 if_version) +{ + if (iface->ndr->if_version != if_version) { + return False; + } + + if (strcmp(iface->ndr->uuid, uuid)==0) { + return True; + } + + return False; +} + +/* + find the interface operations on an endpoint by uuid +*/ +static const struct dcesrv_interface *find_interface_by_uuid(const struct dcesrv_endpoint *endpoint, + const char *uuid, uint32 if_version) +{ + struct dcesrv_if_list *ifl; + for (ifl=endpoint->interface_list; ifl; ifl=ifl->next) { + if (interface_match_by_uuid(&(ifl->iface), uuid, if_version)) { + return &(ifl->iface); } } return NULL; @@ -40,10 +131,10 @@ static const struct dcesrv_endpoint_ops *find_endpoint(struct dcesrv_context *dc /* 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) +static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_connection *dce_conn, uint16 call_id) { struct dcesrv_call_state *c; - for (c=dce->call_list;c;c=c->next) { + for (c=dce_conn->call_list;c;c=c->next) { if (c->pkt.call_id == call_id) { return c; } @@ -52,121 +143,175 @@ static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_state *dce, uint } /* - register an endpoint server + register an interface on an endpoint */ -BOOL dcesrv_endpoint_register(struct dcesrv_context *dce, - const struct dcesrv_endpoint_ops *ops, - const struct dcerpc_interface_table *table) +NTSTATUS dcesrv_interface_register(struct dcesrv_context *dce_ctx, + const char *ep_name, + const struct dcesrv_interface *iface, + const struct security_descriptor *sd) { - BOOL done_smb=False; - BOOL done_tcp=False; - int i; + struct dcesrv_ep_description ep_description; + struct dcesrv_endpoint *ep; + struct dcesrv_if_list *ifl; + BOOL tcp; + BOOL add_ep = False; - for (i=0;iendpoints->count;i++) { - struct dce_endpoint *ep; - BOOL tcp; + tcp = (strncasecmp(ep_name, "TCP-", 4) == 0); - tcp = (strncasecmp(table->endpoints->names[i], "TCP-", 4) == 0); + if (tcp) { + ep_description.type = ENDPOINT_TCP; + ep_description.info.tcp_port = atoi(ep_name+4); + } else { + ep_description.type = ENDPOINT_SMB; + ep_description.info.smb_pipe = ep_name; + } + /* check if this endpoint exists + */ + if ((ep=find_endpoint(dce_ctx, &ep_description))==NULL) { + ep = talloc(dce_ctx->mem_ctx, sizeof(*ep)); + if (!ep) { + return NT_STATUS_NO_MEMORY; + } + ZERO_STRUCTP(ep); if (tcp) { - if (done_tcp) continue; - done_tcp = True; + ep->ep_description.type = ENDPOINT_TCP; + ep->ep_description.info.tcp_port = atoi(ep_name+4); } else { - if (done_smb) continue; - done_smb = True; + ep->ep_description.type = ENDPOINT_SMB; + ep->ep_description.info.smb_pipe = smb_xstrdup(ep_name); } + add_ep = True; + } - ep = malloc(sizeof(*ep)); - if (!ep) { - return False; + /* 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->ndr->name, ep_name)); + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + /* talloc a new interface list element */ + ifl = talloc(dce_ctx->mem_ctx, sizeof(*ifl)); + if (!ifl) { + return NT_STATUS_NO_MEMORY; + } + + /* copy the given interface struct to the one on the endpoints interface list */ + memcpy(&(ifl->iface),iface, sizeof(struct dcesrv_interface)); + + /* if we have a security descriptor given, + * we should see if we can set it up on the endpoint + */ + if (sd != NULL) { + /* if there's currently no security descriptor given on the endpoint + * we try to set it + */ + if (ep->sd == NULL) { + ep->sd = copy_security_descriptor(dce_ctx->mem_ctx, sd); } - if (tcp) { - ep->endpoint.type = ENDPOINT_TCP; - ep->endpoint.info.tcp_port = atoi(table->endpoints->names[i]+4); - } else { - ep->endpoint.type = ENDPOINT_SMB; - ep->endpoint.info.smb_pipe = table->endpoints->names[i]; + /* if now there's no security descriptor given on the endpoint + * something goes wrong, either we failed to copy the security descriptor + * or there was already one on the endpoint + */ + if (ep->sd != NULL) { + DEBUG(0,("dcesrv_interface_register: interface '%s' failed to setup a security descriptor\n" + " on endpoint '%s'\n", + iface->ndr->name, ep_name)); + if (add_ep) free(ep); + free(ifl); + return NT_STATUS_OBJECT_NAME_COLLISION; } + } - ep->endpoint_ops = ops; - DLIST_ADD(dce->endpoint_list, ep); + /* finally add the interface on the endpoint */ + DLIST_ADD(ep->interface_list, ifl); + + /* if it's a new endpoint add it to the dcesrv_context */ + if (add_ep) { + DLIST_ADD(dce_ctx->endpoint_list, ep); } - return True; + DEBUG(3,("dcesrv_interface_register: interface '%s' registered on endpoint '%s'\n", + iface->ndr->name, ep_name)); + + return NT_STATUS_OK; } /* connect to a dcerpc endpoint */ -NTSTATUS dcesrv_endpoint_connect_ops(struct dcesrv_context *dce, - const struct dcesrv_endpoint *endpoint, - const struct dcesrv_endpoint_ops *ops, - struct dcesrv_state **p) +NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx, + const struct dcesrv_endpoint *ep, + struct dcesrv_connection **p) { TALLOC_CTX *mem_ctx; - NTSTATUS status; mem_ctx = talloc_init("dcesrv_endpoint_connect"); if (!mem_ctx) { return NT_STATUS_NO_MEMORY; } - *p = talloc_p(mem_ctx, struct dcesrv_state); + *p = talloc_p(mem_ctx, struct dcesrv_connection); if (! *p) { talloc_destroy(mem_ctx); return NT_STATUS_NO_MEMORY; } - (*p)->dce = dce; + (*p)->dce_ctx = dce_ctx; (*p)->mem_ctx = mem_ctx; - (*p)->endpoint = *endpoint; - (*p)->ops = ops; + (*p)->endpoint = ep; + (*p)->iface = NULL; (*p)->private = NULL; (*p)->call_list = NULL; (*p)->cli_max_recv_frag = 0; - (*p)->ndr = NULL; - (*p)->dispatch = NULL; (*p)->handles = NULL; (*p)->partial_input = data_blob(NULL, 0); (*p)->auth_state.ntlmssp_state = NULL; (*p)->auth_state.auth_info = NULL; - /* 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; } /* - connect to a dcerpc endpoint + search and connect to a dcerpc endpoint */ -NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce, - const struct dcesrv_endpoint *endpoint, - struct dcesrv_state **p) +NTSTATUS dcesrv_endpoint_search_connect(struct dcesrv_context *dce_ctx, + const struct dcesrv_ep_description *ep_description, + struct dcesrv_connection **dce_conn_p) { - const struct dcesrv_endpoint_ops *ops; + NTSTATUS status; + const struct dcesrv_endpoint *ep; /* make sure this endpoint exists */ - ops = find_endpoint(dce, endpoint); - if (!ops) { + ep = find_endpoint(dce_ctx, ep_description); + if (!ep) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } - return dcesrv_endpoint_connect_ops(dce, endpoint, ops, p); + status = dcesrv_endpoint_connect(dce_ctx, ep, dce_conn_p); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* TODO: check security descriptor of the endpoint here + * if it's a smb named pipe + * if it's failed free dce_conn_p + */ + + return NT_STATUS_OK; } /* disconnect a link to an endpoint */ -void dcesrv_endpoint_disconnect(struct dcesrv_state *p) +void dcesrv_endpoint_disconnect(struct dcesrv_connection *p) { - p->ops->disconnect(p); + if (p->iface) { + p->iface->unbind(p, p->iface); + } /* destroy any handles */ while (p->handles) { @@ -315,15 +460,16 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) return dcesrv_bind_nak(call, 0); } - if (!call->dce->ops->set_interface(call->dce, uuid, if_version)) { + call->conn->iface = find_interface_by_uuid(call->conn->endpoint, uuid, if_version); + if (!call->conn->iface) { DEBUG(2,("Request for unknown dcerpc interface %s/%d\n", uuid, if_version)); /* we don't know about that interface */ result = DCERPC_BIND_PROVIDER_REJECT; - reason = DCERPC_BIND_REASON_ASYNTAX; + reason = DCERPC_BIND_REASON_ASYNTAX; } - if (call->dce->cli_max_recv_frag == 0) { - call->dce->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag; + if (call->conn->cli_max_recv_frag == 0) { + call->conn->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag; } /* handle any authentication that is being requested */ @@ -340,9 +486,9 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) 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; - if (call->dce->ndr) { + if (call->conn->iface && call->conn->iface->ndr) { pkt.u.bind_ack.secondary_address = talloc_asprintf(call->mem_ctx, "\\PIPE\\%s", - call->dce->ndr->name); + call->conn->iface->ndr->name); } else { pkt.u.bind_ack.secondary_address = ""; } @@ -361,13 +507,21 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) return dcesrv_bind_nak(call, 0); } + if (call->conn->iface) { + status = call->conn->iface->bind(call, call->conn->iface); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(2,("Request for dcerpc interface %s/%d rejected\n", uuid, if_version)); + return status; + } + } + rep = talloc_p(call->mem_ctx, struct dcesrv_call_reply); if (!rep) { return NT_STATUS_NO_MEMORY; } status = dcerpc_push_auth(&rep->data, call->mem_ctx, &pkt, - call->dce->auth_state.auth_info); + call->conn->auth_state.auth_info); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -375,7 +529,7 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) dcerpc_set_frag_length(&rep->data, rep->data.length); DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *); - DLIST_ADD_END(call->dce->call_list, call, struct dcesrv_call_state *); + DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *); return NT_STATUS_OK; } @@ -413,7 +567,7 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) opnum = call->pkt.u.request.opnum; - if (opnum >= call->dce->ndr->num_calls) { + if (opnum >= call->conn->iface->ndr->num_calls) { return dcesrv_fault(call, DCERPC_FAULT_OP_RNG_ERROR); } @@ -422,7 +576,7 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) return NT_STATUS_NO_MEMORY; } - r = talloc(call->mem_ctx, call->dce->ndr->calls[opnum].struct_size); + r = talloc(call->mem_ctx, call->conn->iface->ndr->calls[opnum].struct_size); if (!r) { return NT_STATUS_NO_MEMORY; } @@ -432,13 +586,13 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) } /* unravel the NDR for the packet */ - status = call->dce->ndr->calls[opnum].ndr_pull(pull, NDR_IN, r); + status = call->conn->iface->ndr->calls[opnum].ndr_pull(pull, NDR_IN, r); if (!NT_STATUS_IS_OK(status)) { return dcesrv_fault(call, DCERPC_FAULT_NDR); } /* call the dispatch function */ - status = call->dce->dispatch[opnum](call->dce, call->mem_ctx, r); + status = call->conn->iface->dispatch(call, call->mem_ctx, r); if (!NT_STATUS_IS_OK(status)) { return dcesrv_fault_nt(call, status); } @@ -453,7 +607,7 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) push->flags |= LIBNDR_FLAG_BIGENDIAN; } - status = call->dce->ndr->calls[opnum].ndr_push(push, NDR_OUT, r); + status = call->conn->iface->ndr->calls[opnum].ndr_push(push, NDR_OUT, r); if (!NT_STATUS_IS_OK(status)) { return dcesrv_fault(call, DCERPC_FAULT_NDR); } @@ -471,9 +625,9 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) } length = stub.length; - if (length + DCERPC_RESPONSE_LENGTH > call->dce->cli_max_recv_frag) { + if (length + DCERPC_RESPONSE_LENGTH > call->conn->cli_max_recv_frag) { /* the 32 is to cope with signing data */ - length = call->dce->cli_max_recv_frag - + length = call->conn->cli_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_RESPONSE_LENGTH); } @@ -507,7 +661,7 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) stub.length -= length; } while (stub.length != 0); - DLIST_ADD_END(call->dce->call_list, call, struct dcesrv_call_state *); + DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *); return NT_STATUS_OK; } @@ -530,18 +684,18 @@ static BOOL dce_full_packet(const DATA_BLOB *data) /* we might have consumed only part of our input - advance past that part */ -static void dce_partial_advance(struct dcesrv_state *dce, uint32 offset) +static void dce_partial_advance(struct dcesrv_connection *dce_conn, uint32 offset) { DATA_BLOB blob; - if (dce->partial_input.length == offset) { - free(dce->partial_input.data); - dce->partial_input = data_blob(NULL, 0); + if (dce_conn->partial_input.length == offset) { + free(dce_conn->partial_input.data); + dce_conn->partial_input = data_blob(NULL, 0); return; } - blob = dce->partial_input; - dce->partial_input = data_blob(blob.data + offset, + blob = dce_conn->partial_input; + dce_conn->partial_input = data_blob(blob.data + offset, blob.length - offset); free(blob.data); } @@ -549,7 +703,7 @@ static void dce_partial_advance(struct dcesrv_state *dce, uint32 offset) /* process some input to a dcerpc endpoint server. */ -NTSTATUS dcesrv_input_process(struct dcesrv_state *dce) +NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn) { struct ndr_pull *ndr; TALLOC_CTX *mem_ctx; @@ -563,20 +717,20 @@ NTSTATUS dcesrv_input_process(struct dcesrv_state *dce) } call = talloc_p(mem_ctx, struct dcesrv_call_state); if (!call) { - talloc_free(dce->mem_ctx, dce->partial_input.data); + talloc_free(dce_conn->mem_ctx, dce_conn->partial_input.data); talloc_destroy(mem_ctx); return NT_STATUS_NO_MEMORY; } call->mem_ctx = mem_ctx; - call->dce = dce; + call->conn = dce_conn; call->replies = NULL; - blob = dce->partial_input; + blob = dce_conn->partial_input; blob.length = dcerpc_get_frag_length(&blob); ndr = ndr_pull_init_blob(&blob, mem_ctx); if (!ndr) { - talloc_free(dce->mem_ctx, dce->partial_input.data); + talloc_free(dce_conn->mem_ctx, dce_conn->partial_input.data); talloc_destroy(mem_ctx); return NT_STATUS_NO_MEMORY; } @@ -587,12 +741,12 @@ NTSTATUS dcesrv_input_process(struct dcesrv_state *dce) status = ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt); if (!NT_STATUS_IS_OK(status)) { - talloc_free(dce->mem_ctx, dce->partial_input.data); + talloc_free(dce_conn->mem_ctx, dce_conn->partial_input.data); talloc_destroy(mem_ctx); return status; } - dce_partial_advance(dce, blob.length); + dce_partial_advance(dce_conn, blob.length); /* we have to check the signing here, before combining the pdus */ @@ -613,7 +767,7 @@ NTSTATUS dcesrv_input_process(struct dcesrv_state *dce) /* 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); + call = dcesrv_find_call(dce_conn, call2->pkt.call_id); if (!call) { return dcesrv_fault(call2, DCERPC_FAULT_OTHER); } @@ -648,7 +802,7 @@ NTSTATUS dcesrv_input_process(struct dcesrv_state *dce) /* 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 *); + DLIST_ADD_END(dce_conn->call_list, call, struct dcesrv_call_state *); return NT_STATUS_OK; } @@ -682,21 +836,21 @@ NTSTATUS dcesrv_input_process(struct dcesrv_state *dce) provide some input to a dcerpc endpoint server. This passes data from a dcerpc client into the server */ -NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data) +NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data) { NTSTATUS status; - dce->partial_input.data = Realloc(dce->partial_input.data, - dce->partial_input.length + data->length); - if (!dce->partial_input.data) { + dce_conn->partial_input.data = Realloc(dce_conn->partial_input.data, + dce_conn->partial_input.length + data->length); + if (!dce_conn->partial_input.data) { return NT_STATUS_NO_MEMORY; } - memcpy(dce->partial_input.data + dce->partial_input.length, + memcpy(dce_conn->partial_input.data + dce_conn->partial_input.length, data->data, data->length); - dce->partial_input.length += data->length; + dce_conn->partial_input.length += data->length; - while (dce_full_packet(&dce->partial_input)) { - status = dcesrv_input_process(dce); + while (dce_full_packet(&dce_conn->partial_input)) { + status = dcesrv_input_process(dce_conn); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -710,12 +864,12 @@ NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data) is wanted is in data->length and data->data is already allocated to hold that much data. */ -NTSTATUS dcesrv_output(struct dcesrv_state *dce, DATA_BLOB *data) +NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, DATA_BLOB *data) { struct dcesrv_call_state *call; struct dcesrv_call_reply *rep; - call = dce->call_list; + call = dce_conn->call_list; if (!call || !call->replies) { return NT_STATUS_FOOBAR; } @@ -736,88 +890,151 @@ NTSTATUS dcesrv_output(struct dcesrv_state *dce, DATA_BLOB *data) if (call->replies == NULL) { /* we're done with the whole call */ - DLIST_REMOVE(dce->call_list, call); + DLIST_REMOVE(dce_conn->call_list, call); talloc_destroy(call->mem_ctx); } return NT_STATUS_OK; } - /* - a useful function for implementing the query endpoint op - */ -BOOL dcesrv_table_query(const struct dcerpc_interface_table *table, - const struct dcesrv_endpoint *ep) + initialise the dcerpc server context +*/ +NTSTATUS dcesrv_init_context(struct dcesrv_context *dce_ctx) { int i; - const struct dcerpc_endpoint_list *endpoints = table->endpoints; + const char **endpoint_servers = lp_dcerpc_endpoint_servers(); - if (ep->type != ENDPOINT_SMB) { - return False; + dce_ctx->mem_ctx = talloc_init("struct dcesrv_context"); + if (!dce_ctx->mem_ctx) { + DEBUG(3,("dcesrv_init_context: talloc_init failed\n")); + return NT_STATUS_NO_MEMORY; } - for (i=0;icount;i++) { - if (strcasecmp(ep->info.smb_pipe, endpoints->names[i]) == 0) { - return True; + dce_ctx->endpoint_list = NULL; + + if (!endpoint_servers) { + DEBUG(3,("dcesrv_init_context: no endpoint servers configured\n")); + return NT_STATUS_OK; + } + + for (i=0;endpoint_servers[i];i++) { + NTSTATUS ret; + const struct dcesrv_endpoint_server *ep_server; + + ep_server = dcesrv_ep_server_byname(endpoint_servers[i]); + if (!ep_server) { + DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i])); + return NT_STATUS_UNSUCCESSFUL; + } + + ret = ep_server->init_server(dce_ctx, ep_server); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s'\n", endpoint_servers[i])); + return ret; } } - return False; + + return NT_STATUS_OK; } +/* the list of currently registered DCERPC endpoint servers. + */ +static struct { + struct dcesrv_endpoint_server *ep_server; +} *ep_servers = NULL; +static int num_ep_servers; /* - 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) + register a DCERPC endpoint server. + + 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 +*/ +static NTSTATUS decrpc_register_ep_server(void *_ep_server) { - int i; - *e = talloc_array_p(mem_ctx, struct dcesrv_ep_iface, table->endpoints->count); - if (! *e) { - return -1; - } - - for (i=0;iendpoints->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]; - } + const struct dcesrv_endpoint_server *ep_server = _ep_server; + + if (dcesrv_ep_server_byname(ep_server->name) != NULL) { + /* its already registered! */ + DEBUG(1,("DCERPC endpoint server '%s' already registered\n", + ep_server->name)); + return NT_STATUS_OBJECT_NAME_COLLISION; } - return i; -} + ep_servers = Realloc(ep_servers, sizeof(ep_servers[0]) * (num_ep_servers+1)); + if (!ep_servers) { + smb_panic("out of memory in decrpc_register"); + } + + ep_servers[num_ep_servers].ep_server = smb_xmemdup(ep_server, sizeof(*ep_server)); + ep_servers[num_ep_servers].ep_server->name = smb_xstrdup(ep_server->name); + + num_ep_servers++; + + DEBUG(1,("DCERPC module '%s' registered\n", + ep_server->name)); + return NT_STATUS_OK; +} -BOOL dcesrv_set_interface(struct dcesrv_state *dce, - const char *uuid, uint32 if_version, - const struct dcerpc_interface_table *table, - const dcesrv_dispatch_fn_t *dispatch_table) +/* + return the operations structure for a named backend of the specified type +*/ +const struct dcesrv_endpoint_server *dcesrv_ep_server_byname(const char *name) { - if (strcasecmp(table->uuid, uuid) != 0 || if_version != table->if_version) { - DEBUG(2,("Attempt to use unknown interface %s/%d\n", uuid, if_version)); - return False; + int i; + + for (i=0;iname, name) == 0) { + return ep_servers[i].ep_server; + } } - dce->ndr = table; - dce->dispatch = dispatch_table; - return True; + return NULL; } +/* + return the DCERPC module version, and the size of some critical types + This can be used by endpoint server modules to either detect compilation errors, or provide + multiple implementations for different smbd compilation options in one module +*/ +const struct dcesrv_critical_sizes *dcerpc_module_version(void) +{ + static const struct dcesrv_critical_sizes critical_sizes = { + DCERPC_MODULE_VERSION, + sizeof(struct dcesrv_context), + sizeof(struct dcesrv_endpoint), + sizeof(struct dcesrv_endpoint_server), + sizeof(struct dcesrv_ep_description), + sizeof(struct dcesrv_interface), + sizeof(struct dcesrv_if_list), + sizeof(struct dcesrv_connection), + sizeof(struct dcesrv_call_state), + sizeof(struct dcesrv_auth), + sizeof(struct dcesrv_handle) + }; + + return &critical_sizes; +} /* - initialise the dcerpc server subsystem + initialise the DCERPC subsystem */ -BOOL dcesrv_init(struct dcesrv_context *dce) +BOOL dcesrv_init(void) { - rpc_rpcecho_init(dce); - rpc_epmapper_init(dce); + NTSTATUS status; + + status = register_subsystem("dcerpc", decrpc_register_ep_server); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + /* FIXME: Perhaps panic if a basic endpoint server, such as EPMAPER, fails to initialise? */ + static_init_dcerpc; + + DEBUG(1,("DCERPC subsystem version %d initialised\n", DCERPC_MODULE_VERSION)); return True; } -- cgit