s3:winbindd: Dispatch RPC calls through interface local handler
[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 "ntdomain.h"
33 #include "librpc/rpc/dcesrv_core.h"
34 #include "librpc/gen_ndr/ndr_winbind.h"
35
36 struct wbint_bh_state {
37         struct winbindd_domain *domain;
38         struct winbindd_child *child;
39 };
40
41 static bool wbint_bh_is_connected(struct dcerpc_binding_handle *h)
42 {
43         struct wbint_bh_state *hs = dcerpc_binding_handle_data(h,
44                                      struct wbint_bh_state);
45
46         if ((hs->domain == NULL) && (hs->child == NULL)) {
47                 return false;
48         }
49
50         return true;
51 }
52
53 static uint32_t wbint_bh_set_timeout(struct dcerpc_binding_handle *h,
54                                      uint32_t timeout)
55 {
56         /* TODO: implement timeouts */
57         return UINT32_MAX;
58 }
59
60 struct wbint_bh_raw_call_state {
61         struct winbindd_domain *domain;
62         uint32_t opnum;
63         DATA_BLOB in_data;
64         struct winbindd_request request;
65         struct winbindd_response *response;
66         DATA_BLOB out_data;
67 };
68
69 static void wbint_bh_raw_call_child_done(struct tevent_req *subreq);
70 static void wbint_bh_raw_call_domain_done(struct tevent_req *subreq);
71
72 static struct tevent_req *wbint_bh_raw_call_send(TALLOC_CTX *mem_ctx,
73                                                   struct tevent_context *ev,
74                                                   struct dcerpc_binding_handle *h,
75                                                   const struct GUID *object,
76                                                   uint32_t opnum,
77                                                   uint32_t in_flags,
78                                                   const uint8_t *in_data,
79                                                   size_t in_length)
80 {
81         struct wbint_bh_state *hs =
82                 dcerpc_binding_handle_data(h,
83                 struct wbint_bh_state);
84         struct tevent_req *req;
85         struct wbint_bh_raw_call_state *state;
86         bool ok;
87         struct tevent_req *subreq;
88
89         req = tevent_req_create(mem_ctx, &state,
90                                 struct wbint_bh_raw_call_state);
91         if (req == NULL) {
92                 return NULL;
93         }
94         state->domain = hs->domain;
95         state->opnum = opnum;
96         state->in_data.data = discard_const_p(uint8_t, in_data);
97         state->in_data.length = in_length;
98
99         ok = wbint_bh_is_connected(h);
100         if (!ok) {
101                 tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
102                 return tevent_req_post(req, ev);
103         }
104
105         if ((state->domain != NULL)
106             && wcache_fetch_ndr(state, state->domain, state->opnum,
107                                 &state->in_data, &state->out_data)) {
108                 DBG_DEBUG("Got opnum %"PRIu32" for domain %s from cache\n",
109                           state->opnum, state->domain->name);
110                 tevent_req_done(req);
111                 return tevent_req_post(req, ev);
112         }
113
114         state->request.cmd = WINBINDD_DUAL_NDRCMD;
115         state->request.data.ndrcmd = state->opnum;
116         state->request.extra_data.data = (char *)state->in_data.data;
117         state->request.extra_len = state->in_data.length;
118
119         if (hs->child != NULL) {
120                 subreq = wb_child_request_send(state, ev, hs->child,
121                                                &state->request);
122                 if (tevent_req_nomem(subreq, req)) {
123                         return tevent_req_post(req, ev);
124                 }
125                 tevent_req_set_callback(
126                         subreq, wbint_bh_raw_call_child_done, req);
127                 return req;
128         }
129
130         subreq = wb_domain_request_send(state, ev, hs->domain,
131                                         &state->request);
132         if (tevent_req_nomem(subreq, req)) {
133                 return tevent_req_post(req, ev);
134         }
135         tevent_req_set_callback(subreq, wbint_bh_raw_call_domain_done, req);
136
137         return req;
138 }
139
140 static void wbint_bh_raw_call_child_done(struct tevent_req *subreq)
141 {
142         struct tevent_req *req =
143                 tevent_req_callback_data(subreq,
144                 struct tevent_req);
145         struct wbint_bh_raw_call_state *state =
146                 tevent_req_data(req,
147                 struct wbint_bh_raw_call_state);
148         int ret, err;
149
150         ret = wb_child_request_recv(subreq, state, &state->response, &err);
151         TALLOC_FREE(subreq);
152         if (ret == -1) {
153                 NTSTATUS status = map_nt_error_from_unix(err);
154                 tevent_req_nterror(req, status);
155                 return;
156         }
157
158         state->out_data = data_blob_talloc(state,
159                 state->response->extra_data.data,
160                 state->response->length - sizeof(struct winbindd_response));
161         if (state->response->extra_data.data && !state->out_data.data) {
162                 tevent_req_oom(req);
163                 return;
164         }
165
166         if (state->domain != NULL) {
167                 wcache_store_ndr(state->domain, state->opnum,
168                                  &state->in_data, &state->out_data);
169         }
170
171         tevent_req_done(req);
172 }
173
174 static void wbint_bh_raw_call_domain_done(struct tevent_req *subreq)
175 {
176         struct tevent_req *req =
177                 tevent_req_callback_data(subreq,
178                 struct tevent_req);
179         struct wbint_bh_raw_call_state *state =
180                 tevent_req_data(req,
181                 struct wbint_bh_raw_call_state);
182         int ret, err;
183
184         ret = wb_domain_request_recv(subreq, state, &state->response, &err);
185         TALLOC_FREE(subreq);
186         if (ret == -1) {
187                 NTSTATUS status = map_nt_error_from_unix(err);
188                 tevent_req_nterror(req, status);
189                 return;
190         }
191
192         state->out_data = data_blob_talloc(state,
193                 state->response->extra_data.data,
194                 state->response->length - sizeof(struct winbindd_response));
195         if (state->response->extra_data.data && !state->out_data.data) {
196                 tevent_req_oom(req);
197                 return;
198         }
199
200         if (state->domain != NULL) {
201                 wcache_store_ndr(state->domain, state->opnum,
202                                  &state->in_data, &state->out_data);
203         }
204
205         tevent_req_done(req);
206 }
207
208 static NTSTATUS wbint_bh_raw_call_recv(struct tevent_req *req,
209                                         TALLOC_CTX *mem_ctx,
210                                         uint8_t **out_data,
211                                         size_t *out_length,
212                                         uint32_t *out_flags)
213 {
214         struct wbint_bh_raw_call_state *state =
215                 tevent_req_data(req,
216                 struct wbint_bh_raw_call_state);
217         NTSTATUS status;
218
219         if (tevent_req_is_nterror(req, &status)) {
220                 tevent_req_received(req);
221                 return status;
222         }
223
224         *out_data = talloc_move(mem_ctx, &state->out_data.data);
225         *out_length = state->out_data.length;
226         *out_flags = 0;
227         tevent_req_received(req);
228         return NT_STATUS_OK;
229 }
230
231 struct wbint_bh_disconnect_state {
232         uint8_t _dummy;
233 };
234
235 static struct tevent_req *wbint_bh_disconnect_send(TALLOC_CTX *mem_ctx,
236                                                 struct tevent_context *ev,
237                                                 struct dcerpc_binding_handle *h)
238 {
239         struct wbint_bh_state *hs = dcerpc_binding_handle_data(h,
240                                      struct wbint_bh_state);
241         struct tevent_req *req;
242         struct wbint_bh_disconnect_state *state;
243         bool ok;
244
245         req = tevent_req_create(mem_ctx, &state,
246                                 struct wbint_bh_disconnect_state);
247         if (req == NULL) {
248                 return NULL;
249         }
250
251         ok = wbint_bh_is_connected(h);
252         if (!ok) {
253                 tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
254                 return tevent_req_post(req, ev);
255         }
256
257         /*
258          * TODO: do a real async disconnect ...
259          */
260         hs->domain = NULL;
261         hs->child = NULL;
262
263         tevent_req_done(req);
264         return tevent_req_post(req, ev);
265 }
266
267 static NTSTATUS wbint_bh_disconnect_recv(struct tevent_req *req)
268 {
269         NTSTATUS status;
270
271         if (tevent_req_is_nterror(req, &status)) {
272                 tevent_req_received(req);
273                 return status;
274         }
275
276         tevent_req_received(req);
277         return NT_STATUS_OK;
278 }
279
280 static bool wbint_bh_ref_alloc(struct dcerpc_binding_handle *h)
281 {
282         return true;
283 }
284
285 static void wbint_bh_do_ndr_print(struct dcerpc_binding_handle *h,
286                                   int ndr_flags,
287                                   const void *_struct_ptr,
288                                   const struct ndr_interface_call *call)
289 {
290         void *struct_ptr = discard_const(_struct_ptr);
291
292         if (DEBUGLEVEL < 10) {
293                 return;
294         }
295
296         if (ndr_flags & NDR_IN) {
297                 ndr_print_function_debug(call->ndr_print,
298                                          call->name,
299                                          ndr_flags,
300                                          struct_ptr);
301         }
302         if (ndr_flags & NDR_OUT) {
303                 ndr_print_function_debug(call->ndr_print,
304                                          call->name,
305                                          ndr_flags,
306                                          struct_ptr);
307         }
308 }
309
310 static const struct dcerpc_binding_handle_ops wbint_bh_ops = {
311         .name                   = "wbint",
312         .is_connected           = wbint_bh_is_connected,
313         .set_timeout            = wbint_bh_set_timeout,
314         .raw_call_send          = wbint_bh_raw_call_send,
315         .raw_call_recv          = wbint_bh_raw_call_recv,
316         .disconnect_send        = wbint_bh_disconnect_send,
317         .disconnect_recv        = wbint_bh_disconnect_recv,
318
319         .ref_alloc              = wbint_bh_ref_alloc,
320         .do_ndr_print           = wbint_bh_do_ndr_print,
321 };
322
323 /* initialise a wbint binding handle */
324 struct dcerpc_binding_handle *wbint_binding_handle(TALLOC_CTX *mem_ctx,
325                                                 struct winbindd_domain *domain,
326                                                 struct winbindd_child *child)
327 {
328         struct dcerpc_binding_handle *h;
329         struct wbint_bh_state *hs;
330
331         h = dcerpc_binding_handle_create(mem_ctx,
332                                          &wbint_bh_ops,
333                                          NULL,
334                                          &ndr_table_winbind,
335                                          &hs,
336                                          struct wbint_bh_state,
337                                          __location__);
338         if (h == NULL) {
339                 return NULL;
340         }
341         hs->domain = domain;
342         hs->child = child;
343
344         return h;
345 }
346
347 enum winbindd_result winbindd_dual_ndrcmd(struct winbindd_domain *domain,
348                                           struct winbindd_cli_state *state)
349 {
350         const struct dcesrv_endpoint_server *ep_server = NULL;
351         struct dcesrv_interface iface;
352         const struct ndr_syntax_id *abstract_syntax;
353         bool ok;
354         uint32_t opnum = state->request->data.ndrcmd;
355         struct pipes_struct *p;
356         TALLOC_CTX *mem_ctx;
357         DATA_BLOB in;
358         DATA_BLOB out;
359         NTSTATUS status;
360
361         DBG_DEBUG("Running command %s (domain '%s')\n",
362                   ndr_table_winbind.calls[opnum].name,
363                   domain ? domain->name : "(null)");
364
365         ep_server = dcesrv_ep_server_byname(ndr_table_winbind.name);
366         if (ep_server == NULL) {
367                 DBG_ERR("Failed to get DCE/RPC endpoint server '%s'\n",
368                         ndr_table_winbind.name);
369                 return WINBINDD_ERROR;
370         }
371
372         abstract_syntax = &ndr_table_winbind.syntax_id;
373         ok = ep_server->interface_by_uuid(&iface, &abstract_syntax->uuid,
374                                           abstract_syntax->if_version);
375         if (!ok) {
376                 DBG_ERR("Failed to get DCE/RPC interface\n");
377                 return WINBINDD_ERROR;
378         }
379
380         mem_ctx = talloc_stackframe();
381         if (mem_ctx == NULL) {
382                 DBG_ERR("No memory");
383                 return WINBINDD_ERROR;
384         }
385
386         p = talloc_zero(mem_ctx, struct pipes_struct);
387         if (p == NULL) {
388                 DBG_ERR("No memory\n");
389                 return WINBINDD_ERROR;
390         }
391         p->mem_ctx = mem_ctx;
392
393         in = data_blob_const(state->request->extra_data.data,
394                              state->request->extra_len);
395
396         status = iface.local(p, opnum, mem_ctx, &in, &out);
397         if (!NT_STATUS_IS_OK(status)) {
398                 TALLOC_FREE(mem_ctx);
399                 return WINBINDD_ERROR;
400         }
401
402         state->response->extra_data.data =
403                 talloc_steal(state->mem_ctx, out.data);
404         state->response->length += out.length;
405
406         TALLOC_FREE(mem_ctx);
407
408         if (state->response->extra_data.data == NULL) {
409                 return WINBINDD_ERROR;
410         }
411
412         return WINBINDD_OK;
413 }