s3:winbindd: Dispatch RPC calls through interface local handler
[samba.git] / source3 / winbindd / winbindd_dual_ndr.c
index e400b6110d4e73f98924f196c94aeebec32f7f99..f615ada1fca8cb4a9ae9b393ad2e1aba050bf228 100644 (file)
@@ -29,7 +29,9 @@
 #include "includes.h"
 #include "winbindd/winbindd.h"
 #include "winbindd/winbindd_proto.h"
-#include "librpc/gen_ndr/srv_wbint.h"
+#include "ntdomain.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_winbind.h"
 
 struct wbint_bh_state {
        struct winbindd_domain *domain;
@@ -41,7 +43,7 @@ static bool wbint_bh_is_connected(struct dcerpc_binding_handle *h)
        struct wbint_bh_state *hs = dcerpc_binding_handle_data(h,
                                     struct wbint_bh_state);
 
-       if (!hs->child) {
+       if ((hs->domain == NULL) && (hs->child == NULL)) {
                return false;
        }
 
@@ -64,7 +66,8 @@ struct wbint_bh_raw_call_state {
        DATA_BLOB out_data;
 };
 
-static void wbint_bh_raw_call_done(struct tevent_req *subreq);
+static void wbint_bh_raw_call_child_done(struct tevent_req *subreq);
+static void wbint_bh_raw_call_domain_done(struct tevent_req *subreq);
 
 static struct tevent_req *wbint_bh_raw_call_send(TALLOC_CTX *mem_ctx,
                                                  struct tevent_context *ev,
@@ -95,13 +98,15 @@ static struct tevent_req *wbint_bh_raw_call_send(TALLOC_CTX *mem_ctx,
 
        ok = wbint_bh_is_connected(h);
        if (!ok) {
-               tevent_req_nterror(req, NT_STATUS_INVALID_CONNECTION);
+               tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
                return tevent_req_post(req, ev);
        }
 
        if ((state->domain != NULL)
            && wcache_fetch_ndr(state, state->domain, state->opnum,
                                &state->in_data, &state->out_data)) {
+               DBG_DEBUG("Got opnum %"PRIu32" for domain %s from cache\n",
+                         state->opnum, state->domain->name);
                tevent_req_done(req);
                return tevent_req_post(req, ev);
        }
@@ -111,17 +116,28 @@ static struct tevent_req *wbint_bh_raw_call_send(TALLOC_CTX *mem_ctx,
        state->request.extra_data.data = (char *)state->in_data.data;
        state->request.extra_len = state->in_data.length;
 
-       subreq = wb_child_request_send(state, ev, hs->child,
-                                      &state->request);
+       if (hs->child != NULL) {
+               subreq = wb_child_request_send(state, ev, hs->child,
+                                              &state->request);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(
+                       subreq, wbint_bh_raw_call_child_done, req);
+               return req;
+       }
+
+       subreq = wb_domain_request_send(state, ev, hs->domain,
+                                       &state->request);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
-       tevent_req_set_callback(subreq, wbint_bh_raw_call_done, req);
+       tevent_req_set_callback(subreq, wbint_bh_raw_call_domain_done, req);
 
        return req;
 }
 
-static void wbint_bh_raw_call_done(struct tevent_req *subreq)
+static void wbint_bh_raw_call_child_done(struct tevent_req *subreq)
 {
        struct tevent_req *req =
                tevent_req_callback_data(subreq,
@@ -143,7 +159,41 @@ static void wbint_bh_raw_call_done(struct tevent_req *subreq)
                state->response->extra_data.data,
                state->response->length - sizeof(struct winbindd_response));
        if (state->response->extra_data.data && !state->out_data.data) {
-               tevent_req_nomem(NULL, req);
+               tevent_req_oom(req);
+               return;
+       }
+
+       if (state->domain != NULL) {
+               wcache_store_ndr(state->domain, state->opnum,
+                                &state->in_data, &state->out_data);
+       }
+
+       tevent_req_done(req);
+}
+
+static void wbint_bh_raw_call_domain_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct wbint_bh_raw_call_state *state =
+               tevent_req_data(req,
+               struct wbint_bh_raw_call_state);
+       int ret, err;
+
+       ret = wb_domain_request_recv(subreq, state, &state->response, &err);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               NTSTATUS status = map_nt_error_from_unix(err);
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       state->out_data = data_blob_talloc(state,
+               state->response->extra_data.data,
+               state->response->length - sizeof(struct winbindd_response));
+       if (state->response->extra_data.data && !state->out_data.data) {
+               tevent_req_oom(req);
                return;
        }
 
@@ -200,15 +250,14 @@ static struct tevent_req *wbint_bh_disconnect_send(TALLOC_CTX *mem_ctx,
 
        ok = wbint_bh_is_connected(h);
        if (!ok) {
-               tevent_req_nterror(req, NT_STATUS_INVALID_CONNECTION);
+               tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
                return tevent_req_post(req, ev);
        }
 
        /*
         * TODO: do a real async disconnect ...
-        *
-        * For now the caller needs to free rpc_cli
         */
+       hs->domain = NULL;
        hs->child = NULL;
 
        tevent_req_done(req);
@@ -282,7 +331,7 @@ struct dcerpc_binding_handle *wbint_binding_handle(TALLOC_CTX *mem_ctx,
        h = dcerpc_binding_handle_create(mem_ctx,
                                         &wbint_bh_ops,
                                         NULL,
-                                        &ndr_table_wbint,
+                                        &ndr_table_winbind,
                                         &hs,
                                         struct wbint_bh_state,
                                         __location__);
@@ -298,41 +347,67 @@ struct dcerpc_binding_handle *wbint_binding_handle(TALLOC_CTX *mem_ctx,
 enum winbindd_result winbindd_dual_ndrcmd(struct winbindd_domain *domain,
                                          struct winbindd_cli_state *state)
 {
-       struct pipes_struct p;
-       struct api_struct *fns;
-       int num_fns;
-       bool ret;
+       const struct dcesrv_endpoint_server *ep_server = NULL;
+       struct dcesrv_interface iface;
+       const struct ndr_syntax_id *abstract_syntax;
+       bool ok;
+       uint32_t opnum = state->request->data.ndrcmd;
+       struct pipes_struct *p;
+       TALLOC_CTX *mem_ctx;
+       DATA_BLOB in;
+       DATA_BLOB out;
+       NTSTATUS status;
 
-       wbint_get_pipe_fns(&fns, &num_fns);
+       DBG_DEBUG("Running command %s (domain '%s')\n",
+                 ndr_table_winbind.calls[opnum].name,
+                 domain ? domain->name : "(null)");
 
-       if (state->request->data.ndrcmd >= num_fns) {
+       ep_server = dcesrv_ep_server_byname(ndr_table_winbind.name);
+       if (ep_server == NULL) {
+               DBG_ERR("Failed to get DCE/RPC endpoint server '%s'\n",
+                       ndr_table_winbind.name);
                return WINBINDD_ERROR;
        }
 
-       DEBUG(10, ("winbindd_dual_ndrcmd: Running command %s (%s)\n",
-                  fns[state->request->data.ndrcmd].name,
-                  domain ? domain->name : "no domain"));
+       abstract_syntax = &ndr_table_winbind.syntax_id;
+       ok = ep_server->interface_by_uuid(&iface, &abstract_syntax->uuid,
+                                         abstract_syntax->if_version);
+       if (!ok) {
+               DBG_ERR("Failed to get DCE/RPC interface\n");
+               return WINBINDD_ERROR;
+       }
 
-       ZERO_STRUCT(p);
-       p.mem_ctx = talloc_stackframe();
-       p.in_data.data = data_blob_const(state->request->extra_data.data,
-                                        state->request->extra_len);
+       mem_ctx = talloc_stackframe();
+       if (mem_ctx == NULL) {
+               DBG_ERR("No memory");
+               return WINBINDD_ERROR;
+       }
 
-       ret = fns[state->request->data.ndrcmd].fn(&p);
-       if (!ret) {
-               TALLOC_FREE(p.mem_ctx);
+       p = talloc_zero(mem_ctx, struct pipes_struct);
+       if (p == NULL) {
+               DBG_ERR("No memory\n");
+               return WINBINDD_ERROR;
+       }
+       p->mem_ctx = mem_ctx;
+
+       in = data_blob_const(state->request->extra_data.data,
+                            state->request->extra_len);
+
+       status = iface.local(p, opnum, mem_ctx, &in, &out);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(mem_ctx);
                return WINBINDD_ERROR;
        }
 
        state->response->extra_data.data =
-               talloc_move(state->mem_ctx, &p.out_data.rdata.data);
-       state->response->length += p.out_data.rdata.length;
-       p.out_data.rdata.length = 0;
+               talloc_steal(state->mem_ctx, out.data);
+       state->response->length += out.length;
 
-       TALLOC_FREE(p.mem_ctx);
+       TALLOC_FREE(mem_ctx);
 
        if (state->response->extra_data.data == NULL) {
                return WINBINDD_ERROR;
        }
+
        return WINBINDD_OK;
 }