s3:rpc_server: Do not use the default ncalrpc endpoint for external services
authorSamuel Cabrero <scabrero@suse.de>
Mon, 23 Aug 2021 12:27:49 +0000 (14:27 +0200)
committerVolker Lendecke <vl@samba.org>
Tue, 21 Sep 2021 11:00:01 +0000 (11:00 +0000)
In samba3 it is possible to run some services externally, for example:

rpc_daemon:lsasd = fork
rpc_server:netlogon = disabled
rpc_server:samr = external
rpc_server:lsarpc = external

The external services running in separate processes have to use its own
dedicated ncalrpc endpoint, otherwise will race with main smbd serving the
embedded services to accept connections on ncalrpc default socket. If the
connection ends in an external process and the client tries to bind to an
interface not registered there (like winreg for example) the bind will fail.

Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Volker Lendecke <vl@samba.org>
Autobuild-User(master): Volker Lendecke <vl@samba.org>
Autobuild-Date(master): Tue Sep 21 11:00:01 UTC 2021 on sn-devel-184

pidl/lib/Parse/Pidl/Samba4/NDR/ServerCompat.pm
source3/rpc_server/rpc_ncacn_np.c
source3/winbindd/winbindd_dual_ndr.c

index 064bec8aee434f49287c6415ee2fd5f5916cd04d..e35813a24abd3fc7db24768d9a3938874d6e8894 100644 (file)
@@ -469,6 +469,7 @@ sub boilerplate_ep_server($)
        $self->pidl("static NTSTATUS $name\__check_register_in_endpoint(const char *name, struct dcerpc_binding *binding) {");
        $self->indent();
        $self->pidl("enum dcerpc_transport_t transport = dcerpc_binding_get_transport(binding);");
+       $self->pidl("NTSTATUS status;");
        $self->pidl("");
        $self->pidl("/* If service is disabled, do not register */");
        $self->pidl("if (rpc_service_mode(name) == RPC_SERVICE_MODE_DISABLED) {");
@@ -487,6 +488,38 @@ sub boilerplate_ep_server($)
        $self->pidl("return NT_STATUS_NOT_SUPPORTED;");
        $self->deindent();
        $self->pidl("}");
+
+       $self->pidl("");
+       $self->pidl("/*");
+       $self->pidl(" * If rpc service is external then change the default ncalrpc endpoint,");
+       $self->pidl(" * otherwise if the rpc daemon running this service is configured in");
+       $self->pidl(" * fork mode the forked process will race with main smbd to accept the");
+       $self->pidl(" * connections in the default ncalrpc socket, and the forked process");
+       $self->pidl(" * may not have the requested interface registered.");
+       $self->pidl(" * For example, in the ad_member test environment:");
+       $self->pidl(" *");
+       $self->pidl(" *   rpc_server:lsarpc = external");
+       $self->pidl(" *   rpc_server:samr = external");
+       $self->pidl(" *   rpc_server:netlogon = disabled");
+       $self->pidl(" *   rpc_daemon:lsasd = fork");
+       $self->pidl(" *");
+       $self->pidl(" * With these settings both, the main smbd and all the preforked lsasd");
+       $self->pidl(" * processes would be listening in the default ncalrpc socket if it is");
+       $self->pidl(" * not changed. If a client connection is accepted by one of the lsasd");
+       $self->pidl(" * worker processes and the client asks for an interface not registered");
+       $self->pidl(" * in these processes (winreg for example) it will get an error.");
+       $self->pidl(" */");
+       $self->pidl("if (rpc_service_mode(name) == RPC_SERVICE_MODE_EXTERNAL && transport == NCALRPC) {");
+       $self->indent();
+       $self->pidl("status = dcerpc_binding_set_string_option(binding, \"endpoint\", \"$uname\");");
+       $self->pidl("if (!NT_STATUS_IS_OK(status)) {");
+       $self->indent();
+       $self->pidl("return status;");
+       $self->deindent();
+       $self->pidl("}");
+       $self->deindent();
+       $self->pidl("}");
+
        $self->pidl("");
        $self->pidl("return NT_STATUS_OK;");
        $self->deindent();
@@ -499,6 +532,7 @@ sub boilerplate_ep_server($)
        $self->pidl("uint32_t i;");
        $self->pidl("NTSTATUS ret;");
        $self->pidl("struct dcerpc_binding *binding;");
+       $self->pidl("struct dcerpc_binding *binding2 = NULL;");
        $self->pidl("");
        $self->pidlnoindent("#ifdef DCESRV_INTERFACE_$uname\_NCACN_NP_SECONDARY_ENDPOINT");
        $self->pidl("const char *ncacn_np_secondary_endpoint = DCESRV_INTERFACE_$uname\_NCACN_NP_SECONDARY_ENDPOINT;");
@@ -525,9 +559,25 @@ sub boilerplate_ep_server($)
        $self->pidl("continue;");
        $self->deindent();
        $self->pidl("}");
-       $self->pidl("talloc_free(binding);");
        $self->pidl("");
-       $self->pidl("ret = dcesrv_interface_register(dce_ctx, name, ncacn_np_secondary_endpoint, &dcesrv_$name\_interface, NULL);");
+
+       $self->pidl("if (ncacn_np_secondary_endpoint != NULL) {");
+       $self->indent();
+       $self->pidl("ret = dcerpc_parse_binding(dce_ctx, ncacn_np_secondary_endpoint, &binding2);");
+       $self->pidl("if (NT_STATUS_IS_ERR(ret)) {");
+       $self->indent();
+       $self->pidl("DBG_ERR(\"Failed to parse 2nd binding string \'%s\'\\n\", ncacn_np_secondary_endpoint);");
+       $self->pidl("TALLOC_FREE(binding);");
+       $self->pidl("return ret;");
+       $self->deindent();
+       $self->pidl("}");
+       $self->deindent();
+       $self->pidl("}");
+       $self->pidl("");
+
+       $self->pidl("ret = dcesrv_interface_register_b(dce_ctx, binding, binding2, &dcesrv_$name\_interface, NULL);");
+       $self->pidl("TALLOC_FREE(binding);");
+       $self->pidl("TALLOC_FREE(binding2);");
        $self->pidl("if (!NT_STATUS_IS_OK(ret)) {");
        $self->indent();
        $self->pidl("DBG_ERR(\"Failed to register endpoint \'%s\'\\n\",name);");
index ee362e409d15a0f19cdc718ef52f0dee1f88e563..bb95c678e7b11f853b3f82afa8befca083f57a77 100644 (file)
@@ -314,7 +314,8 @@ fail:
 }
 
 static NTSTATUS find_ncalrpc_default_endpoint(struct dcesrv_context *dce_ctx,
-                                             struct dcesrv_endpoint **ep)
+                               const struct ndr_interface_table *ndr_table,
+                               struct dcesrv_endpoint **ep)
 {
        TALLOC_CTX *tmp_ctx = NULL;
        struct dcerpc_binding *binding = NULL;
@@ -326,6 +327,25 @@ static NTSTATUS find_ncalrpc_default_endpoint(struct dcesrv_context *dce_ctx,
                return NT_STATUS_NO_MEMORY;
        }
 
+       if (rpc_service_mode(ndr_table->name) == RPC_SERVICE_MODE_EXTERNAL) {
+               ep_description = talloc_asprintf(tmp_ctx, "ncalrpc:[%s]",
+                               talloc_strdup_upper(tmp_ctx, ndr_table->name));
+               if (ep_description == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto out;
+               }
+
+               status = dcerpc_parse_binding(tmp_ctx, ep_description, &binding);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto out;
+               }
+
+               status = dcesrv_find_endpoint(dce_ctx, binding, ep);
+               if (NT_STATUS_IS_OK(status)) {
+                       goto out;
+               }
+       }
+
        /*
         * Some services use a rpcint binding handle in their initialization,
         * before the server is fully initialized. Search the NCALRPC endpoint
@@ -380,7 +400,7 @@ static NTSTATUS make_internal_dcesrv_connection(TALLOC_CTX *mem_ctx,
        conn->preferred_transfer = &ndr_transfer_syntax_ndr;
        conn->transport.private_data = ncacn_conn;
 
-       status = find_ncalrpc_default_endpoint(conn->dce_ctx, &endpoint);
+       status = find_ncalrpc_default_endpoint(conn->dce_ctx, ndr_table, &endpoint);
        if (!NT_STATUS_IS_OK(status)) {
                goto fail;
        }
index 5f0ec82067c0b3a931d10fc24d970018aa752b5f..36f01224e24c9e567fb64379fc2ce2e2b44821d4 100644 (file)
@@ -378,6 +378,16 @@ static NTSTATUS find_ncalrpc_default_endpoint(struct dcesrv_context *dce_ctx,
                goto out;
        }
 
+       status = dcerpc_parse_binding(tmp_ctx, "ncalrpc:[WINBIND]", &binding);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto out;
+       }
+
+       status = dcesrv_find_endpoint(dce_ctx, binding, ep);
+       if (NT_STATUS_IS_OK(status)) {
+               goto out;
+       }
+
        status = dcerpc_parse_binding(tmp_ctx, "ncalrpc:[DEFAULT]", &binding);
        if (!NT_STATUS_IS_OK(status)) {
                goto out;