s3:winbindd: add wbint dcerpc_binding_handle backend
[amitay/samba.git] / source3 / winbindd / winbindd_dual_ndr.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Provide parent->child communication based on NDR marshalling
5
6    Copyright (C) Volker Lendecke 2009
7
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.
12
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.
17
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/>.
20 */
21
22 /*
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.
27  */
28
29 #include "includes.h"
30 #include "winbindd/winbindd.h"
31 #include "winbindd/winbindd_proto.h"
32 #include "librpc/gen_ndr/srv_wbint.h"
33
34 struct wb_ndr_transport_priv {
35         struct winbindd_domain *domain;
36         struct winbindd_child *child;
37 };
38
39 struct wb_ndr_dispatch_state {
40         struct wb_ndr_transport_priv *transport;
41         uint32_t opnum;
42         const struct ndr_interface_call *call;
43         void *r;
44         DATA_BLOB req_blob, resp_blob;
45         struct winbindd_request request;
46         struct winbindd_response *response;
47 };
48
49 static void wb_ndr_dispatch_done(struct tevent_req *subreq);
50
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,
55                                                uint32_t opnum,
56                                                void *r)
57 {
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;
64
65         req = tevent_req_create(mem_ctx, &state,
66                                 struct wb_ndr_dispatch_state);
67         if (req == NULL) {
68                 return NULL;
69         }
70
71         state->r = r;
72         state->call = &table->calls[opnum];
73         state->transport = transport;
74         state->opnum = opnum;
75
76         push = ndr_push_init_ctx(state);
77         if (tevent_req_nomem(push, req)) {
78                 return tevent_req_post(req, ev);
79         }
80
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));
84                 TALLOC_FREE(push);
85                 return tevent_req_post(req, ev);
86         }
87
88         state->req_blob = ndr_push_blob(push);
89
90         if ((transport->domain != NULL)
91             && wcache_fetch_ndr(state, transport->domain, opnum,
92                                 &state->req_blob, &state->resp_blob)) {
93                 tevent_req_done(req);
94                 return tevent_req_post(req, ev);
95         }
96
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;
101
102         subreq = wb_child_request_send(state, ev, transport->child,
103                                        &state->request);
104         if (tevent_req_nomem(subreq, req)) {
105                 return tevent_req_post(req, ev);
106         }
107         tevent_req_set_callback(subreq, wb_ndr_dispatch_done, req);
108         return req;
109 }
110
111 static void wb_ndr_dispatch_done(struct tevent_req *subreq)
112 {
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);
117         int ret, err;
118
119         ret = wb_child_request_recv(subreq, state, &state->response, &err);
120         TALLOC_FREE(subreq);
121         if (ret == -1) {
122                 tevent_req_nterror(req, map_nt_error_from_unix(err));
123                 return;
124         }
125
126         state->resp_blob = data_blob_const(
127                 state->response->extra_data.data,
128                 state->response->length - sizeof(struct winbindd_response));
129
130         if (state->transport->domain != NULL) {
131                 wcache_store_ndr(state->transport->domain, state->opnum,
132                                  &state->req_blob, &state->resp_blob);
133         }
134
135         tevent_req_done(req);
136 }
137
138 static NTSTATUS wb_ndr_dispatch_recv(struct tevent_req *req,
139                                      TALLOC_CTX *mem_ctx)
140 {
141         struct wb_ndr_dispatch_state *state = tevent_req_data(
142                 req, struct wb_ndr_dispatch_state);
143         NTSTATUS status;
144         struct ndr_pull *pull;
145         enum ndr_err_code ndr_err;
146
147         if (tevent_req_is_nterror(req, &status)) {
148                 return status;
149         }
150
151         pull = ndr_pull_init_blob(&state->resp_blob, mem_ctx);
152         if (pull == NULL) {
153                 return NT_STATUS_NO_MEMORY;
154         }
155
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);
159         TALLOC_FREE(pull);
160
161         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
162                 return ndr_map_error2ntstatus(ndr_err);
163         }
164
165         return NT_STATUS_OK;
166 }
167
168 static NTSTATUS wb_ndr_dispatch(struct rpc_pipe_client *cli,
169                                 TALLOC_CTX *mem_ctx,
170                                 const struct ndr_interface_table *table,
171                                 uint32_t opnum, void *r)
172 {
173         TALLOC_CTX *frame = talloc_stackframe();
174         struct event_context *ev;
175         struct tevent_req *req;
176         NTSTATUS status = NT_STATUS_OK;
177
178         ev = event_context_init(frame);
179         if (ev == NULL) {
180                 status = NT_STATUS_NO_MEMORY;
181                 goto fail;
182         }
183
184         req = wb_ndr_dispatch_send(frame, ev, cli, table, opnum, r);
185         if (req == NULL) {
186                 status = NT_STATUS_NO_MEMORY;
187                 goto fail;
188         }
189
190         if (!tevent_req_poll(req, ev)) {
191                 status = map_nt_error_from_unix(errno);
192                 goto fail;
193         }
194
195         status = wb_ndr_dispatch_recv(req, mem_ctx);
196  fail:
197         TALLOC_FREE(frame);
198         return status;
199 }
200
201 struct wbint_bh_state {
202         struct rpc_pipe_client *rpc_cli;
203 };
204
205 static bool wbint_bh_is_connected(struct dcerpc_binding_handle *h)
206 {
207         struct wbint_bh_state *hs = dcerpc_binding_handle_data(h,
208                                      struct wbint_bh_state);
209
210         if (!hs->rpc_cli) {
211                 return false;
212         }
213
214         return true;
215 }
216
217 struct wbint_bh_raw_call_state {
218         struct winbindd_domain *domain;
219         uint32_t opnum;
220         DATA_BLOB in_data;
221         struct winbindd_request request;
222         struct winbindd_response *response;
223         DATA_BLOB out_data;
224 };
225
226 static void wbint_bh_raw_call_done(struct tevent_req *subreq);
227
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,
232                                                   uint32_t opnum,
233                                                   uint32_t in_flags,
234                                                   const uint8_t *in_data,
235                                                   size_t in_length)
236 {
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;
245         bool ok;
246         struct tevent_req *subreq;
247
248         req = tevent_req_create(mem_ctx, &state,
249                                 struct wbint_bh_raw_call_state);
250         if (req == NULL) {
251                 return NULL;
252         }
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;
257
258         ok = wbint_bh_is_connected(h);
259         if (!ok) {
260                 tevent_req_nterror(req, NT_STATUS_INVALID_CONNECTION);
261                 return tevent_req_post(req, ev);
262         }
263
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);
269         }
270
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;
275
276         subreq = wb_child_request_send(state, ev, transport->child,
277                                        &state->request);
278         if (tevent_req_nomem(subreq, req)) {
279                 return tevent_req_post(req, ev);
280         }
281         tevent_req_set_callback(subreq, wbint_bh_raw_call_done, req);
282
283         return req;
284 }
285
286 static void wbint_bh_raw_call_done(struct tevent_req *subreq)
287 {
288         struct tevent_req *req =
289                 tevent_req_callback_data(subreq,
290                 struct tevent_req);
291         struct wbint_bh_raw_call_state *state =
292                 tevent_req_data(req,
293                 struct wbint_bh_raw_call_state);
294         int ret, err;
295
296         ret = wb_child_request_recv(subreq, state, &state->response, &err);
297         TALLOC_FREE(subreq);
298         if (ret == -1) {
299                 NTSTATUS status = map_nt_error_from_unix(err);
300                 tevent_req_nterror(req, status);
301                 return;
302         }
303
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);
309                 return;
310         }
311
312         if (state->domain != NULL) {
313                 wcache_store_ndr(state->domain, state->opnum,
314                                  &state->in_data, &state->out_data);
315         }
316
317         tevent_req_done(req);
318 }
319
320 static NTSTATUS wbint_bh_raw_call_recv(struct tevent_req *req,
321                                         TALLOC_CTX *mem_ctx,
322                                         uint8_t **out_data,
323                                         size_t *out_length,
324                                         uint32_t *out_flags)
325 {
326         struct wbint_bh_raw_call_state *state =
327                 tevent_req_data(req,
328                 struct wbint_bh_raw_call_state);
329         NTSTATUS status;
330
331         if (tevent_req_is_nterror(req, &status)) {
332                 tevent_req_received(req);
333                 return status;
334         }
335
336         *out_data = talloc_move(mem_ctx, &state->out_data.data);
337         *out_length = state->out_data.length;
338         *out_flags = 0;
339         tevent_req_received(req);
340         return NT_STATUS_OK;
341 }
342
343 struct wbint_bh_disconnect_state {
344         uint8_t _dummy;
345 };
346
347 static struct tevent_req *wbint_bh_disconnect_send(TALLOC_CTX *mem_ctx,
348                                                 struct tevent_context *ev,
349                                                 struct dcerpc_binding_handle *h)
350 {
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;
355         bool ok;
356
357         req = tevent_req_create(mem_ctx, &state,
358                                 struct wbint_bh_disconnect_state);
359         if (req == NULL) {
360                 return NULL;
361         }
362
363         ok = wbint_bh_is_connected(h);
364         if (!ok) {
365                 tevent_req_nterror(req, NT_STATUS_INVALID_CONNECTION);
366                 return tevent_req_post(req, ev);
367         }
368
369         /*
370          * TODO: do a real async disconnect ...
371          *
372          * For now the caller needs to free rpc_cli
373          */
374         hs->rpc_cli = NULL;
375
376         tevent_req_done(req);
377         return tevent_req_post(req, ev);
378 }
379
380 static NTSTATUS wbint_bh_disconnect_recv(struct tevent_req *req)
381 {
382         NTSTATUS status;
383
384         if (tevent_req_is_nterror(req, &status)) {
385                 tevent_req_received(req);
386                 return status;
387         }
388
389         tevent_req_received(req);
390         return NT_STATUS_OK;
391 }
392
393 static bool wbint_bh_ref_alloc(struct dcerpc_binding_handle *h)
394 {
395         return true;
396 }
397
398 static void wbint_bh_do_ndr_print(struct dcerpc_binding_handle *h,
399                                   int ndr_flags,
400                                   const void *_struct_ptr,
401                                   const struct ndr_interface_call *call)
402 {
403         void *struct_ptr = discard_const(_struct_ptr);
404
405         if (DEBUGLEVEL < 10) {
406                 return;
407         }
408
409         if (ndr_flags & NDR_IN) {
410                 ndr_print_function_debug(call->ndr_print,
411                                          call->name,
412                                          ndr_flags,
413                                          struct_ptr);
414         }
415         if (ndr_flags & NDR_OUT) {
416                 ndr_print_function_debug(call->ndr_print,
417                                          call->name,
418                                          ndr_flags,
419                                          struct_ptr);
420         }
421 }
422
423 static const struct dcerpc_binding_handle_ops wbint_bh_ops = {
424         .name                   = "wbint",
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,
430
431         .ref_alloc              = wbint_bh_ref_alloc,
432         .do_ndr_print           = wbint_bh_do_ndr_print,
433 };
434
435 /* initialise a wbint binding handle */
436 static struct dcerpc_binding_handle *wbint_binding_handle(struct rpc_pipe_client *rpc_cli)
437 {
438         struct dcerpc_binding_handle *h;
439         struct wbint_bh_state *hs;
440
441         h = dcerpc_binding_handle_create(rpc_cli,
442                                          &wbint_bh_ops,
443                                          NULL,
444                                          NULL, /* TODO */
445                                          &hs,
446                                          struct wbint_bh_state,
447                                          __location__);
448         if (h == NULL) {
449                 return NULL;
450         }
451         hs->rpc_cli = rpc_cli;
452
453         return h;
454 }
455
456 struct rpc_pipe_client *wbint_rpccli_create(TALLOC_CTX *mem_ctx,
457                                             struct winbindd_domain *domain,
458                                             struct winbindd_child *child)
459 {
460         struct rpc_pipe_client *result;
461         struct wb_ndr_transport_priv *transp;
462
463         result = talloc(mem_ctx, struct rpc_pipe_client);
464         if (result == NULL) {
465                 return NULL;
466         }
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;
476
477         /*
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.
484          */
485
486         result->transport = talloc_zero(result, struct rpc_cli_transport);
487         if (result->transport == NULL) {
488                 TALLOC_FREE(result);
489                 return NULL;
490         }
491         transp = talloc(result->transport, struct wb_ndr_transport_priv);
492         if (transp == NULL) {
493                 TALLOC_FREE(result);
494                 return NULL;
495         }
496         transp->domain = domain;
497         transp->child = child;
498         result->transport->priv = transp;
499
500         result->binding_handle = wbint_binding_handle(result);
501         if (result->binding_handle == NULL) {
502                 TALLOC_FREE(result);
503                 return NULL;
504         }
505
506         return result;
507 }
508
509 enum winbindd_result winbindd_dual_ndrcmd(struct winbindd_domain *domain,
510                                           struct winbindd_cli_state *state)
511 {
512         struct pipes_struct p;
513         struct api_struct *fns;
514         int num_fns;
515         bool ret;
516
517         wbint_get_pipe_fns(&fns, &num_fns);
518
519         if (state->request->data.ndrcmd >= num_fns) {
520                 return WINBINDD_ERROR;
521         }
522
523         DEBUG(10, ("winbindd_dual_ndrcmd: Running command %s (%s)\n",
524                    fns[state->request->data.ndrcmd].name,
525                    domain ? domain->name : "no domain"));
526
527         ZERO_STRUCT(p);
528         p.mem_ctx = talloc_stackframe();
529         p.in_data.data = data_blob_const(state->request->extra_data.data,
530                                          state->request->extra_len);
531
532         ret = fns[state->request->data.ndrcmd].fn(&p);
533         if (!ret) {
534                 TALLOC_FREE(p.mem_ctx);
535                 return WINBINDD_ERROR;
536         }
537
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;
542
543         TALLOC_FREE(p.mem_ctx);
544
545         if (state->response->extra_data.data == NULL) {
546                 return WINBINDD_ERROR;
547         }
548         return WINBINDD_OK;
549 }