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