2 Unix SMB/CIFS implementation.
4 Provide parent->child communication based on NDR marshalling
6 Copyright (C) Volker Lendecke 2009
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 * This file implements an RPC between winbind parent and child processes,
24 * leveraging the autogenerated marshalling routines for MSRPC. This is not
25 * MSRPC, as it does not go through the whole DCERPC fragmentation, we just
26 * leverage much the same infrastructure we already have for it.
30 #include "winbindd/winbindd.h"
31 #include "winbindd/winbindd_proto.h"
32 #include "librpc/gen_ndr/srv_wbint.h"
34 struct wb_ndr_transport_priv {
35 struct winbindd_domain *domain;
36 struct winbindd_child *child;
39 struct wb_ndr_dispatch_state {
40 struct wb_ndr_transport_priv *transport;
42 const struct ndr_interface_call *call;
44 DATA_BLOB req_blob, resp_blob;
45 struct winbindd_request request;
46 struct winbindd_response *response;
49 static void wb_ndr_dispatch_done(struct tevent_req *subreq);
51 static struct tevent_req *wb_ndr_dispatch_send(TALLOC_CTX *mem_ctx,
52 struct tevent_context *ev,
53 struct rpc_pipe_client *cli,
54 const struct ndr_interface_table *table,
58 struct tevent_req *req, *subreq;
59 struct wb_ndr_dispatch_state *state;
60 struct wb_ndr_transport_priv *transport = talloc_get_type_abort(
61 cli->transport->priv, struct wb_ndr_transport_priv);
62 struct ndr_push *push;
63 enum ndr_err_code ndr_err;
65 req = tevent_req_create(mem_ctx, &state,
66 struct wb_ndr_dispatch_state);
72 state->call = &table->calls[opnum];
73 state->transport = transport;
76 push = ndr_push_init_ctx(state);
77 if (tevent_req_nomem(push, req)) {
78 return tevent_req_post(req, ev);
81 ndr_err = state->call->ndr_push(push, NDR_IN, r);
82 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
83 tevent_req_nterror(req, ndr_map_error2ntstatus(ndr_err));
85 return tevent_req_post(req, ev);
88 state->req_blob = ndr_push_blob(push);
90 if ((transport->domain != NULL)
91 && wcache_fetch_ndr(state, transport->domain, opnum,
92 &state->req_blob, &state->resp_blob)) {
94 return tevent_req_post(req, ev);
97 state->request.cmd = WINBINDD_DUAL_NDRCMD;
98 state->request.data.ndrcmd = opnum;
99 state->request.extra_data.data = (char *)state->req_blob.data;
100 state->request.extra_len = state->req_blob.length;
102 subreq = wb_child_request_send(state, ev, transport->child,
104 if (tevent_req_nomem(subreq, req)) {
105 return tevent_req_post(req, ev);
107 tevent_req_set_callback(subreq, wb_ndr_dispatch_done, req);
111 static void wb_ndr_dispatch_done(struct tevent_req *subreq)
113 struct tevent_req *req = tevent_req_callback_data(
114 subreq, struct tevent_req);
115 struct wb_ndr_dispatch_state *state = tevent_req_data(
116 req, struct wb_ndr_dispatch_state);
119 ret = wb_child_request_recv(subreq, state, &state->response, &err);
122 tevent_req_nterror(req, map_nt_error_from_unix(err));
126 state->resp_blob = data_blob_const(
127 state->response->extra_data.data,
128 state->response->length - sizeof(struct winbindd_response));
130 if (state->transport->domain != NULL) {
131 wcache_store_ndr(state->transport->domain, state->opnum,
132 &state->req_blob, &state->resp_blob);
135 tevent_req_done(req);
138 static NTSTATUS wb_ndr_dispatch_recv(struct tevent_req *req,
141 struct wb_ndr_dispatch_state *state = tevent_req_data(
142 req, struct wb_ndr_dispatch_state);
144 struct ndr_pull *pull;
145 enum ndr_err_code ndr_err;
147 if (tevent_req_is_nterror(req, &status)) {
151 pull = ndr_pull_init_blob(&state->resp_blob, mem_ctx);
153 return NT_STATUS_NO_MEMORY;
156 /* have the ndr parser alloc memory for us */
157 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
158 ndr_err = state->call->ndr_pull(pull, NDR_OUT, state->r);
161 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
162 return ndr_map_error2ntstatus(ndr_err);
168 static NTSTATUS wb_ndr_dispatch(struct rpc_pipe_client *cli,
170 const struct ndr_interface_table *table,
171 uint32_t opnum, void *r)
173 TALLOC_CTX *frame = talloc_stackframe();
174 struct event_context *ev;
175 struct tevent_req *req;
176 NTSTATUS status = NT_STATUS_OK;
178 ev = event_context_init(frame);
180 status = NT_STATUS_NO_MEMORY;
184 req = wb_ndr_dispatch_send(frame, ev, cli, table, opnum, r);
186 status = NT_STATUS_NO_MEMORY;
190 if (!tevent_req_poll(req, ev)) {
191 status = map_nt_error_from_unix(errno);
195 status = wb_ndr_dispatch_recv(req, mem_ctx);
201 struct wbint_bh_state {
202 struct rpc_pipe_client *rpc_cli;
205 static bool wbint_bh_is_connected(struct dcerpc_binding_handle *h)
207 struct wbint_bh_state *hs = dcerpc_binding_handle_data(h,
208 struct wbint_bh_state);
217 struct wbint_bh_raw_call_state {
218 struct winbindd_domain *domain;
221 struct winbindd_request request;
222 struct winbindd_response *response;
226 static void wbint_bh_raw_call_done(struct tevent_req *subreq);
228 static struct tevent_req *wbint_bh_raw_call_send(TALLOC_CTX *mem_ctx,
229 struct tevent_context *ev,
230 struct dcerpc_binding_handle *h,
231 const struct GUID *object,
234 const uint8_t *in_data,
237 struct wbint_bh_state *hs =
238 dcerpc_binding_handle_data(h,
239 struct wbint_bh_state);
240 struct wb_ndr_transport_priv *transport =
241 talloc_get_type_abort(hs->rpc_cli->transport->priv,
242 struct wb_ndr_transport_priv);
243 struct tevent_req *req;
244 struct wbint_bh_raw_call_state *state;
246 struct tevent_req *subreq;
248 req = tevent_req_create(mem_ctx, &state,
249 struct wbint_bh_raw_call_state);
253 state->domain = transport->domain;
254 state->opnum = opnum;
255 state->in_data.data = discard_const_p(uint8_t, in_data);
256 state->in_data.length = in_length;
258 ok = wbint_bh_is_connected(h);
260 tevent_req_nterror(req, NT_STATUS_INVALID_CONNECTION);
261 return tevent_req_post(req, ev);
264 if ((state->domain != NULL)
265 && wcache_fetch_ndr(state, state->domain, state->opnum,
266 &state->in_data, &state->out_data)) {
267 tevent_req_done(req);
268 return tevent_req_post(req, ev);
271 state->request.cmd = WINBINDD_DUAL_NDRCMD;
272 state->request.data.ndrcmd = state->opnum;
273 state->request.extra_data.data = (char *)state->in_data.data;
274 state->request.extra_len = state->in_data.length;
276 subreq = wb_child_request_send(state, ev, transport->child,
278 if (tevent_req_nomem(subreq, req)) {
279 return tevent_req_post(req, ev);
281 tevent_req_set_callback(subreq, wbint_bh_raw_call_done, req);
286 static void wbint_bh_raw_call_done(struct tevent_req *subreq)
288 struct tevent_req *req =
289 tevent_req_callback_data(subreq,
291 struct wbint_bh_raw_call_state *state =
293 struct wbint_bh_raw_call_state);
296 ret = wb_child_request_recv(subreq, state, &state->response, &err);
299 NTSTATUS status = map_nt_error_from_unix(err);
300 tevent_req_nterror(req, status);
304 state->out_data = data_blob_talloc(state,
305 state->response->extra_data.data,
306 state->response->length - sizeof(struct winbindd_response));
307 if (state->response->extra_data.data && !state->out_data.data) {
308 tevent_req_nomem(NULL, req);
312 if (state->domain != NULL) {
313 wcache_store_ndr(state->domain, state->opnum,
314 &state->in_data, &state->out_data);
317 tevent_req_done(req);
320 static NTSTATUS wbint_bh_raw_call_recv(struct tevent_req *req,
326 struct wbint_bh_raw_call_state *state =
328 struct wbint_bh_raw_call_state);
331 if (tevent_req_is_nterror(req, &status)) {
332 tevent_req_received(req);
336 *out_data = talloc_move(mem_ctx, &state->out_data.data);
337 *out_length = state->out_data.length;
339 tevent_req_received(req);
343 struct wbint_bh_disconnect_state {
347 static struct tevent_req *wbint_bh_disconnect_send(TALLOC_CTX *mem_ctx,
348 struct tevent_context *ev,
349 struct dcerpc_binding_handle *h)
351 struct wbint_bh_state *hs = dcerpc_binding_handle_data(h,
352 struct wbint_bh_state);
353 struct tevent_req *req;
354 struct wbint_bh_disconnect_state *state;
357 req = tevent_req_create(mem_ctx, &state,
358 struct wbint_bh_disconnect_state);
363 ok = wbint_bh_is_connected(h);
365 tevent_req_nterror(req, NT_STATUS_INVALID_CONNECTION);
366 return tevent_req_post(req, ev);
370 * TODO: do a real async disconnect ...
372 * For now the caller needs to free rpc_cli
376 tevent_req_done(req);
377 return tevent_req_post(req, ev);
380 static NTSTATUS wbint_bh_disconnect_recv(struct tevent_req *req)
384 if (tevent_req_is_nterror(req, &status)) {
385 tevent_req_received(req);
389 tevent_req_received(req);
393 static bool wbint_bh_ref_alloc(struct dcerpc_binding_handle *h)
398 static void wbint_bh_do_ndr_print(struct dcerpc_binding_handle *h,
400 const void *_struct_ptr,
401 const struct ndr_interface_call *call)
403 void *struct_ptr = discard_const(_struct_ptr);
405 if (DEBUGLEVEL < 10) {
409 if (ndr_flags & NDR_IN) {
410 ndr_print_function_debug(call->ndr_print,
415 if (ndr_flags & NDR_OUT) {
416 ndr_print_function_debug(call->ndr_print,
423 static const struct dcerpc_binding_handle_ops wbint_bh_ops = {
425 .is_connected = wbint_bh_is_connected,
426 .raw_call_send = wbint_bh_raw_call_send,
427 .raw_call_recv = wbint_bh_raw_call_recv,
428 .disconnect_send = wbint_bh_disconnect_send,
429 .disconnect_recv = wbint_bh_disconnect_recv,
431 .ref_alloc = wbint_bh_ref_alloc,
432 .do_ndr_print = wbint_bh_do_ndr_print,
435 /* initialise a wbint binding handle */
436 static struct dcerpc_binding_handle *wbint_binding_handle(struct rpc_pipe_client *rpc_cli)
438 struct dcerpc_binding_handle *h;
439 struct wbint_bh_state *hs;
441 h = dcerpc_binding_handle_create(rpc_cli,
446 struct wbint_bh_state,
451 hs->rpc_cli = rpc_cli;
456 struct rpc_pipe_client *wbint_rpccli_create(TALLOC_CTX *mem_ctx,
457 struct winbindd_domain *domain,
458 struct winbindd_child *child)
460 struct rpc_pipe_client *result;
461 struct wb_ndr_transport_priv *transp;
463 result = talloc(mem_ctx, struct rpc_pipe_client);
464 if (result == NULL) {
467 result->abstract_syntax = ndr_table_wbint.syntax_id;
468 result->transfer_syntax = ndr_transfer_syntax;
469 result->dispatch = wb_ndr_dispatch;
470 result->dispatch_send = wb_ndr_dispatch_send;
471 result->dispatch_recv = wb_ndr_dispatch_recv;
472 result->max_xmit_frag = RPC_MAX_PDU_FRAG_LEN;
473 result->max_recv_frag = RPC_MAX_PDU_FRAG_LEN;
474 result->desthost = NULL;
475 result->srv_name_slash = NULL;
478 * Initialize a fake transport. Due to our own wb_ndr_dispatch
479 * function we don't use all the fragmentation engine in
480 * cli_pipe, which would use all the _read and _write
481 * functions in rpc_cli_transport. But we need a place to
482 * store the child struct in, and we're re-using
483 * result->transport->priv for that.
486 result->transport = talloc_zero(result, struct rpc_cli_transport);
487 if (result->transport == NULL) {
491 transp = talloc(result->transport, struct wb_ndr_transport_priv);
492 if (transp == NULL) {
496 transp->domain = domain;
497 transp->child = child;
498 result->transport->priv = transp;
500 result->binding_handle = wbint_binding_handle(result);
501 if (result->binding_handle == NULL) {
509 enum winbindd_result winbindd_dual_ndrcmd(struct winbindd_domain *domain,
510 struct winbindd_cli_state *state)
512 struct pipes_struct p;
513 struct api_struct *fns;
517 wbint_get_pipe_fns(&fns, &num_fns);
519 if (state->request->data.ndrcmd >= num_fns) {
520 return WINBINDD_ERROR;
523 DEBUG(10, ("winbindd_dual_ndrcmd: Running command %s (%s)\n",
524 fns[state->request->data.ndrcmd].name,
525 domain ? domain->name : "no domain"));
528 p.mem_ctx = talloc_stackframe();
529 p.in_data.data = data_blob_const(state->request->extra_data.data,
530 state->request->extra_len);
532 ret = fns[state->request->data.ndrcmd].fn(&p);
534 TALLOC_FREE(p.mem_ctx);
535 return WINBINDD_ERROR;
538 state->response->extra_data.data =
539 talloc_move(state->mem_ctx, &p.out_data.rdata.data);
540 state->response->length += p.out_data.rdata.length;
541 p.out_data.rdata.length = 0;
543 TALLOC_FREE(p.mem_ctx);
545 if (state->response->extra_data.data == NULL) {
546 return WINBINDD_ERROR;