From e6a58d370403e818bc2cfb8389751b78adcc14fd Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 9 Jul 2013 16:38:59 +0200 Subject: s4:rpc_server: make sure we don't terminate a connection with pending requests (bug #9820) Sadly we may have nested event loops, which won't work correctly with broken connections, that's why we have to do this... Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett Autobuild-User(master): Stefan Metzmacher Autobuild-Date(master): Wed Jul 10 08:47:38 CEST 2013 on sn-devel-104 --- source4/rpc_server/dcerpc_server.c | 55 ++++++++++++++++++++++++++++++++++++-- source4/rpc_server/dcerpc_server.h | 8 +++++- 2 files changed, 60 insertions(+), 3 deletions(-) (limited to 'source4') diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index 389cbe3666..10e711b37d 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -386,6 +386,8 @@ _PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx, return NT_STATUS_NO_MEMORY; } + p->prev = NULL; + p->next = NULL; p->dce_ctx = dce_ctx; p->endpoint = ep; p->contexts = NULL; @@ -402,7 +404,7 @@ _PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx, p->event_ctx = event_ctx; p->msg_ctx = msg_ctx; p->server_id = server_id; - p->processing = false; + p->terminate = NULL; p->state_flags = state_flags; ZERO_STRUCT(p->transport); @@ -1143,6 +1145,7 @@ _PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, dce_ctx->lp_ctx = lp_ctx; dce_ctx->assoc_groups_idr = idr_init(dce_ctx); NT_STATUS_HAVE_NO_MEMORY(dce_ctx->assoc_groups_idr); + dce_ctx->broken_connections = NULL; for (i=0;endpoint_servers[i];i++) { const struct dcesrv_endpoint_server *ep_server; @@ -1269,12 +1272,45 @@ const struct dcesrv_critical_sizes *dcerpc_module_version(void) static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason) { + struct dcesrv_context *dce_ctx = dce_conn->dce_ctx; struct stream_connection *srv_conn; srv_conn = talloc_get_type(dce_conn->transport.private_data, struct stream_connection); - stream_terminate_connection(srv_conn, reason); + if (dce_conn->pending_call_list == NULL) { + char *full_reason = talloc_asprintf(dce_conn, "dcesrv: %s", reason); + + DLIST_REMOVE(dce_ctx->broken_connections, dce_conn); + stream_terminate_connection(srv_conn, full_reason ? full_reason : reason); + return; + } + + if (dce_conn->terminate != NULL) { + return; + } + + DEBUG(3,("dcesrv: terminating connection due to '%s' defered 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"; + } + DLIST_ADD_END(dce_ctx->broken_connections, dce_conn, NULL); } + +static void dcesrv_cleanup_broken_connections(struct dcesrv_context *dce_ctx) +{ + struct dcesrv_connection *cur, *next; + + next = dce_ctx->broken_connections; + while (next != NULL) { + cur = next; + next = cur->next; + + dcesrv_terminate_connection(cur, cur->terminate); + } +} + /* We need this include to be able to compile on some plateforms * (ie. freebsd 7.2) as it seems that is not included * correctly. @@ -1386,6 +1422,8 @@ static void dcesrv_sock_accept(struct stream_connection *srv_conn) struct tevent_req *subreq; struct loadparm_context *lp_ctx = dcesrv_sock->dcesrv_ctx->lp_ctx; + dcesrv_cleanup_broken_connections(dcesrv_sock->dcesrv_ctx); + if (!srv_conn->session_info) { status = auth_anonymous_session_info(srv_conn, lp_ctx, @@ -1473,10 +1511,23 @@ static void dcesrv_read_fragment_done(struct tevent_req *subreq) { struct dcesrv_connection *dce_conn = tevent_req_callback_data(subreq, struct dcesrv_connection); + struct dcesrv_context *dce_ctx = dce_conn->dce_ctx; struct ncacn_packet *pkt; DATA_BLOB buffer; NTSTATUS status; + if (dce_conn->terminate) { + /* + * if the current connection is broken + * we need to clean it up before any other connection + */ + dcesrv_terminate_connection(dce_conn, dce_conn->terminate); + dcesrv_cleanup_broken_connections(dce_ctx); + return; + } + + dcesrv_cleanup_broken_connections(dce_ctx); + status = dcerpc_read_ncacn_packet_recv(subreq, dce_conn, &pkt, &buffer); TALLOC_FREE(subreq); diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h index 4fcb5c50a1..66fe51e17f 100644 --- a/source4/rpc_server/dcerpc_server.h +++ b/source4/rpc_server/dcerpc_server.h @@ -170,6 +170,9 @@ struct dcesrv_connection_context { /* the state associated with a dcerpc server connection */ struct dcesrv_connection { + /* for the broken_connections DLIST */ + struct dcesrv_connection *prev, *next; + /* the top level context for this server */ struct dcesrv_context *dce_ctx; @@ -208,7 +211,8 @@ struct dcesrv_connection { /* the transport level session key */ DATA_BLOB transport_session_key; - bool processing; + /* is this connection pending termination? If so, why? */ + const char *terminate; const char *packet_log_dir; @@ -288,6 +292,8 @@ struct dcesrv_context { struct loadparm_context *lp_ctx; struct idr_context *assoc_groups_idr; + + struct dcesrv_connection *broken_connections; }; /* this structure is used by modules to determine the size of some critical types */ -- cgit