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