librpc:ndr: Introduce ‘ndr_flags_type’ type
[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/rpc/dcesrv_core.h"
34 #include "librpc/gen_ndr/ndr_winbind.h"
35 #include "rpc_server/rpc_config.h"
36 #include "rpc_server/rpc_server.h"
37 #include "rpc_dce.h"
38 #include "lib/tsocket/tsocket.h"
39
40 struct wbint_bh_state {
41         struct winbindd_domain *domain;
42         struct winbindd_child *child;
43 };
44
45 static bool wbint_bh_is_connected(struct dcerpc_binding_handle *h)
46 {
47         struct wbint_bh_state *hs = dcerpc_binding_handle_data(h,
48                                      struct wbint_bh_state);
49
50         if ((hs->domain == NULL) && (hs->child == NULL)) {
51                 return false;
52         }
53
54         return true;
55 }
56
57 static uint32_t wbint_bh_set_timeout(struct dcerpc_binding_handle *h,
58                                      uint32_t timeout)
59 {
60         /* TODO: implement timeouts */
61         return UINT32_MAX;
62 }
63
64 struct wbint_bh_raw_call_state {
65         struct winbindd_domain *domain;
66         uint32_t opnum;
67         DATA_BLOB in_data;
68         struct winbindd_request request;
69         struct winbindd_response *response;
70         DATA_BLOB out_data;
71 };
72
73 static void wbint_bh_raw_call_child_done(struct tevent_req *subreq);
74 static void wbint_bh_raw_call_domain_done(struct tevent_req *subreq);
75
76 static struct tevent_req *wbint_bh_raw_call_send(TALLOC_CTX *mem_ctx,
77                                                   struct tevent_context *ev,
78                                                   struct dcerpc_binding_handle *h,
79                                                   const struct GUID *object,
80                                                   uint32_t opnum,
81                                                   uint32_t in_flags,
82                                                   const uint8_t *in_data,
83                                                   size_t in_length)
84 {
85         struct wbint_bh_state *hs =
86                 dcerpc_binding_handle_data(h,
87                 struct wbint_bh_state);
88         struct tevent_req *req;
89         struct wbint_bh_raw_call_state *state;
90         bool ok;
91         struct tevent_req *subreq;
92
93         req = tevent_req_create(mem_ctx, &state,
94                                 struct wbint_bh_raw_call_state);
95         if (req == NULL) {
96                 return NULL;
97         }
98         state->domain = hs->domain;
99         state->opnum = opnum;
100         state->in_data.data = discard_const_p(uint8_t, in_data);
101         state->in_data.length = in_length;
102
103         ok = wbint_bh_is_connected(h);
104         if (!ok) {
105                 tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
106                 return tevent_req_post(req, ev);
107         }
108
109         if ((state->domain != NULL)
110             && wcache_fetch_ndr(state, state->domain, state->opnum,
111                                 &state->in_data, &state->out_data)) {
112                 DBG_DEBUG("Got opnum %"PRIu32" for domain %s from cache\n",
113                           state->opnum, state->domain->name);
114                 tevent_req_done(req);
115                 return tevent_req_post(req, ev);
116         }
117
118         state->request.cmd = WINBINDD_DUAL_NDRCMD;
119         state->request.data.ndrcmd = state->opnum;
120         state->request.extra_data.data = (char *)state->in_data.data;
121         state->request.extra_len = state->in_data.length;
122
123         if (hs->child != NULL) {
124                 subreq = wb_child_request_send(state, ev, hs->child,
125                                                &state->request);
126                 if (tevent_req_nomem(subreq, req)) {
127                         return tevent_req_post(req, ev);
128                 }
129                 tevent_req_set_callback(
130                         subreq, wbint_bh_raw_call_child_done, req);
131                 return req;
132         }
133
134         subreq = wb_domain_request_send(state, ev, hs->domain,
135                                         &state->request);
136         if (tevent_req_nomem(subreq, req)) {
137                 return tevent_req_post(req, ev);
138         }
139         tevent_req_set_callback(subreq, wbint_bh_raw_call_domain_done, req);
140
141         return req;
142 }
143
144 static void wbint_bh_raw_call_child_done(struct tevent_req *subreq)
145 {
146         struct tevent_req *req =
147                 tevent_req_callback_data(subreq,
148                 struct tevent_req);
149         struct wbint_bh_raw_call_state *state =
150                 tevent_req_data(req,
151                 struct wbint_bh_raw_call_state);
152         int ret, err;
153
154         ret = wb_child_request_recv(subreq, state, &state->response, &err);
155         TALLOC_FREE(subreq);
156         if (ret == -1) {
157                 NTSTATUS status = map_nt_error_from_unix(err);
158                 tevent_req_nterror(req, status);
159                 return;
160         }
161
162         state->out_data = data_blob_talloc(state,
163                 state->response->extra_data.data,
164                 state->response->length - sizeof(struct winbindd_response));
165         if (state->response->extra_data.data && !state->out_data.data) {
166                 tevent_req_oom(req);
167                 return;
168         }
169
170         if (state->domain != NULL) {
171                 wcache_store_ndr(state->domain, state->opnum,
172                                  &state->in_data, &state->out_data);
173         }
174
175         tevent_req_done(req);
176 }
177
178 static void wbint_bh_raw_call_domain_done(struct tevent_req *subreq)
179 {
180         struct tevent_req *req =
181                 tevent_req_callback_data(subreq,
182                 struct tevent_req);
183         struct wbint_bh_raw_call_state *state =
184                 tevent_req_data(req,
185                 struct wbint_bh_raw_call_state);
186         int ret, err;
187
188         ret = wb_domain_request_recv(subreq, state, &state->response, &err);
189         TALLOC_FREE(subreq);
190         if (ret == -1) {
191                 NTSTATUS status = map_nt_error_from_unix(err);
192                 tevent_req_nterror(req, status);
193                 return;
194         }
195
196         state->out_data = data_blob_talloc(state,
197                 state->response->extra_data.data,
198                 state->response->length - sizeof(struct winbindd_response));
199         if (state->response->extra_data.data && !state->out_data.data) {
200                 tevent_req_oom(req);
201                 return;
202         }
203
204         if (state->domain != NULL) {
205                 wcache_store_ndr(state->domain, state->opnum,
206                                  &state->in_data, &state->out_data);
207         }
208
209         tevent_req_done(req);
210 }
211
212 static NTSTATUS wbint_bh_raw_call_recv(struct tevent_req *req,
213                                         TALLOC_CTX *mem_ctx,
214                                         uint8_t **out_data,
215                                         size_t *out_length,
216                                         uint32_t *out_flags)
217 {
218         struct wbint_bh_raw_call_state *state =
219                 tevent_req_data(req,
220                 struct wbint_bh_raw_call_state);
221         NTSTATUS status;
222
223         if (tevent_req_is_nterror(req, &status)) {
224                 tevent_req_received(req);
225                 return status;
226         }
227
228         *out_data = talloc_move(mem_ctx, &state->out_data.data);
229         *out_length = state->out_data.length;
230         *out_flags = 0;
231         tevent_req_received(req);
232         return NT_STATUS_OK;
233 }
234
235 struct wbint_bh_disconnect_state {
236         uint8_t _dummy;
237 };
238
239 static struct tevent_req *wbint_bh_disconnect_send(TALLOC_CTX *mem_ctx,
240                                                 struct tevent_context *ev,
241                                                 struct dcerpc_binding_handle *h)
242 {
243         struct wbint_bh_state *hs = dcerpc_binding_handle_data(h,
244                                      struct wbint_bh_state);
245         struct tevent_req *req;
246         struct wbint_bh_disconnect_state *state;
247         bool ok;
248
249         req = tevent_req_create(mem_ctx, &state,
250                                 struct wbint_bh_disconnect_state);
251         if (req == NULL) {
252                 return NULL;
253         }
254
255         ok = wbint_bh_is_connected(h);
256         if (!ok) {
257                 tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
258                 return tevent_req_post(req, ev);
259         }
260
261         /*
262          * TODO: do a real async disconnect ...
263          */
264         hs->domain = NULL;
265         hs->child = NULL;
266
267         tevent_req_done(req);
268         return tevent_req_post(req, ev);
269 }
270
271 static NTSTATUS wbint_bh_disconnect_recv(struct tevent_req *req)
272 {
273         NTSTATUS status;
274
275         if (tevent_req_is_nterror(req, &status)) {
276                 tevent_req_received(req);
277                 return status;
278         }
279
280         tevent_req_received(req);
281         return NT_STATUS_OK;
282 }
283
284 static bool wbint_bh_ref_alloc(struct dcerpc_binding_handle *h)
285 {
286         return true;
287 }
288
289 static void wbint_bh_do_ndr_print(struct dcerpc_binding_handle *h,
290                                   ndr_flags_type ndr_flags,
291                                   const void *_struct_ptr,
292                                   const struct ndr_interface_call *call)
293 {
294         void *struct_ptr = discard_const(_struct_ptr);
295
296         if (DEBUGLEVEL < 10) {
297                 return;
298         }
299
300         if (ndr_flags & NDR_IN) {
301                 ndr_print_function_debug(call->ndr_print,
302                                          call->name,
303                                          ndr_flags,
304                                          struct_ptr);
305         }
306         if (ndr_flags & NDR_OUT) {
307                 ndr_print_function_debug(call->ndr_print,
308                                          call->name,
309                                          ndr_flags,
310                                          struct_ptr);
311         }
312 }
313
314 static const struct dcerpc_binding_handle_ops wbint_bh_ops = {
315         .name                   = "wbint",
316         .is_connected           = wbint_bh_is_connected,
317         .set_timeout            = wbint_bh_set_timeout,
318         .raw_call_send          = wbint_bh_raw_call_send,
319         .raw_call_recv          = wbint_bh_raw_call_recv,
320         .disconnect_send        = wbint_bh_disconnect_send,
321         .disconnect_recv        = wbint_bh_disconnect_recv,
322
323         .ref_alloc              = wbint_bh_ref_alloc,
324         .do_ndr_print           = wbint_bh_do_ndr_print,
325 };
326
327 static NTSTATUS make_internal_ncacn_conn(TALLOC_CTX *mem_ctx,
328                                 const struct ndr_interface_table *table,
329                                 struct dcerpc_ncacn_conn **_out)
330 {
331         struct dcerpc_ncacn_conn *ncacn_conn = NULL;
332
333         ncacn_conn = talloc_zero(mem_ctx, struct dcerpc_ncacn_conn);
334         if (ncacn_conn == NULL) {
335                 return NT_STATUS_NO_MEMORY;
336         }
337
338         ncacn_conn->p.mem_ctx = mem_ctx;
339
340         *_out = ncacn_conn;
341
342         return NT_STATUS_OK;
343 }
344
345 static NTSTATUS find_ncalrpc_default_endpoint(struct dcesrv_context *dce_ctx,
346                                               struct dcesrv_endpoint **ep)
347 {
348         TALLOC_CTX *tmp_ctx = NULL;
349         struct dcerpc_binding *binding = NULL;
350         NTSTATUS status;
351
352         tmp_ctx = talloc_new(dce_ctx);
353         if (tmp_ctx == NULL) {
354                 return NT_STATUS_NO_MEMORY;
355         }
356
357         /*
358          * Some services use a rpcint binding handle in their initialization,
359          * before the server is fully initialized. Search the NCALRPC endpoint
360          * with and without endpoint
361          */
362         status = dcerpc_parse_binding(tmp_ctx, "ncalrpc:", &binding);
363         if (!NT_STATUS_IS_OK(status)) {
364                 goto out;
365         }
366
367         status = dcesrv_find_endpoint(dce_ctx, binding, ep);
368         if (NT_STATUS_IS_OK(status)) {
369                 goto out;
370         }
371
372         status = dcerpc_parse_binding(tmp_ctx, "ncalrpc:[WINBIND]", &binding);
373         if (!NT_STATUS_IS_OK(status)) {
374                 goto out;
375         }
376
377         status = dcesrv_find_endpoint(dce_ctx, binding, ep);
378         if (NT_STATUS_IS_OK(status)) {
379                 goto out;
380         }
381
382         status = dcerpc_parse_binding(tmp_ctx, "ncalrpc:[DEFAULT]", &binding);
383         if (!NT_STATUS_IS_OK(status)) {
384                 goto out;
385         }
386
387         status = dcesrv_find_endpoint(dce_ctx, binding, ep);
388         if (!NT_STATUS_IS_OK(status)) {
389                 goto out;
390         }
391
392 out:
393         talloc_free(tmp_ctx);
394         return status;
395 }
396
397 static NTSTATUS make_internal_dcesrv_connection(TALLOC_CTX *mem_ctx,
398                                 const struct ndr_interface_table *ndr_table,
399                                 struct dcerpc_ncacn_conn *ncacn_conn,
400                                 struct dcesrv_connection **_out)
401 {
402         struct dcesrv_connection *conn = NULL;
403         struct dcesrv_connection_context *context = NULL;
404         struct dcesrv_endpoint *endpoint = NULL;
405         NTSTATUS status;
406
407         conn = talloc_zero(mem_ctx, struct dcesrv_connection);
408         if (conn == NULL) {
409                 return NT_STATUS_NO_MEMORY;
410         }
411         conn->dce_ctx = global_dcesrv_context();
412         conn->preferred_transfer = &ndr_transfer_syntax_ndr;
413         conn->transport.private_data = ncacn_conn;
414
415         status = find_ncalrpc_default_endpoint(conn->dce_ctx, &endpoint);
416         if (!NT_STATUS_IS_OK(status)) {
417                 goto fail;
418         }
419         conn->endpoint = endpoint;
420
421         conn->default_auth_state = talloc_zero(conn, struct dcesrv_auth);
422         if (conn->default_auth_state == NULL) {
423                 status = NT_STATUS_NO_MEMORY;
424                 goto fail;
425         }
426         conn->default_auth_state->auth_finished = true;
427
428         context = talloc_zero(conn, struct dcesrv_connection_context);
429         if (context == NULL) {
430                 status = NT_STATUS_NO_MEMORY;
431                 goto fail;
432         }
433         context->conn = conn;
434         context->context_id = 0;
435         context->transfer_syntax = *(conn->preferred_transfer);
436         context->iface = find_interface_by_syntax_id(
437                 conn->endpoint, &ndr_table->syntax_id);
438         if (context->iface == NULL) {
439                 status = NT_STATUS_RPC_INTERFACE_NOT_FOUND;
440                 goto fail;
441         }
442
443         DLIST_ADD(conn->contexts, context);
444
445         *_out = conn;
446
447         return NT_STATUS_OK;
448 fail:
449         talloc_free(conn);
450         return status;
451 }
452
453 static NTSTATUS set_remote_addresses(struct dcesrv_connection *conn,
454                                      int sock)
455 {
456         struct sockaddr_storage st = { 0 };
457         struct sockaddr *sar = (struct sockaddr *)&st;
458         struct tsocket_address *remote = NULL;
459         struct tsocket_address *local = NULL;
460         socklen_t sa_len = sizeof(st);
461         NTSTATUS status;
462         int ret;
463
464         ZERO_STRUCT(st);
465         ret = getpeername(sock, sar, &sa_len);
466         if (ret != 0) {
467                 status = map_nt_error_from_unix(ret);
468                 DBG_ERR("getpeername failed: %s\n", nt_errstr(status));
469                 return status;
470         }
471
472         ret = tsocket_address_bsd_from_sockaddr(conn, sar, sa_len, &remote);
473         if (ret != 0) {
474                 status = map_nt_error_from_unix(ret);
475                 DBG_ERR("tsocket_address_bsd_from_sockaddr failed: %s\n",
476                         nt_errstr(status));
477                 return status;
478         }
479
480         ZERO_STRUCT(st);
481         ret = getsockname(sock, sar, &sa_len);
482         if (ret != 0) {
483                 status = map_nt_error_from_unix(ret);
484                 DBG_ERR("getsockname failed: %s\n", nt_errstr(status));
485                 return status;
486         }
487
488         ret = tsocket_address_bsd_from_sockaddr(conn, sar, sa_len, &local);
489         if (ret != 0) {
490                 status = map_nt_error_from_unix(ret);
491                 DBG_ERR("tsocket_address_bsd_from_sockaddr failed: %s\n",
492                         nt_errstr(status));
493                 return status;
494         }
495
496         conn->local_address = talloc_move(conn, &local);
497         conn->remote_address = talloc_move(conn, &remote);
498
499         return NT_STATUS_OK;
500 }
501
502 /* initialise a wbint binding handle */
503 struct dcerpc_binding_handle *wbint_binding_handle(TALLOC_CTX *mem_ctx,
504                                                 struct winbindd_domain *domain,
505                                                 struct winbindd_child *child)
506 {
507         struct dcerpc_binding_handle *h;
508         struct wbint_bh_state *hs;
509
510         h = dcerpc_binding_handle_create(mem_ctx,
511                                          &wbint_bh_ops,
512                                          NULL,
513                                          &ndr_table_winbind,
514                                          &hs,
515                                          struct wbint_bh_state,
516                                          __location__);
517         if (h == NULL) {
518                 return NULL;
519         }
520         hs->domain = domain;
521         hs->child = child;
522
523         return h;
524 }
525
526 enum winbindd_result winbindd_dual_ndrcmd(struct winbindd_domain *domain,
527                                           struct winbindd_cli_state *state)
528 {
529         struct dcerpc_ncacn_conn *ncacn_conn = NULL;
530         struct dcesrv_connection *dcesrv_conn = NULL;
531         struct dcesrv_call_state *dcesrv_call = NULL;
532         struct data_blob_list_item *rep = NULL;
533         struct dcesrv_context_callbacks *cb = NULL;
534         uint32_t opnum = state->request->data.ndrcmd;
535         TALLOC_CTX *mem_ctx;
536         NTSTATUS status;
537
538         DBG_DEBUG("Running command %s (domain '%s')\n",
539                   ndr_table_winbind.calls[opnum].name,
540                   domain ? domain->name : "(null)");
541
542         mem_ctx = talloc_stackframe();
543         if (mem_ctx == NULL) {
544                 DBG_ERR("No memory\n");
545                 return WINBINDD_ERROR;
546         }
547
548         status = make_internal_ncacn_conn(mem_ctx,
549                                           &ndr_table_winbind,
550                                           &ncacn_conn);
551         if (!NT_STATUS_IS_OK(status)) {
552                 goto out;
553         }
554
555         status = make_internal_dcesrv_connection(ncacn_conn,
556                                                  &ndr_table_winbind,
557                                                  ncacn_conn,
558                                                  &dcesrv_conn);
559         if (!NT_STATUS_IS_OK(status)) {
560                 goto out;
561         }
562
563         status = set_remote_addresses(dcesrv_conn, state->sock);
564         if (!NT_STATUS_IS_OK(status)) {
565                 goto out;
566         }
567
568         dcesrv_call = talloc_zero(dcesrv_conn, struct dcesrv_call_state);
569         if (dcesrv_call == NULL) {
570                 status = NT_STATUS_NO_MEMORY;
571                 goto out;
572         }
573
574         dcesrv_call->conn = dcesrv_conn;
575         dcesrv_call->context = dcesrv_conn->contexts;
576         dcesrv_call->auth_state = dcesrv_conn->default_auth_state;
577
578         ZERO_STRUCT(dcesrv_call->pkt);
579         dcesrv_call->pkt.u.bind.assoc_group_id = 0;
580
581         cb = dcesrv_call->conn->dce_ctx->callbacks;
582         status = cb->assoc_group.find(
583                 dcesrv_call, cb->assoc_group.private_data);
584         if (!NT_STATUS_IS_OK(status)) {
585                 goto out;
586         }
587
588         ZERO_STRUCT(dcesrv_call->pkt);
589         dcesrv_call->pkt.u.request.opnum = opnum;
590         dcesrv_call->pkt.u.request.context_id = 0;
591         dcesrv_call->pkt.u.request.stub_and_verifier =
592                 data_blob_const(state->request->extra_data.data,
593                                 state->request->extra_len);
594
595         status = dcesrv_call_dispatch_local(dcesrv_call);
596         if (!NT_STATUS_IS_OK(status)) {
597                 goto out;
598         }
599
600         rep = dcesrv_call->replies;
601         DLIST_REMOVE(dcesrv_call->replies, rep);
602
603         state->response->extra_data.data = talloc_steal(state->mem_ctx,
604                                                         rep->blob.data);
605         state->response->length += rep->blob.length;
606
607         talloc_free(rep);
608
609 out:
610         talloc_free(mem_ctx);
611         if (NT_STATUS_IS_OK(status)) {
612                 return WINBINDD_OK;
613         }
614         return WINBINDD_ERROR;
615 }