]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
s3:rpc_server: Switch to core dcerpc server loop
authorSamuel Cabrero <scabrero@samba.org>
Thu, 5 Dec 2019 10:45:54 +0000 (11:45 +0100)
committerSamuel Cabrero <scabrero@sn-devel-184>
Fri, 20 Mar 2020 15:36:35 +0000 (15:36 +0000)
This commit finally switches the RPC server implementation.

At the same we have to do other related changes to keep code compiling
and test environments running.

First avoid moving the session_info into the allocated pipes_struct memory
context as it is owned now by the core RPC server, and the s3compat pidl
compiler will update the pipes_struct session_info before dispatching
the call with dcesrv_call->auth_state->session_info.

Also, fix a segfault in the endpoint mapper daemon when it tries to delete
the endpoints previously registered over a NCALRPC connection.

If we have:

rpc_server : epmapper = external
rpc_server : lsarpc = external
rpc_daemon : epmd = fork
rpc_daemon : lsasd = fork

The sequence is:

* The endpoint mapper starts (start_epmd in source3/smbd/server.c)
* The lsarpc daemon starts (start_lsasd in source3/smbd/server.c)
  * The lsarpc daemon creates the sockets and registers its endpoints
    (rpc_ep_register in source3/rpc_server/lsasd.c)
  * The endpoint registration code opens a NCALRPC connection to the
    endpoint mapper daemon (ep_register in source3/librpc/rpc/dcerpc_ep.c)
    and keeps it open to re-register if the endpoint mapper daemon dies
    (rpc_ep_register_loop in source3/rpc_server/rpc_ep_register.c)
* When the endpoint mapper daemon accepts a NCALRPC connection it sets a
  termination function (srv_epmapper_delete_endpoints)
* Suppose the lsarpc daemon exits. The NCALRPC connection termination
  function is called.
* The termination function tries to delete all endpoints registered by that
  connection by calling _epm_Delete
* _epm_Delete calls is_privileged_pipe which access to
  pipes_struct->session_info.

As the call to _epm_Delete occurs outside of the PIDL generated code,
the pipes_stuct->session_info is NULL. This commit also sets
pipes_struct->session_info from the dcerpc_connection before calling
_epm_Delete. As the core rpc server supports security context multiplexing we
need to pass the dcesrv_connection to the termination function and let the
implementation pick a auth context. In the case of the endpoint mapper
the termination function has to pick one of type NCALRPC_AS_SYSTEM to
check if the connection is privileged and delete the endpoints
registered by the connection being closed.

Finally, the samba.tests.dcerpc.raw_protocol testsuite passes against
the ad_member environment.

Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
selftest/knownfail
source3/printing/spoolssd.c
source3/rpc_server/epmapper/srv_epmapper.c
source3/rpc_server/epmapper/srv_epmapper.h
source3/rpc_server/lsasd.c
source3/rpc_server/mdssd.c
source3/rpc_server/rpc_ncacn_np.c
source3/rpc_server/rpc_server.c
source3/rpc_server/rpc_server.h

index 5eb410e81872ad1aa78eb17270091ec1451dc817..f186b85aea78eb644be0642713c48afde7f8d378 100644 (file)
 ^samba.tests.dcerpc.raw_protocol.*.TestDCERPC_BIND.test_no_auth_presentation_ctx_invalid4
 ^samba.tests.dcerpc.raw_protocol.*.TestDCERPC_BIND.test_spnego_change_auth_type2
 ^samba.tests.dcerpc.raw_protocol.*.TestDCERPC_BIND.test_spnego_change_transfer
-^samba.tests.dcerpc.raw_protocol.*\(ad_member\)
 # Association groups not implemented yet in s3 server implementation
 ^samba.tests.dcerpc.raw_protocol.*.TestDCERPC_BIND.test_assoc_group_fail1\(ad_member\)
 ^samba.tests.dcerpc.raw_protocol.*.TestDCERPC_BIND.test_assoc_group_fail2\(ad_member\)
 ^samba4.rpc.echo.*on.*with.object.echo.enum.*nt4_dc
 ^samba4.rpc.echo.*on.*with.object.echo.testcall.*nt4_dc
 ^samba4.rpc.echo.*on.*with.object.echo.testcall2.*nt4_dc
-^samba4.rpc.echo.*on.*with.object.echo.sinkdata.*nt4_dc
-^samba4.rpc.echo.*on.*with.object.echo.addone.*nt4_dc
 ^samba4.rpc.echo.*on.*ncacn_ip_tcp.*with.object.*nt4_dc
 ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_add_duplicate_different_type.*
 ^samba.tests.dcerpc.dnsserver.samba.tests.dcerpc.dnsserver.DnsserverTests.test_rank_none.*
index 3bd7e55001cab29356cfba68d3a4838b611ef426..70d14914f5b813e131f1fb124ebb337d617deee8 100644 (file)
@@ -341,7 +341,8 @@ static int spoolss_children_main(struct tevent_context *ev_ctx,
        return ret;
 }
 
-static void spoolss_client_terminated(struct pipes_struct *p, void *pvt)
+static void spoolss_client_terminated(struct dcesrv_connection *conn,
+                                     void *pvt)
 {
        struct spoolss_children_data *data;
 
index a2373c063fde9ecc26f4cf4dc82ab77d3e44a9b7..ddbd4d68f78644d035aa0b4f26b659903fa9c99a 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "librpc/rpc/dcesrv_core.h"
 #include "librpc/gen_ndr/ndr_epmapper_scompat.h"
+#include "rpc_server/rpc_server.h"
 
 typedef uint32_t error_status_t;
 
@@ -309,12 +310,32 @@ static bool is_privileged_pipe(struct auth_session_info *info) {
        return true;
 }
 
-void srv_epmapper_delete_endpoints(struct pipes_struct *p, void *private_data)
+void srv_epmapper_delete_endpoints(struct dcesrv_connection *conn,
+                                  void *private_data)
 {
+       struct pipes_struct *p = dcesrv_get_pipes_struct(conn);
+       struct dcesrv_auth *auth = NULL;
        struct epm_Delete r;
        struct dcesrv_ep_entry_list *el = p->ep_entries;
        error_status_t result;
 
+       /* We have to set p->session_info to check if the connection is
+        * privileged and delete the endpoints registered by this connection.
+        * Set the default session info created at connection time as a
+        * fallback.
+        */
+       p->session_info = conn->default_auth_state->session_info;
+
+       /* Due to security context multiplexing we can have several states
+        * in the connection. Search the one of type NCALRPC_AS_SYSTEM to
+        * replace the default.
+        */
+       for (auth = conn->auth_states; auth != NULL; auth = auth->next) {
+               if (auth->auth_type == DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM) {
+                       p->session_info = auth->session_info;
+               }
+       }
+
        while (el) {
                struct dcesrv_ep_entry_list *next = el->next;
 
index b591844c2b2ff8fd98c6a827a3d502befe12d8e7..7ec35ace66697d00790787719a8f872193251f8f 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef _SRV_EPMAPPER_H_
 #define _SRV_EPMAPPER_H_
 
+struct dcesrv_connection;
+
 /**
  * @brief Cleanup memory and other stuff.
  */
@@ -31,7 +33,8 @@ void srv_epmapper_cleanup(void);
  * @brief Cleanup function used to delete endpoints when a ncalrpc connection
  *        from an external daemon is lost
  */
-void srv_epmapper_delete_endpoints(struct pipes_struct *p, void *private_data);
+void srv_epmapper_delete_endpoints(struct dcesrv_connection *conn,
+                                  void *private_data);
 
 #endif /*_SRV_EPMAPPER_H_ */
 
index 676a22b12ace48e7a2e5b422ae92394941f81480..ecfa92b5b874ecd056648e94f2b9ea58591fee15 100644 (file)
@@ -332,7 +332,7 @@ static int lsasd_children_main(struct tevent_context *ev_ctx,
        return ret;
 }
 
-static void lsasd_client_terminated(struct pipes_struct *p, void *pvt)
+static void lsasd_client_terminated(struct dcesrv_connection *conn, void *pvt)
 {
        struct lsasd_children_data *data;
 
index 0da768cafb413292e7bae317f4e045aaa841ca95..24177e22b368cefc8576cb1b7e02e9557d3332a9 100644 (file)
@@ -280,7 +280,7 @@ static int mdssd_children_main(struct tevent_context *ev_ctx,
        return ret;
 }
 
-static void mdssd_client_terminated(struct pipes_struct *p, void *pvt)
+static void mdssd_client_terminated(struct dcesrv_connection *conn, void *pvt)
 {
        struct mdssd_children_data *data;
 
index 7f80fd0490f6a359b31b150cd93ef745423883b7..7a98543a63ffd3b6ac064d596a7f654d5c3ee20f 100644 (file)
@@ -106,7 +106,7 @@ NTSTATUS make_internal_rpc_pipe_socketpair(
 {
        TALLOC_CTX *tmp_ctx = talloc_stackframe();
        struct dcerpc_ncacn_conn *ncacn_conn = NULL;
-       struct tevent_req *subreq;
+       struct dcesrv_connection *dcesrv_conn = NULL;
        struct npa_state *npa;
        NTSTATUS status;
        int error;
@@ -192,7 +192,6 @@ NTSTATUS make_internal_rpc_pipe_socketpair(
                                      transport,
                                      ncacn_conn->remote_client_addr,
                                      ncacn_conn->local_server_addr,
-                                     &ncacn_conn->session_info,
                                      &ncacn_conn->p,
                                      &error);
        if (rc == -1) {
@@ -200,21 +199,49 @@ NTSTATUS make_internal_rpc_pipe_socketpair(
                goto out;
        }
 
-       ncacn_conn->send_queue = tevent_queue_create(ncacn_conn, "npa_server_write_queue");
-       if (ncacn_conn->send_queue == NULL) {
+       /*
+        * 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(ncacn_conn->dce_ctx,
+                                        ncacn_conn,
+                                        ncacn_conn->endpoint,
+                                        ncacn_conn->session_info,
+                                        ncacn_conn->ev_ctx,
+                                        DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
+                                        &dcesrv_conn);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("Failed to connect to endpoint: %s\n",
+                       nt_errstr(status));
+               goto out;
+       }
+
+       dcesrv_conn->transport.private_data = ncacn_conn;
+       dcesrv_conn->transport.report_output_data =
+               dcesrv_sock_report_output_data;
+       dcesrv_conn->transport.terminate_connection =
+               dcesrv_transport_terminate_connection;
+       dcesrv_conn->send_queue = tevent_queue_create(dcesrv_conn,
+                                                     "dcesrv send queue");
+       if (dcesrv_conn->send_queue == NULL) {
                status = NT_STATUS_NO_MEMORY;
+               DBG_ERR("Failed to create send queue: %s\n",
+                       nt_errstr(status));
                goto out;
        }
 
-       subreq = dcerpc_read_ncacn_packet_send(ncacn_conn, ncacn_conn->ev_ctx,
-                                              ncacn_conn->tstream);
-       if (subreq == NULL) {
-               DEBUG(2, ("Failed to start receiving packets\n"));
-               status = NT_STATUS_PIPE_BROKEN;
+       dcesrv_conn->stream = talloc_move(dcesrv_conn, &ncacn_conn->tstream);
+       dcesrv_conn->local_address = ncacn_conn->local_server_addr;
+       dcesrv_conn->remote_address = ncacn_conn->remote_client_addr;
+
+       status = dcesrv_connection_loop_start(dcesrv_conn);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("Failed to start dcesrv_connection loop: %s\n",
+                       nt_errstr(status));
                goto out;
        }
-       tevent_req_set_callback(subreq, dcerpc_ncacn_packet_process,
-                               ncacn_conn);
 
        *pnpa = talloc_move(mem_ctx, &npa);
        status = NT_STATUS_OK;
index 704dcb8a5fbec5f7c54d6a05181b859e378dd224..8789b998a34abd47bdec178e2cf954346dc340ef 100644 (file)
@@ -47,11 +47,9 @@ int make_server_pipes_struct(TALLOC_CTX *mem_ctx,
                             enum dcerpc_transport_t transport,
                             const struct tsocket_address *remote_address,
                             const struct tsocket_address *local_address,
-                            struct auth_session_info **psession_info,
                             struct pipes_struct **_p,
                             int *perrno)
 {
-       struct auth_session_info *session_info = *psession_info;
        struct pipes_struct *p;
        int ret;
 
@@ -63,18 +61,6 @@ int make_server_pipes_struct(TALLOC_CTX *mem_ctx,
                return -1;
        }
 
-       if ((session_info->unix_token == NULL) ||
-           (session_info->unix_info == NULL) ||
-           (session_info->security_token == NULL)) {
-               DBG_ERR("Supplied session_info was incomplete!\n");
-               TALLOC_FREE(p);
-               *perrno = EINVAL;
-               return -1;
-       }
-
-       /* Don't call create_local_token(), we already have the full details here */
-       p->session_info = talloc_move(p, psession_info);
-
        *_p = p;
        return 0;
 }
@@ -726,11 +712,14 @@ static void dcesrv_ncalrpc_listener(struct tevent_context *ev,
                            state->termination_data);
 }
 
-static int dcerpc_ncacn_conn_destructor(struct dcerpc_ncacn_conn *ncacn_conn)
+static int dcesrv_connection_destructor(struct dcesrv_connection *conn)
 {
+       struct dcerpc_ncacn_conn *ncacn_conn = talloc_get_type_abort(
+                       conn->transport.private_data,
+                       struct dcerpc_ncacn_conn);
+
        if (ncacn_conn->termination_fn != NULL) {
-               ncacn_conn->termination_fn(ncacn_conn->p,
-                                          ncacn_conn->termination_data);
+               ncacn_conn->termination_fn(conn, ncacn_conn->termination_data);
        }
 
        return 0;
@@ -751,7 +740,6 @@ NTSTATUS dcerpc_ncacn_conn_init(TALLOC_CTX *mem_ctx,
        if (ncacn_conn == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
-       talloc_set_destructor(ncacn_conn, dcerpc_ncacn_conn_destructor);
 
        ncacn_conn->ev_ctx = ev_ctx;
        ncacn_conn->msg_ctx = msg_ctx;
@@ -926,7 +914,6 @@ static void dcesrv_ncacn_np_accept_done(struct tevent_req *subreq)
 
 static void dcesrv_ncacn_accept_step2(struct dcerpc_ncacn_conn *ncacn_conn)
 {
-       struct tevent_req *subreq = NULL;
        char *pipe_name = NULL;
        uid_t uid;
        gid_t gid;
@@ -936,6 +923,8 @@ static void dcesrv_ncacn_accept_step2(struct dcerpc_ncacn_conn *ncacn_conn)
                        ncacn_conn->endpoint->ep_description);
        const char *endpoint = dcerpc_binding_get_string_option(
                        ncacn_conn->endpoint->ep_description, "endpoint");
+       struct dcesrv_connection *dcesrv_conn = NULL;
+       NTSTATUS status;
 
        switch (transport) {
                case NCACN_IP_TCP:
@@ -995,8 +984,6 @@ static void dcesrv_ncacn_accept_step2(struct dcerpc_ncacn_conn *ncacn_conn)
        }
 
        if (ncacn_conn->session_info == NULL) {
-               NTSTATUS status;
-
                status = make_session_info_anonymous(ncacn_conn,
                                                     &ncacn_conn->session_info);
                if (!NT_STATUS_IS_OK(status)) {
@@ -1014,7 +1001,6 @@ static void dcesrv_ncacn_accept_step2(struct dcerpc_ncacn_conn *ncacn_conn)
                                      transport,
                                      ncacn_conn->remote_client_addr,
                                      ncacn_conn->local_server_addr,
-                                     &ncacn_conn->session_info,
                                      &ncacn_conn->p,
                                      &sys_errno);
        if (rc < 0) {
@@ -1024,25 +1010,51 @@ static void dcesrv_ncacn_accept_step2(struct dcerpc_ncacn_conn *ncacn_conn)
                return;
        }
 
-       ncacn_conn->send_queue = tevent_queue_create(ncacn_conn,
-                                                       "dcerpc send queue");
-       if (ncacn_conn->send_queue == NULL) {
-               DBG_ERR("No memory\n");
-               ncacn_terminate_connection(ncacn_conn, "No memory");
+       /*
+        * 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(ncacn_conn->dce_ctx,
+                                        ncacn_conn,
+                                        ncacn_conn->endpoint,
+                                        ncacn_conn->session_info,
+                                        ncacn_conn->ev_ctx,
+                                        DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
+                                        &dcesrv_conn);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("Failed to connect to endpoint: %s\n",
+                       nt_errstr(status));
+               ncacn_terminate_connection(ncacn_conn, nt_errstr(status));
                return;
        }
+       talloc_set_destructor(dcesrv_conn, dcesrv_connection_destructor);
 
-       subreq = dcerpc_read_ncacn_packet_send(ncacn_conn,
-                                              ncacn_conn->ev_ctx,
-                                              ncacn_conn->tstream);
-       if (subreq == NULL) {
-               DBG_ERR("No memory\n");
-               ncacn_terminate_connection(ncacn_conn, "No memory");
+       dcesrv_conn->transport.private_data = ncacn_conn;
+       dcesrv_conn->transport.report_output_data =
+               dcesrv_sock_report_output_data;
+       dcesrv_conn->transport.terminate_connection =
+               dcesrv_transport_terminate_connection;
+       dcesrv_conn->send_queue = tevent_queue_create(dcesrv_conn,
+                                                     "dcesrv send queue");
+       if (dcesrv_conn->send_queue == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               DBG_ERR("Failed to create send queue: %s\n",
+                       nt_errstr(status));
+               ncacn_terminate_connection(ncacn_conn, nt_errstr(status));
                return;
        }
 
-       tevent_req_set_callback(subreq, dcerpc_ncacn_packet_process, ncacn_conn);
-
+       dcesrv_conn->stream = talloc_move(dcesrv_conn, &ncacn_conn->tstream);
+       dcesrv_conn->local_address = ncacn_conn->local_server_addr;
+       dcesrv_conn->remote_address = ncacn_conn->remote_client_addr;
+       status = dcesrv_connection_loop_start(dcesrv_conn);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("Failed to start dcesrv_connection loop: %s\n",
+                               nt_errstr(status));
+               ncacn_terminate_connection(ncacn_conn, nt_errstr(status));
+       }
        DBG_DEBUG("dcerpc_ncacn_accept done\n");
 
        return;
@@ -1404,6 +1416,16 @@ NTSTATUS dcesrv_assoc_group_find(struct dcesrv_call_state *call)
        return dcesrv_assoc_group_new(call, assoc_group_id);
 }
 
+void dcesrv_transport_terminate_connection(struct dcesrv_connection *dce_conn,
+                                          const char *reason)
+{
+       struct dcerpc_ncacn_conn *ncacn_conn = talloc_get_type_abort(
+                       dce_conn->transport.private_data,
+                       struct dcerpc_ncacn_conn);
+
+       ncacn_terminate_connection(ncacn_conn, reason);
+}
+
 static void ncacn_terminate_connection(struct dcerpc_ncacn_conn *conn,
                                       const char *reason)
 {
index 62d219ee7e5946c2f9b54166119b8d63535780a5..99d595070a9db2bb9461258872c41b28be16ef9b 100644 (file)
@@ -28,7 +28,8 @@ struct pipes_struct;
 struct auth_session_info;
 struct cli_credentials;
 
-typedef void (*dcerpc_ncacn_termination_fn)(struct pipes_struct *, void *);
+typedef void (*dcerpc_ncacn_termination_fn)(struct dcesrv_connection *,
+                                           void *);
 
 struct dcerpc_ncacn_conn {
        int sock;
@@ -70,7 +71,6 @@ int make_server_pipes_struct(TALLOC_CTX *mem_ctx,
                             enum dcerpc_transport_t transport,
                             const struct tsocket_address *remote_address,
                             const struct tsocket_address *local_address,
-                            struct auth_session_info **session_info,
                             struct pipes_struct **_p,
                             int *perrno);
 
@@ -126,4 +126,7 @@ NTSTATUS dcesrv_endpoint_by_ncacn_np_name(struct dcesrv_context *dce_ctx,
 
 struct pipes_struct *dcesrv_get_pipes_struct(struct dcesrv_connection *conn);
 
+void dcesrv_transport_terminate_connection(struct dcesrv_connection *dce_conn,
+                                          const char *reason);
+
 #endif /* _PRC_SERVER_H_ */