ed2b5ea22da830478370b3558cab89b5a19e0c99
[gd/samba-autobuild/.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->child) {
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_done(struct tevent_req *subreq);
69
70 static struct tevent_req *wbint_bh_raw_call_send(TALLOC_CTX *mem_ctx,
71                                                   struct tevent_context *ev,
72                                                   struct dcerpc_binding_handle *h,
73                                                   const struct GUID *object,
74                                                   uint32_t opnum,
75                                                   uint32_t in_flags,
76                                                   const uint8_t *in_data,
77                                                   size_t in_length)
78 {
79         struct wbint_bh_state *hs =
80                 dcerpc_binding_handle_data(h,
81                 struct wbint_bh_state);
82         struct tevent_req *req;
83         struct wbint_bh_raw_call_state *state;
84         bool ok;
85         struct tevent_req *subreq;
86
87         req = tevent_req_create(mem_ctx, &state,
88                                 struct wbint_bh_raw_call_state);
89         if (req == NULL) {
90                 return NULL;
91         }
92         state->domain = hs->domain;
93         state->opnum = opnum;
94         state->in_data.data = discard_const_p(uint8_t, in_data);
95         state->in_data.length = in_length;
96
97         ok = wbint_bh_is_connected(h);
98         if (!ok) {
99                 tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
100                 return tevent_req_post(req, ev);
101         }
102
103         if ((state->domain != NULL)
104             && wcache_fetch_ndr(state, state->domain, state->opnum,
105                                 &state->in_data, &state->out_data)) {
106                 tevent_req_done(req);
107                 return tevent_req_post(req, ev);
108         }
109
110         state->request.cmd = WINBINDD_DUAL_NDRCMD;
111         state->request.data.ndrcmd = state->opnum;
112         state->request.extra_data.data = (char *)state->in_data.data;
113         state->request.extra_len = state->in_data.length;
114
115         subreq = wb_child_request_send(state, ev, hs->child,
116                                        &state->request);
117         if (tevent_req_nomem(subreq, req)) {
118                 return tevent_req_post(req, ev);
119         }
120         tevent_req_set_callback(subreq, wbint_bh_raw_call_done, req);
121
122         return req;
123 }
124
125 static void wbint_bh_raw_call_done(struct tevent_req *subreq)
126 {
127         struct tevent_req *req =
128                 tevent_req_callback_data(subreq,
129                 struct tevent_req);
130         struct wbint_bh_raw_call_state *state =
131                 tevent_req_data(req,
132                 struct wbint_bh_raw_call_state);
133         int ret, err;
134
135         ret = wb_child_request_recv(subreq, state, &state->response, &err);
136         TALLOC_FREE(subreq);
137         if (ret == -1) {
138                 NTSTATUS status = map_nt_error_from_unix(err);
139                 tevent_req_nterror(req, status);
140                 return;
141         }
142
143         state->out_data = data_blob_talloc(state,
144                 state->response->extra_data.data,
145                 state->response->length - sizeof(struct winbindd_response));
146         if (state->response->extra_data.data && !state->out_data.data) {
147                 tevent_req_oom(req);
148                 return;
149         }
150
151         if (state->domain != NULL) {
152                 wcache_store_ndr(state->domain, state->opnum,
153                                  &state->in_data, &state->out_data);
154         }
155
156         tevent_req_done(req);
157 }
158
159 static NTSTATUS wbint_bh_raw_call_recv(struct tevent_req *req,
160                                         TALLOC_CTX *mem_ctx,
161                                         uint8_t **out_data,
162                                         size_t *out_length,
163                                         uint32_t *out_flags)
164 {
165         struct wbint_bh_raw_call_state *state =
166                 tevent_req_data(req,
167                 struct wbint_bh_raw_call_state);
168         NTSTATUS status;
169
170         if (tevent_req_is_nterror(req, &status)) {
171                 tevent_req_received(req);
172                 return status;
173         }
174
175         *out_data = talloc_move(mem_ctx, &state->out_data.data);
176         *out_length = state->out_data.length;
177         *out_flags = 0;
178         tevent_req_received(req);
179         return NT_STATUS_OK;
180 }
181
182 struct wbint_bh_disconnect_state {
183         uint8_t _dummy;
184 };
185
186 static struct tevent_req *wbint_bh_disconnect_send(TALLOC_CTX *mem_ctx,
187                                                 struct tevent_context *ev,
188                                                 struct dcerpc_binding_handle *h)
189 {
190         struct wbint_bh_state *hs = dcerpc_binding_handle_data(h,
191                                      struct wbint_bh_state);
192         struct tevent_req *req;
193         struct wbint_bh_disconnect_state *state;
194         bool ok;
195
196         req = tevent_req_create(mem_ctx, &state,
197                                 struct wbint_bh_disconnect_state);
198         if (req == NULL) {
199                 return NULL;
200         }
201
202         ok = wbint_bh_is_connected(h);
203         if (!ok) {
204                 tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
205                 return tevent_req_post(req, ev);
206         }
207
208         /*
209          * TODO: do a real async disconnect ...
210          *
211          * For now the caller needs to free rpc_cli
212          */
213         hs->child = NULL;
214
215         tevent_req_done(req);
216         return tevent_req_post(req, ev);
217 }
218
219 static NTSTATUS wbint_bh_disconnect_recv(struct tevent_req *req)
220 {
221         NTSTATUS status;
222
223         if (tevent_req_is_nterror(req, &status)) {
224                 tevent_req_received(req);
225                 return status;
226         }
227
228         tevent_req_received(req);
229         return NT_STATUS_OK;
230 }
231
232 static bool wbint_bh_ref_alloc(struct dcerpc_binding_handle *h)
233 {
234         return true;
235 }
236
237 static void wbint_bh_do_ndr_print(struct dcerpc_binding_handle *h,
238                                   int ndr_flags,
239                                   const void *_struct_ptr,
240                                   const struct ndr_interface_call *call)
241 {
242         void *struct_ptr = discard_const(_struct_ptr);
243
244         if (DEBUGLEVEL < 10) {
245                 return;
246         }
247
248         if (ndr_flags & NDR_IN) {
249                 ndr_print_function_debug(call->ndr_print,
250                                          call->name,
251                                          ndr_flags,
252                                          struct_ptr);
253         }
254         if (ndr_flags & NDR_OUT) {
255                 ndr_print_function_debug(call->ndr_print,
256                                          call->name,
257                                          ndr_flags,
258                                          struct_ptr);
259         }
260 }
261
262 static const struct dcerpc_binding_handle_ops wbint_bh_ops = {
263         .name                   = "wbint",
264         .is_connected           = wbint_bh_is_connected,
265         .set_timeout            = wbint_bh_set_timeout,
266         .raw_call_send          = wbint_bh_raw_call_send,
267         .raw_call_recv          = wbint_bh_raw_call_recv,
268         .disconnect_send        = wbint_bh_disconnect_send,
269         .disconnect_recv        = wbint_bh_disconnect_recv,
270
271         .ref_alloc              = wbint_bh_ref_alloc,
272         .do_ndr_print           = wbint_bh_do_ndr_print,
273 };
274
275 /* initialise a wbint binding handle */
276 struct dcerpc_binding_handle *wbint_binding_handle(TALLOC_CTX *mem_ctx,
277                                                 struct winbindd_domain *domain,
278                                                 struct winbindd_child *child)
279 {
280         struct dcerpc_binding_handle *h;
281         struct wbint_bh_state *hs;
282
283         h = dcerpc_binding_handle_create(mem_ctx,
284                                          &wbint_bh_ops,
285                                          NULL,
286                                          &ndr_table_winbind,
287                                          &hs,
288                                          struct wbint_bh_state,
289                                          __location__);
290         if (h == NULL) {
291                 return NULL;
292         }
293         hs->domain = domain;
294         hs->child = child;
295
296         return h;
297 }
298
299 enum winbindd_result winbindd_dual_ndrcmd(struct winbindd_domain *domain,
300                                           struct winbindd_cli_state *state)
301 {
302         struct pipes_struct p;
303         struct api_struct *fns;
304         int num_fns;
305         bool ret;
306
307         winbind_get_pipe_fns(&fns, &num_fns);
308
309         if (state->request->data.ndrcmd >= num_fns) {
310                 return WINBINDD_ERROR;
311         }
312
313         DEBUG(10, ("winbindd_dual_ndrcmd: Running command %s (%s)\n",
314                    fns[state->request->data.ndrcmd].name,
315                    domain ? domain->name : "no domain"));
316
317         ZERO_STRUCT(p);
318         p.mem_ctx = talloc_stackframe();
319         p.in_data.data = data_blob_const(state->request->extra_data.data,
320                                          state->request->extra_len);
321
322         ret = fns[state->request->data.ndrcmd].fn(&p);
323         if (!ret) {
324                 TALLOC_FREE(p.mem_ctx);
325                 return WINBINDD_ERROR;
326         }
327
328         state->response->extra_data.data =
329                 talloc_move(state->mem_ctx, &p.out_data.rdata.data);
330         state->response->length += p.out_data.rdata.length;
331         p.out_data.rdata.length = 0;
332
333         TALLOC_FREE(p.mem_ctx);
334
335         if (state->response->extra_data.data == NULL) {
336                 return WINBINDD_ERROR;
337         }
338         return WINBINDD_OK;
339 }