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
+ 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,
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 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "librpc/gen_ndr/ndr_dcerpc.h"
#include "auth/auth.h"
-#include "dlinklist.h"
+#include "auth/gensec/gensec.h"
+#include "lib/util/dlinklist.h"
#include "rpc_server/dcerpc_server.h"
#include "lib/events/events.h"
#include "smbd/service_task.h"
#include "system/filesys.h"
#include "libcli/security/security.h"
#include "build.h"
+#include "param/param.h"
+
+extern const struct dcesrv_interface dcesrv_mgmt_interface;
/*
see if two endpoints match
*/
-static BOOL endpoints_match(const struct dcerpc_binding *ep1,
+static bool endpoints_match(const struct dcerpc_binding *ep1,
const struct dcerpc_binding *ep2)
{
if (ep1->transport != ep2->transport) {
- return False;
+ return false;
}
if (!ep1->endpoint || !ep2->endpoint) {
}
if (strcasecmp(ep1->endpoint, ep2->endpoint) != 0)
- return False;
+ return false;
- return True;
+ return true;
}
/*
}
/*
- find a call that is pending in our call list
+ find the earlier parts of a fragmented call awaiting reassembily
*/
-static struct dcesrv_call_state *dcesrv_find_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
+static struct dcesrv_call_state *dcesrv_find_fragmented_call(struct dcesrv_connection *dce_conn, uint16_t call_id)
{
struct dcesrv_call_state *c;
- for (c=dce_conn->call_list;c;c=c->next) {
+ for (c=dce_conn->incoming_fragmented_call_list;c;c=c->next) {
if (c->pkt.call_id == call_id) {
return c;
}
ZERO_STRUCTP(ep);
ep->ep_description = talloc_reference(ep, binding);
add_ep = True;
+
+ /* add mgmt interface */
+ ifl = talloc(dce_ctx, struct dcesrv_if_list);
+ if (!ifl) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(&(ifl->iface), &dcesrv_mgmt_interface,
+ sizeof(struct dcesrv_interface));
+
+ DLIST_ADD(ep->interface_list, ifl);
}
/* see if the interface is already registered on te endpoint */
struct auth_session_info *session_info,
struct event_context *event_ctx,
struct messaging_context *msg_ctx,
- uint32_t server_id,
+ struct server_id server_id,
uint32_t state_flags,
struct dcesrv_connection **_p)
{
p->endpoint = ep;
p->contexts = NULL;
p->call_list = NULL;
+ p->incoming_fragmented_call_list = NULL;
p->pending_call_list = NULL;
p->cli_max_recv_frag = 0;
p->partial_input = data_blob(NULL, 0);
struct auth_session_info *session_info,
struct event_context *event_ctx,
struct messaging_context *msg_ctx,
- uint32_t server_id,
+ struct server_id server_id,
uint32_t state_flags,
struct dcesrv_connection **dce_conn_p)
{
{
pkt->rpc_vers = 5;
pkt->rpc_vers_minor = 0;
- if (lp_rpc_big_endian()) {
+ if (lp_rpc_big_endian(global_loadparm)) {
pkt->drep[0] = 0;
} else {
pkt->drep[0] = DCERPC_DREP_LE;
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
+ 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;
+ }
+}
+
/*
return a dcerpc fault
*/
dcerpc_set_frag_length(&rep->blob, rep->blob.length);
DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
- DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
return NT_STATUS_OK;
}
dcerpc_set_frag_length(&rep->blob, rep->blob.length);
DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
- DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
return NT_STATUS_OK;
}
uint32_t context_id;
const struct dcesrv_interface *iface;
+ if (call->pkt.u.bind.assoc_group_id != 0) {
+ return dcesrv_bind_nak(call, 0);
+ }
+
if (call->pkt.u.bind.num_contexts < 1 ||
call->pkt.u.bind.ctx_list[0].num_transfer_syntaxes < 1) {
return dcesrv_bind_nak(call, 0);
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;
+ /* we need to send a non zero assoc_group_id here to make longhorn happy, it also matches samba3 */
+ pkt.u.bind_ack.assoc_group_id = 0x12345678;
if (iface) {
/* FIXME: Use pipe name as specified by endpoint instead of interface name */
pkt.u.bind_ack.secondary_address = talloc_asprintf(call, "\\PIPE\\%s", iface->name);
pkt.u.bind_ack.ctx_list[0].syntax = ndr_transfer_syntax;
pkt.u.bind_ack.auth_info = data_blob(NULL, 0);
- if (!dcesrv_auth_bind_ack(call, &pkt)) {
+ status = dcesrv_auth_bind_ack(call, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
return dcesrv_bind_nak(call, 0);
}
dcerpc_set_frag_length(&rep->blob, rep->blob.length);
DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
- DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
return NT_STATUS_OK;
}
pkt.u.alter_resp.auth_info = data_blob(NULL, 0);
pkt.u.alter_resp.secondary_address = "";
- if (!dcesrv_auth_alter_ack(call, &pkt)) {
- return dcesrv_bind_nak(call, 0);
+ status = dcesrv_auth_alter_ack(call, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)
+ || NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)
+ || NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)
+ || NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ return dcesrv_fault(call, DCERPC_FAULT_ACCESS_DENIED);
+ }
+ return dcesrv_fault(call, 0);
}
rep = talloc(call, struct data_blob_list_item);
dcerpc_set_frag_length(&rep->blob, rep->blob.length);
DLIST_ADD_END(call->replies, rep, struct data_blob_list_item *);
- DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+ dcesrv_call_set_list(call, DCESRV_LIST_CALL_LIST);
return NT_STATUS_OK;
}
NTSTATUS status;
struct dcesrv_connection_context *context;
- call->fault_code = 0;
- call->state_flags = call->conn->state_flags;
- call->time = timeval_current();
-
/* if authenticated, and the mech we use can't do async replies, don't use them... */
if (call->conn->auth_state.gensec_security &&
!gensec_have_feature(call->conn->auth_state.gensec_security, GENSEC_FEATURE_ASYNC_REPLIES)) {
pull->flags |= LIBNDR_FLAG_REF_ALLOC;
call->context = context;
- call->event_ctx = context->conn->event_ctx;
call->ndr_pull = pull;
- if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_ORPC) {
+ if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
}
}
/* add the call to the pending list */
- DLIST_ADD_END(call->conn->pending_call_list, call, struct dcesrv_call_state *);
+ dcesrv_call_set_list(call, DCESRV_LIST_PENDING_CALL_LIST);
if (call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {
return NT_STATUS_OK;
pointers */
push->ptr_count = call->ndr_pull->ptr_count;
- if (lp_rpc_big_endian()) {
+ if (lp_rpc_big_endian(global_loadparm)) {
push->flags |= LIBNDR_FLAG_BIGENDIAN;
}
} while (stub.length != 0);
/* move the call from the pending to the finished calls list */
- DLIST_REMOVE(call->conn->pending_call_list, call);
- DLIST_ADD_END(call->conn->call_list, call, struct dcesrv_call_state *);
+ 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) {
data_blob_free(&blob);
}
+/*
+ remove the call from the right list when freed
+ */
+static int dcesrv_call_dequeue(struct dcesrv_call_state *call)
+{
+ dcesrv_call_set_list(call, DCESRV_LIST_NONE);
+ return 0;
+}
+
/*
process some input to a dcerpc endpoint server.
*/
struct dcesrv_call_state *call;
DATA_BLOB blob;
- call = talloc(dce_conn, struct dcesrv_call_state);
+ call = talloc_zero(dce_conn, struct dcesrv_call_state);
if (!call) {
talloc_free(dce_conn->partial_input.data);
return NT_STATUS_NO_MEMORY;
}
- call->conn = dce_conn;
- call->replies = NULL;
- call->context = NULL;
- call->event_ctx = dce_conn->event_ctx;
+ call->conn = dce_conn;
+ call->event_ctx = dce_conn->event_ctx;
+ call->msg_ctx = dce_conn->msg_ctx;
+ call->state_flags = call->conn->state_flags;
+ call->time = timeval_current();
+ call->list = DCESRV_LIST_NONE;
+
+ talloc_set_destructor(call, dcesrv_call_dequeue);
blob = dce_conn->partial_input;
blob.length = dcerpc_get_frag_length(&blob);
/* this is a continuation of an existing call - find the call then
tack it on the end */
- call = dcesrv_find_call(dce_conn, call2->pkt.call_id);
+ call = dcesrv_find_fragmented_call(dce_conn, call2->pkt.call_id);
if (!call) {
return dcesrv_fault(call2, DCERPC_FAULT_OTHER);
}
}
/* 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 */
+ just put it on the incoming_fragmented_call_list and wait for the rest */
if (call->pkt.ptype == DCERPC_PKT_REQUEST &&
!(call->pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
- DLIST_ADD_END(dce_conn->call_list, call, struct dcesrv_call_state *);
+ dcesrv_call_set_list(call, DCESRV_LIST_FRAGMENTED_CALL_LIST);
return NT_STATUS_OK;
- }
+ }
+
+ /* This removes any fragments we may have had stashed away */
+ dcesrv_call_set_list(call, DCESRV_LIST_NONE);
switch (call->pkt.ptype) {
case DCERPC_PKT_BIND:
if (call->replies == NULL) {
/* we're done with the whole call */
- DLIST_REMOVE(dce_conn->call_list, call);
+ dcesrv_call_set_list(call, DCESRV_LIST_NONE);
talloc_free(call);
}
return status;
}
-static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
+_PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_servers, struct dcesrv_context **_dce_ctx)
{
NTSTATUS status;
struct dcesrv_context *dce_ctx;
return NT_STATUS_OK;
}
-/*
- initialise the dcerpc server context for ncacn_np based services
-*/
-_PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
-{
- NTSTATUS status;
- struct dcesrv_context *dce_ctx;
-
- status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), &dce_ctx);
- NT_STATUS_NOT_OK_RETURN(status);
-
- *_dce_ctx = dce_ctx;
- return NT_STATUS_OK;
-}
-
/* the list of currently registered DCERPC endpoint servers.
*/
static struct ep_server {
}
/*
- open the dcerpc server sockets
+ initialise the dcerpc server context for ncacn_np based services
*/
-static void dcesrv_task_init(struct task_server *task)
+_PUBLIC_ NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx)
{
NTSTATUS status;
struct dcesrv_context *dce_ctx;
- struct dcesrv_endpoint *e;
-
- task_server_set_title(task, "task[dcesrv]");
-
- status = dcesrv_init_context(task->event_ctx,
- lp_dcerpc_endpoint_servers(),
- &dce_ctx);
- if (!NT_STATUS_IS_OK(status)) goto failed;
-
- /* Make sure the directory for NCALRPC exists */
- if (!directory_exist(lp_ncalrpc_dir())) {
- mkdir(lp_ncalrpc_dir(), 0755);
- }
- for (e=dce_ctx->endpoint_list;e;e=e->next) {
- switch (e->ep_description->transport) {
- case NCACN_UNIX_STREAM:
- status = dcesrv_add_ep_unix(dce_ctx, e, task->event_ctx, task->model_ops);
- if (!NT_STATUS_IS_OK(status)) goto failed;
- break;
-
- case NCALRPC:
- status = dcesrv_add_ep_ncalrpc(dce_ctx, e, task->event_ctx, task->model_ops);
- if (!NT_STATUS_IS_OK(status)) goto failed;
- break;
-
- case NCACN_IP_TCP:
- status = dcesrv_add_ep_tcp(dce_ctx, e, task->event_ctx, task->model_ops);
- if (!NT_STATUS_IS_OK(status)) goto failed;
- break;
-
- case NCACN_NP:
-/* FIXME: status = dcesrv_add_ep_np(dce_ctx, e, task->event_ctx, task->model_ops);
- if (!NT_STATUS_IS_OK(status)) goto failed;
-*/ break;
-
- default:
- status = NT_STATUS_NOT_SUPPORTED;
- if (!NT_STATUS_IS_OK(status)) goto failed;
- }
- }
-
- return;
-failed:
- task_server_terminate(task, "Failed to startup dcerpc server task");
-}
+ status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(global_loadparm), &dce_ctx);
+ NT_STATUS_NOT_OK_RETURN(status);
-/*
- called on startup of the smb server service It's job is to start
- listening on all configured sockets
-*/
-static NTSTATUS dcesrv_init(struct event_context *event_context,
- const struct model_ops *model_ops)
-{
- return task_server_startup(event_context, model_ops, dcesrv_task_init);
+ *_dce_ctx = dce_ctx;
+ return NT_STATUS_OK;
}
-NTSTATUS server_service_rpc_init(void)
-{
- init_module_fn static_init[] = STATIC_dcerpc_server_MODULES;
- init_module_fn *shared_init = load_samba_modules(NULL, "dcerpc_server");
-
- run_init_functions(static_init);
- run_init_functions(shared_init);
- talloc_free(shared_init);
-
- return register_server_service("rpc", dcesrv_init);
-}