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