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