samba-tool tests: rename 'group create' to 'group add'
[Samba.git] / source3 / winbindd / winbindd_dual_ndr.c
blobf2d8c815dca3dcfb03bbffd520bad47c4ee23ed0
1 /*
2 Unix SMB/CIFS implementation.
4 Provide parent->child communication based on NDR marshalling
6 Copyright (C) Volker Lendecke 2009
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.
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.
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/>.
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.
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"
39 struct wbint_bh_state {
40 struct winbindd_domain *domain;
41 struct winbindd_child *child;
44 static bool wbint_bh_is_connected(struct dcerpc_binding_handle *h)
46 struct wbint_bh_state *hs = dcerpc_binding_handle_data(h,
47 struct wbint_bh_state);
49 if ((hs->domain == NULL) && (hs->child == NULL)) {
50 return false;
53 return true;
56 static uint32_t wbint_bh_set_timeout(struct dcerpc_binding_handle *h,
57 uint32_t timeout)
59 /* TODO: implement timeouts */
60 return UINT32_MAX;
63 struct wbint_bh_raw_call_state {
64 struct winbindd_domain *domain;
65 uint32_t opnum;
66 DATA_BLOB in_data;
67 struct winbindd_request request;
68 struct winbindd_response *response;
69 DATA_BLOB out_data;
72 static void wbint_bh_raw_call_child_done(struct tevent_req *subreq);
73 static void wbint_bh_raw_call_domain_done(struct tevent_req *subreq);
75 static struct tevent_req *wbint_bh_raw_call_send(TALLOC_CTX *mem_ctx,
76 struct tevent_context *ev,
77 struct dcerpc_binding_handle *h,
78 const struct GUID *object,
79 uint32_t opnum,
80 uint32_t in_flags,
81 const uint8_t *in_data,
82 size_t in_length)
84 struct wbint_bh_state *hs =
85 dcerpc_binding_handle_data(h,
86 struct wbint_bh_state);
87 struct tevent_req *req;
88 struct wbint_bh_raw_call_state *state;
89 bool ok;
90 struct tevent_req *subreq;
92 req = tevent_req_create(mem_ctx, &state,
93 struct wbint_bh_raw_call_state);
94 if (req == NULL) {
95 return NULL;
97 state->domain = hs->domain;
98 state->opnum = opnum;
99 state->in_data.data = discard_const_p(uint8_t, in_data);
100 state->in_data.length = in_length;
102 ok = wbint_bh_is_connected(h);
103 if (!ok) {
104 tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
105 return tevent_req_post(req, ev);
108 if ((state->domain != NULL)
109 && wcache_fetch_ndr(state, state->domain, state->opnum,
110 &state->in_data, &state->out_data)) {
111 DBG_DEBUG("Got opnum %"PRIu32" for domain %s from cache\n",
112 state->opnum, state->domain->name);
113 tevent_req_done(req);
114 return tevent_req_post(req, ev);
117 state->request.cmd = WINBINDD_DUAL_NDRCMD;
118 state->request.data.ndrcmd = state->opnum;
119 state->request.extra_data.data = (char *)state->in_data.data;
120 state->request.extra_len = state->in_data.length;
122 if (hs->child != NULL) {
123 subreq = wb_child_request_send(state, ev, hs->child,
124 &state->request);
125 if (tevent_req_nomem(subreq, req)) {
126 return tevent_req_post(req, ev);
128 tevent_req_set_callback(
129 subreq, wbint_bh_raw_call_child_done, req);
130 return req;
133 subreq = wb_domain_request_send(state, ev, hs->domain,
134 &state->request);
135 if (tevent_req_nomem(subreq, req)) {
136 return tevent_req_post(req, ev);
138 tevent_req_set_callback(subreq, wbint_bh_raw_call_domain_done, req);
140 return req;
143 static void wbint_bh_raw_call_child_done(struct tevent_req *subreq)
145 struct tevent_req *req =
146 tevent_req_callback_data(subreq,
147 struct tevent_req);
148 struct wbint_bh_raw_call_state *state =
149 tevent_req_data(req,
150 struct wbint_bh_raw_call_state);
151 int ret, err;
153 ret = wb_child_request_recv(subreq, state, &state->response, &err);
154 TALLOC_FREE(subreq);
155 if (ret == -1) {
156 NTSTATUS status = map_nt_error_from_unix(err);
157 tevent_req_nterror(req, status);
158 return;
161 state->out_data = data_blob_talloc(state,
162 state->response->extra_data.data,
163 state->response->length - sizeof(struct winbindd_response));
164 if (state->response->extra_data.data && !state->out_data.data) {
165 tevent_req_oom(req);
166 return;
169 if (state->domain != NULL) {
170 wcache_store_ndr(state->domain, state->opnum,
171 &state->in_data, &state->out_data);
174 tevent_req_done(req);
177 static void wbint_bh_raw_call_domain_done(struct tevent_req *subreq)
179 struct tevent_req *req =
180 tevent_req_callback_data(subreq,
181 struct tevent_req);
182 struct wbint_bh_raw_call_state *state =
183 tevent_req_data(req,
184 struct wbint_bh_raw_call_state);
185 int ret, err;
187 ret = wb_domain_request_recv(subreq, state, &state->response, &err);
188 TALLOC_FREE(subreq);
189 if (ret == -1) {
190 NTSTATUS status = map_nt_error_from_unix(err);
191 tevent_req_nterror(req, status);
192 return;
195 state->out_data = data_blob_talloc(state,
196 state->response->extra_data.data,
197 state->response->length - sizeof(struct winbindd_response));
198 if (state->response->extra_data.data && !state->out_data.data) {
199 tevent_req_oom(req);
200 return;
203 if (state->domain != NULL) {
204 wcache_store_ndr(state->domain, state->opnum,
205 &state->in_data, &state->out_data);
208 tevent_req_done(req);
211 static NTSTATUS wbint_bh_raw_call_recv(struct tevent_req *req,
212 TALLOC_CTX *mem_ctx,
213 uint8_t **out_data,
214 size_t *out_length,
215 uint32_t *out_flags)
217 struct wbint_bh_raw_call_state *state =
218 tevent_req_data(req,
219 struct wbint_bh_raw_call_state);
220 NTSTATUS status;
222 if (tevent_req_is_nterror(req, &status)) {
223 tevent_req_received(req);
224 return status;
227 *out_data = talloc_move(mem_ctx, &state->out_data.data);
228 *out_length = state->out_data.length;
229 *out_flags = 0;
230 tevent_req_received(req);
231 return NT_STATUS_OK;
234 struct wbint_bh_disconnect_state {
235 uint8_t _dummy;
238 static struct tevent_req *wbint_bh_disconnect_send(TALLOC_CTX *mem_ctx,
239 struct tevent_context *ev,
240 struct dcerpc_binding_handle *h)
242 struct wbint_bh_state *hs = dcerpc_binding_handle_data(h,
243 struct wbint_bh_state);
244 struct tevent_req *req;
245 struct wbint_bh_disconnect_state *state;
246 bool ok;
248 req = tevent_req_create(mem_ctx, &state,
249 struct wbint_bh_disconnect_state);
250 if (req == NULL) {
251 return NULL;
254 ok = wbint_bh_is_connected(h);
255 if (!ok) {
256 tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
257 return tevent_req_post(req, ev);
261 * TODO: do a real async disconnect ...
263 hs->domain = NULL;
264 hs->child = NULL;
266 tevent_req_done(req);
267 return tevent_req_post(req, ev);
270 static NTSTATUS wbint_bh_disconnect_recv(struct tevent_req *req)
272 NTSTATUS status;
274 if (tevent_req_is_nterror(req, &status)) {
275 tevent_req_received(req);
276 return status;
279 tevent_req_received(req);
280 return NT_STATUS_OK;
283 static bool wbint_bh_ref_alloc(struct dcerpc_binding_handle *h)
285 return true;
288 static void wbint_bh_do_ndr_print(struct dcerpc_binding_handle *h,
289 int ndr_flags,
290 const void *_struct_ptr,
291 const struct ndr_interface_call *call)
293 void *struct_ptr = discard_const(_struct_ptr);
295 if (DEBUGLEVEL < 10) {
296 return;
299 if (ndr_flags & NDR_IN) {
300 ndr_print_function_debug(call->ndr_print,
301 call->name,
302 ndr_flags,
303 struct_ptr);
305 if (ndr_flags & NDR_OUT) {
306 ndr_print_function_debug(call->ndr_print,
307 call->name,
308 ndr_flags,
309 struct_ptr);
313 static const struct dcerpc_binding_handle_ops wbint_bh_ops = {
314 .name = "wbint",
315 .is_connected = wbint_bh_is_connected,
316 .set_timeout = wbint_bh_set_timeout,
317 .raw_call_send = wbint_bh_raw_call_send,
318 .raw_call_recv = wbint_bh_raw_call_recv,
319 .disconnect_send = wbint_bh_disconnect_send,
320 .disconnect_recv = wbint_bh_disconnect_recv,
322 .ref_alloc = wbint_bh_ref_alloc,
323 .do_ndr_print = wbint_bh_do_ndr_print,
326 static NTSTATUS make_internal_ncacn_conn(TALLOC_CTX *mem_ctx,
327 const struct ndr_interface_table *table,
328 struct dcerpc_ncacn_conn **_out)
330 struct dcerpc_ncacn_conn *ncacn_conn = NULL;
331 NTSTATUS status;
333 ncacn_conn = talloc_zero(mem_ctx, struct dcerpc_ncacn_conn);
334 if (ncacn_conn == NULL) {
335 return NT_STATUS_NO_MEMORY;
338 ncacn_conn->p = talloc_zero(ncacn_conn, struct pipes_struct);
339 if (ncacn_conn->p == NULL) {
340 status = NT_STATUS_NO_MEMORY;
341 goto fail;
343 ncacn_conn->p->mem_ctx = mem_ctx;
345 *_out = ncacn_conn;
347 return NT_STATUS_OK;
349 fail:
350 talloc_free(ncacn_conn);
351 return status;
354 static NTSTATUS find_ncalrpc_default_endpoint(struct dcesrv_context *dce_ctx,
355 struct dcesrv_endpoint **ep)
357 TALLOC_CTX *tmp_ctx = NULL;
358 struct dcerpc_binding *binding = NULL;
359 NTSTATUS status;
361 tmp_ctx = talloc_new(dce_ctx);
362 if (tmp_ctx == NULL) {
363 return NT_STATUS_NO_MEMORY;
367 * Some services use a rpcint binding handle in their initialization,
368 * before the server is fully initialized. Search the NCALRPC endpoint
369 * with and without endpoint
371 status = dcerpc_parse_binding(tmp_ctx, "ncalrpc:", &binding);
372 if (!NT_STATUS_IS_OK(status)) {
373 goto out;
376 status = dcesrv_find_endpoint(dce_ctx, binding, ep);
377 if (NT_STATUS_IS_OK(status)) {
378 goto out;
381 status = dcerpc_parse_binding(tmp_ctx, "ncalrpc:[DEFAULT]", &binding);
382 if (!NT_STATUS_IS_OK(status)) {
383 goto out;
386 status = dcesrv_find_endpoint(dce_ctx, binding, ep);
387 if (!NT_STATUS_IS_OK(status)) {
388 goto out;
391 out:
392 talloc_free(tmp_ctx);
393 return status;
396 static NTSTATUS make_internal_dcesrv_connection(TALLOC_CTX *mem_ctx,
397 const struct ndr_interface_table *ndr_table,
398 struct dcerpc_ncacn_conn *ncacn_conn,
399 struct dcesrv_connection **_out)
401 struct dcesrv_connection *conn = NULL;
402 struct dcesrv_connection_context *context = NULL;
403 struct dcesrv_endpoint *endpoint = NULL;
404 NTSTATUS status;
406 conn = talloc_zero(mem_ctx, struct dcesrv_connection);
407 if (conn == NULL) {
408 return NT_STATUS_NO_MEMORY;
410 conn->dce_ctx = global_dcesrv_context();
411 conn->preferred_transfer = &ndr_transfer_syntax_ndr;
412 conn->transport.private_data = ncacn_conn;
414 status = find_ncalrpc_default_endpoint(conn->dce_ctx, &endpoint);
415 if (!NT_STATUS_IS_OK(status)) {
416 goto fail;
418 conn->endpoint = endpoint;
420 conn->default_auth_state = talloc_zero(conn, struct dcesrv_auth);
421 if (conn->default_auth_state == NULL) {
422 status = NT_STATUS_NO_MEMORY;
423 goto fail;
425 conn->default_auth_state->session_info = ncacn_conn->session_info;
426 conn->default_auth_state->auth_finished = true;
428 context = talloc_zero(conn, struct dcesrv_connection_context);
429 if (context == NULL) {
430 status = NT_STATUS_NO_MEMORY;
431 goto fail;
433 context->conn = conn;
434 context->context_id = 0;
435 context->transfer_syntax = *(conn->preferred_transfer);
436 context->iface = find_interface_by_uuid(conn->endpoint,
437 &ndr_table->syntax_id.uuid,
438 ndr_table->syntax_id.if_version);
439 if (context->iface == NULL) {
440 status = NT_STATUS_RPC_INTERFACE_NOT_FOUND;
441 goto fail;
444 DLIST_ADD(conn->contexts, context);
446 *_out = conn;
448 return NT_STATUS_OK;
449 fail:
450 talloc_free(conn);
451 return status;
454 /* initialise a wbint binding handle */
455 struct dcerpc_binding_handle *wbint_binding_handle(TALLOC_CTX *mem_ctx,
456 struct winbindd_domain *domain,
457 struct winbindd_child *child)
459 struct dcerpc_binding_handle *h;
460 struct wbint_bh_state *hs;
462 h = dcerpc_binding_handle_create(mem_ctx,
463 &wbint_bh_ops,
464 NULL,
465 &ndr_table_winbind,
466 &hs,
467 struct wbint_bh_state,
468 __location__);
469 if (h == NULL) {
470 return NULL;
472 hs->domain = domain;
473 hs->child = child;
475 return h;
478 static NTSTATUS rpcint_dispatch(struct dcesrv_call_state *call)
480 NTSTATUS status;
481 struct ndr_pull *pull = NULL;
482 struct ndr_push *push = NULL;
483 struct data_blob_list_item *rep = NULL;
485 pull = ndr_pull_init_blob(&call->pkt.u.request.stub_and_verifier,
486 call);
487 if (pull == NULL) {
488 return NT_STATUS_NO_MEMORY;
491 pull->flags |= LIBNDR_FLAG_REF_ALLOC;
493 call->ndr_pull = pull;
495 /* unravel the NDR for the packet */
496 status = call->context->iface->ndr_pull(call, call, pull, &call->r);
497 if (!NT_STATUS_IS_OK(status)) {
498 DBG_ERR("DCE/RPC fault in call %s:%02X - %s\n",
499 call->context->iface->name,
500 call->pkt.u.request.opnum,
501 dcerpc_errstr(call, call->fault_code));
502 return status;
505 status = call->context->iface->local(call, call, call->r);
506 if (!NT_STATUS_IS_OK(status)) {
507 DBG_ERR("DCE/RPC fault in call %s:%02X - %s\n",
508 call->context->iface->name,
509 call->pkt.u.request.opnum,
510 dcerpc_errstr(call, call->fault_code));
511 return status;
514 push = ndr_push_init_ctx(call);
515 if (push == NULL) {
516 return NT_STATUS_NO_MEMORY;
519 push->ptr_count = call->ndr_pull->ptr_count;
521 status = call->context->iface->ndr_push(call, call, push, call->r);
522 if (!NT_STATUS_IS_OK(status)) {
523 DBG_ERR("DCE/RPC fault in call %s:%02X - %s\n",
524 call->context->iface->name,
525 call->pkt.u.request.opnum,
526 dcerpc_errstr(call, call->fault_code));
527 return status;
530 rep = talloc_zero(call, struct data_blob_list_item);
531 if (rep == NULL) {
532 return NT_STATUS_NO_MEMORY;
535 rep->blob = ndr_push_blob(push);
536 DLIST_ADD_END(call->replies, rep);
538 return NT_STATUS_OK;
541 enum winbindd_result winbindd_dual_ndrcmd(struct winbindd_domain *domain,
542 struct winbindd_cli_state *state)
544 struct dcerpc_ncacn_conn *ncacn_conn = NULL;
545 struct dcesrv_connection *dcesrv_conn = NULL;
546 struct dcesrv_call_state *dcesrv_call = NULL;
547 struct data_blob_list_item *rep = NULL;
548 uint32_t opnum = state->request->data.ndrcmd;
549 TALLOC_CTX *mem_ctx;
550 NTSTATUS status;
552 DBG_DEBUG("Running command %s (domain '%s')\n",
553 ndr_table_winbind.calls[opnum].name,
554 domain ? domain->name : "(null)");
556 mem_ctx = talloc_stackframe();
557 if (mem_ctx == NULL) {
558 DBG_ERR("No memory");
559 return WINBINDD_ERROR;
562 status = make_internal_ncacn_conn(mem_ctx,
563 &ndr_table_winbind,
564 &ncacn_conn);
565 if (!NT_STATUS_IS_OK(status)) {
566 goto out;
569 status = make_internal_dcesrv_connection(ncacn_conn,
570 &ndr_table_winbind,
571 ncacn_conn,
572 &dcesrv_conn);
573 if (!NT_STATUS_IS_OK(status)) {
574 goto out;
577 dcesrv_call = talloc_zero(dcesrv_conn, struct dcesrv_call_state);
578 if (dcesrv_call == NULL) {
579 status = NT_STATUS_NO_MEMORY;
580 goto out;
583 dcesrv_call->conn = dcesrv_conn;
584 dcesrv_call->context = dcesrv_conn->contexts;
585 dcesrv_call->auth_state = dcesrv_conn->default_auth_state;
587 ZERO_STRUCT(dcesrv_call->pkt);
588 dcesrv_call->pkt.u.bind.assoc_group_id = 0;
589 status = dcesrv_call->conn->dce_ctx->callbacks.assoc_group.find(
590 dcesrv_call);
591 if (!NT_STATUS_IS_OK(status)) {
592 goto out;
595 ZERO_STRUCT(dcesrv_call->pkt);
596 dcesrv_call->pkt.u.request.opnum = opnum;
597 dcesrv_call->pkt.u.request.context_id = 0;
598 dcesrv_call->pkt.u.request.stub_and_verifier =
599 data_blob_const(state->request->extra_data.data,
600 state->request->extra_len);
602 status = rpcint_dispatch(dcesrv_call);
603 if (!NT_STATUS_IS_OK(status)) {
604 goto out;
607 rep = dcesrv_call->replies;
608 DLIST_REMOVE(dcesrv_call->replies, rep);
610 state->response->extra_data.data = talloc_steal(state->mem_ctx,
611 rep->blob.data);
612 state->response->length += rep->blob.length;
614 talloc_free(rep);
616 out:
617 talloc_free(mem_ctx);
618 if (NT_STATUS_IS_OK(status)) {
619 return WINBINDD_OK;
621 return WINBINDD_ERROR;