/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- make_nonstd_fd
- make_safe_fd
- winbind_closed_fd
- wb_context_init
- wb_connect_send
- wbc_connect_connected
- wb_connect_recv
- wb_int_trans_send
- wb_int_trans_write_done
- wb_int_trans_read_done
- wb_int_trans_recv
- winbindd_socket_dir
- wb_open_pipe_send
- wb_open_pipe_connect_nonpriv_done
- wb_open_pipe_ping_done
- wb_open_pipe_getpriv_done
- wb_open_pipe_connect_priv_done
- wb_open_pipe_recv
- wb_trans_send
- wb_trans_retry
- wb_trans_retry_wait_done
- wb_trans_connect_done
- wb_trans_done
- wb_trans_recv
1 /*
2 Unix SMB/CIFS implementation.
3 Infrastructure for async winbind requests
4 Copyright (C) Volker Lendecke 2008
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "wbc_async.h"
22
23 struct wb_context {
24 struct tevent_queue *queue;
25 int fd;
26 bool is_priv;
27 };
28
29 static int make_nonstd_fd(int fd)
/* [<][>][^][v][top][bottom][index][help] */
30 {
31 int i;
32 int sys_errno = 0;
33 int fds[3];
34 int num_fds = 0;
35
36 if (fd == -1) {
37 return -1;
38 }
39 while (fd < 3) {
40 fds[num_fds++] = fd;
41 fd = dup(fd);
42 if (fd == -1) {
43 sys_errno = errno;
44 break;
45 }
46 }
47 for (i=0; i<num_fds; i++) {
48 close(fds[i]);
49 }
50 if (fd == -1) {
51 errno = sys_errno;
52 }
53 return fd;
54 }
55
56 /****************************************************************************
57 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
58 else
59 if SYSV use O_NDELAY
60 if BSD use FNDELAY
61 Set close on exec also.
62 ****************************************************************************/
63
64 static int make_safe_fd(int fd)
/* [<][>][^][v][top][bottom][index][help] */
65 {
66 int result, flags;
67 int new_fd = make_nonstd_fd(fd);
68
69 if (new_fd == -1) {
70 goto fail;
71 }
72
73 /* Socket should be nonblocking. */
74
75 #ifdef O_NONBLOCK
76 #define FLAG_TO_SET O_NONBLOCK
77 #else
78 #ifdef SYSV
79 #define FLAG_TO_SET O_NDELAY
80 #else /* BSD */
81 #define FLAG_TO_SET FNDELAY
82 #endif
83 #endif
84
85 if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
86 goto fail;
87 }
88
89 flags |= FLAG_TO_SET;
90 if (fcntl(new_fd, F_SETFL, flags) == -1) {
91 goto fail;
92 }
93
94 #undef FLAG_TO_SET
95
96 /* Socket should be closed on exec() */
97 #ifdef FD_CLOEXEC
98 result = flags = fcntl(new_fd, F_GETFD, 0);
99 if (flags >= 0) {
100 flags |= FD_CLOEXEC;
101 result = fcntl( new_fd, F_SETFD, flags );
102 }
103 if (result < 0) {
104 goto fail;
105 }
106 #endif
107 return new_fd;
108
109 fail:
110 if (new_fd != -1) {
111 int sys_errno = errno;
112 close(new_fd);
113 errno = sys_errno;
114 }
115 return -1;
116 }
117
118 static bool winbind_closed_fd(int fd)
/* [<][>][^][v][top][bottom][index][help] */
119 {
120 struct timeval tv;
121 fd_set r_fds;
122
123 if (fd == -1) {
124 return true;
125 }
126
127 FD_ZERO(&r_fds);
128 FD_SET(fd, &r_fds);
129 ZERO_STRUCT(tv);
130
131 if ((select(fd+1, &r_fds, NULL, NULL, &tv) == -1)
132 || FD_ISSET(fd, &r_fds)) {
133 return true;
134 }
135
136 return false;
137 }
138
139 struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx)
/* [<][>][^][v][top][bottom][index][help] */
140 {
141 struct wb_context *result;
142
143 result = talloc(mem_ctx, struct wb_context);
144 if (result == NULL) {
145 return NULL;
146 }
147 result->queue = tevent_queue_create(result, "wb_trans");
148 if (result->queue == NULL) {
149 TALLOC_FREE(result);
150 return NULL;
151 }
152 result->fd = -1;
153 return result;
154 }
155
156 struct wb_connect_state {
157 int dummy;
158 };
159
160 static void wbc_connect_connected(struct tevent_req *subreq);
161
162 static struct tevent_req *wb_connect_send(TALLOC_CTX *mem_ctx,
/* [<][>][^][v][top][bottom][index][help] */
163 struct tevent_context *ev,
164 struct wb_context *wb_ctx,
165 const char *dir)
166 {
167 struct tevent_req *result, *subreq;
168 struct wb_connect_state *state;
169 struct sockaddr_un sunaddr;
170 struct stat st;
171 char *path = NULL;
172 wbcErr wbc_err;
173
174 result = tevent_req_create(mem_ctx, &state, struct wb_connect_state);
175 if (result == NULL) {
176 return NULL;
177 }
178
179 if (wb_ctx->fd != -1) {
180 close(wb_ctx->fd);
181 wb_ctx->fd = -1;
182 }
183
184 /* Check permissions on unix socket directory */
185
186 if (lstat(dir, &st) == -1) {
187 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
188 goto post_status;
189 }
190
191 if (!S_ISDIR(st.st_mode) ||
192 (st.st_uid != 0 && st.st_uid != geteuid())) {
193 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
194 goto post_status;
195 }
196
197 /* Connect to socket */
198
199 path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
200 WINBINDD_SOCKET_NAME);
201 if (path == NULL) {
202 goto nomem;
203 }
204
205 sunaddr.sun_family = AF_UNIX;
206 strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
207 TALLOC_FREE(path);
208
209 /* If socket file doesn't exist, don't bother trying to connect
210 with retry. This is an attempt to make the system usable when
211 the winbindd daemon is not running. */
212
213 if ((lstat(sunaddr.sun_path, &st) == -1)
214 || !S_ISSOCK(st.st_mode)
215 || (st.st_uid != 0 && st.st_uid != geteuid())) {
216 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
217 goto post_status;
218 }
219
220 wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
221 if (wb_ctx->fd == -1) {
222 wbc_err = map_wbc_err_from_errno(errno);
223 goto post_status;
224 }
225
226 subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
227 (struct sockaddr *)&sunaddr,
228 sizeof(sunaddr));
229 if (subreq == NULL) {
230 goto nomem;
231 }
232 tevent_req_set_callback(subreq, wbc_connect_connected, result);
233
234 if (!tevent_req_set_endtime(subreq, ev, timeval_current_ofs(30, 0))) {
235 goto nomem;
236 }
237
238 return result;
239
240 post_status:
241 tevent_req_error(result, wbc_err);
242 return tevent_req_post(result, ev);
243 nomem:
244 TALLOC_FREE(result);
245 return NULL;
246 }
247
248 static void wbc_connect_connected(struct tevent_req *subreq)
/* [<][>][^][v][top][bottom][index][help] */
249 {
250 struct tevent_req *req = tevent_req_callback_data(
251 subreq, struct tevent_req);
252 int res, err;
253
254 res = async_connect_recv(subreq, &err);
255 TALLOC_FREE(subreq);
256 if (res == -1) {
257 tevent_req_error(req, map_wbc_err_from_errno(err));
258 return;
259 }
260 tevent_req_done(req);
261 }
262
263 static wbcErr wb_connect_recv(struct tevent_req *req)
/* [<][>][^][v][top][bottom][index][help] */
264 {
265 return tevent_req_simple_recv_wbcerr(req);
266 }
267
268 struct wb_int_trans_state {
269 struct tevent_context *ev;
270 int fd;
271 struct winbindd_request *wb_req;
272 struct winbindd_response *wb_resp;
273 };
274
275 static void wb_int_trans_write_done(struct tevent_req *subreq);
276 static void wb_int_trans_read_done(struct tevent_req *subreq);
277
278 static struct tevent_req *wb_int_trans_send(TALLOC_CTX *mem_ctx,
/* [<][>][^][v][top][bottom][index][help] */
279 struct tevent_context *ev,
280 struct tevent_queue *queue, int fd,
281 struct winbindd_request *wb_req)
282 {
283 struct tevent_req *result, *subreq;
284 struct wb_int_trans_state *state;
285
286 result = tevent_req_create(mem_ctx, &state,
287 struct wb_int_trans_state);
288 if (result == NULL) {
289 return NULL;
290 }
291
292 if (winbind_closed_fd(fd)) {
293 tevent_req_error(result, WBC_ERR_WINBIND_NOT_AVAILABLE);
294 return tevent_req_post(result, ev);
295 }
296
297 state->ev = ev;
298 state->fd = fd;
299 state->wb_req = wb_req;
300 state->wb_req->length = sizeof(struct winbindd_request);
301 state->wb_req->pid = getpid();
302
303 subreq = wb_req_write_send(state, state->ev, queue, state->fd,
304 state->wb_req);
305 if (subreq == NULL) {
306 goto fail;
307 }
308 tevent_req_set_callback(subreq, wb_int_trans_write_done, result);
309
310 return result;
311
312 fail:
313 TALLOC_FREE(result);
314 return NULL;
315 }
316
317 static void wb_int_trans_write_done(struct tevent_req *subreq)
/* [<][>][^][v][top][bottom][index][help] */
318 {
319 struct tevent_req *req = tevent_req_callback_data(
320 subreq, struct tevent_req);
321 struct wb_int_trans_state *state = tevent_req_data(
322 req, struct wb_int_trans_state);
323 wbcErr wbc_err;
324
325 wbc_err = wb_req_write_recv(subreq);
326 TALLOC_FREE(subreq);
327 if (!WBC_ERROR_IS_OK(wbc_err)) {
328 tevent_req_error(req, wbc_err);
329 return;
330 }
331
332 subreq = wb_resp_read_send(state, state->ev, state->fd);
333 if (tevent_req_nomem(subreq, req)) {
334 return;
335 }
336 tevent_req_set_callback(subreq, wb_int_trans_read_done, req);
337 }
338
339 static void wb_int_trans_read_done(struct tevent_req *subreq)
/* [<][>][^][v][top][bottom][index][help] */
340 {
341 struct tevent_req *req = tevent_req_callback_data(
342 subreq, struct tevent_req);
343 struct wb_int_trans_state *state = tevent_req_data(
344 req, struct wb_int_trans_state);
345 wbcErr wbc_err;
346
347 wbc_err = wb_resp_read_recv(subreq, state, &state->wb_resp);
348 TALLOC_FREE(subreq);
349 if (!WBC_ERROR_IS_OK(wbc_err)) {
350 tevent_req_error(req, wbc_err);
351 return;
352 }
353
354 tevent_req_done(req);
355 }
356
357 static wbcErr wb_int_trans_recv(struct tevent_req *req,
/* [<][>][^][v][top][bottom][index][help] */
358 TALLOC_CTX *mem_ctx,
359 struct winbindd_response **presponse)
360 {
361 struct wb_int_trans_state *state = tevent_req_data(
362 req, struct wb_int_trans_state);
363 wbcErr wbc_err;
364
365 if (tevent_req_is_wbcerr(req, &wbc_err)) {
366 return wbc_err;
367 }
368
369 *presponse = talloc_move(mem_ctx, &state->wb_resp);
370 return WBC_ERR_SUCCESS;
371 }
372
373 static const char *winbindd_socket_dir(void)
/* [<][>][^][v][top][bottom][index][help] */
374 {
375 #ifdef SOCKET_WRAPPER
376 const char *env_dir;
377
378 env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
379 if (env_dir) {
380 return env_dir;
381 }
382 #endif
383
384 return WINBINDD_SOCKET_DIR;
385 }
386
387 struct wb_open_pipe_state {
388 struct wb_context *wb_ctx;
389 struct tevent_context *ev;
390 bool need_priv;
391 struct winbindd_request wb_req;
392 };
393
394 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq);
395 static void wb_open_pipe_ping_done(struct tevent_req *subreq);
396 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq);
397 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq);
398
399 static struct tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
/* [<][>][^][v][top][bottom][index][help] */
400 struct tevent_context *ev,
401 struct wb_context *wb_ctx,
402 bool need_priv)
403 {
404 struct tevent_req *result, *subreq;
405 struct wb_open_pipe_state *state;
406
407 result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
408 if (result == NULL) {
409 return NULL;
410 }
411 state->wb_ctx = wb_ctx;
412 state->ev = ev;
413 state->need_priv = need_priv;
414
415 if (wb_ctx->fd != -1) {
416 close(wb_ctx->fd);
417 wb_ctx->fd = -1;
418 }
419
420 subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
421 if (subreq == NULL) {
422 goto fail;
423 }
424 tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
425 result);
426 return result;
427
428 fail:
429 TALLOC_FREE(result);
430 return NULL;
431 }
432
433 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
/* [<][>][^][v][top][bottom][index][help] */
434 {
435 struct tevent_req *req = tevent_req_callback_data(
436 subreq, struct tevent_req);
437 struct wb_open_pipe_state *state = tevent_req_data(
438 req, struct wb_open_pipe_state);
439 wbcErr wbc_err;
440
441 wbc_err = wb_connect_recv(subreq);
442 TALLOC_FREE(subreq);
443 if (!WBC_ERROR_IS_OK(wbc_err)) {
444 state->wb_ctx->is_priv = true;
445 tevent_req_error(req, wbc_err);
446 return;
447 }
448
449 ZERO_STRUCT(state->wb_req);
450 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
451
452 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
453 &state->wb_req);
454 if (tevent_req_nomem(subreq, req)) {
455 return;
456 }
457 tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
458 }
459
460 static void wb_open_pipe_ping_done(struct tevent_req *subreq)
/* [<][>][^][v][top][bottom][index][help] */
461 {
462 struct tevent_req *req = tevent_req_callback_data(
463 subreq, struct tevent_req);
464 struct wb_open_pipe_state *state = tevent_req_data(
465 req, struct wb_open_pipe_state);
466 struct winbindd_response *wb_resp;
467 wbcErr wbc_err;
468
469 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
470 TALLOC_FREE(subreq);
471 if (!WBC_ERROR_IS_OK(wbc_err)) {
472 tevent_req_error(req, wbc_err);
473 return;
474 }
475
476 if (!state->need_priv) {
477 tevent_req_done(req);
478 return;
479 }
480
481 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
482
483 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
484 &state->wb_req);
485 if (tevent_req_nomem(subreq, req)) {
486 return;
487 }
488 tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
489 }
490
491 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
/* [<][>][^][v][top][bottom][index][help] */
492 {
493 struct tevent_req *req = tevent_req_callback_data(
494 subreq, struct tevent_req);
495 struct wb_open_pipe_state *state = tevent_req_data(
496 req, struct wb_open_pipe_state);
497 struct winbindd_response *wb_resp = NULL;
498 wbcErr wbc_err;
499
500 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
501 TALLOC_FREE(subreq);
502 if (!WBC_ERROR_IS_OK(wbc_err)) {
503 tevent_req_error(req, wbc_err);
504 return;
505 }
506
507 close(state->wb_ctx->fd);
508 state->wb_ctx->fd = -1;
509
510 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
511 (char *)wb_resp->extra_data.data);
512 TALLOC_FREE(wb_resp);
513 if (tevent_req_nomem(subreq, req)) {
514 return;
515 }
516 tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
517 }
518
519 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
/* [<][>][^][v][top][bottom][index][help] */
520 {
521 struct tevent_req *req = tevent_req_callback_data(
522 subreq, struct tevent_req);
523 struct wb_open_pipe_state *state = tevent_req_data(
524 req, struct wb_open_pipe_state);
525 wbcErr wbc_err;
526
527 wbc_err = wb_connect_recv(subreq);
528 TALLOC_FREE(subreq);
529 if (!WBC_ERROR_IS_OK(wbc_err)) {
530 tevent_req_error(req, wbc_err);
531 return;
532 }
533 state->wb_ctx->is_priv = true;
534 tevent_req_done(req);
535 }
536
537 static wbcErr wb_open_pipe_recv(struct tevent_req *req)
/* [<][>][^][v][top][bottom][index][help] */
538 {
539 return tevent_req_simple_recv_wbcerr(req);
540 }
541
542 struct wb_trans_state {
543 struct wb_trans_state *prev, *next;
544 struct wb_context *wb_ctx;
545 struct tevent_context *ev;
546 struct winbindd_request *wb_req;
547 struct winbindd_response *wb_resp;
548 int num_retries;
549 bool need_priv;
550 };
551
552 static void wb_trans_connect_done(struct tevent_req *subreq);
553 static void wb_trans_done(struct tevent_req *subreq);
554 static void wb_trans_retry_wait_done(struct tevent_req *subreq);
555
556 struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
/* [<][>][^][v][top][bottom][index][help] */
557 struct tevent_context *ev,
558 struct wb_context *wb_ctx, bool need_priv,
559 struct winbindd_request *wb_req)
560 {
561 struct tevent_req *req, *subreq;
562 struct wb_trans_state *state;
563
564 req = tevent_req_create(mem_ctx, &state, struct wb_trans_state);
565 if (req == NULL) {
566 return NULL;
567 }
568 state->wb_ctx = wb_ctx;
569 state->ev = ev;
570 state->wb_req = wb_req;
571 state->num_retries = 10;
572 state->need_priv = need_priv;
573
574 if ((wb_ctx->fd == -1) || (need_priv && !wb_ctx->is_priv)) {
575 subreq = wb_open_pipe_send(state, ev, wb_ctx, need_priv);
576 if (subreq == NULL) {
577 goto fail;
578 }
579 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
580 return req;
581 }
582
583 subreq = wb_int_trans_send(state, ev, wb_ctx->queue, wb_ctx->fd,
584 wb_req);
585 if (subreq == NULL) {
586 goto fail;
587 }
588 tevent_req_set_callback(subreq, wb_trans_done, req);
589 return req;
590 fail:
591 TALLOC_FREE(req);
592 return NULL;
593 }
594
595 static bool wb_trans_retry(struct tevent_req *req,
/* [<][>][^][v][top][bottom][index][help] */
596 struct wb_trans_state *state,
597 wbcErr wbc_err)
598 {
599 struct tevent_req *subreq;
600
601 if (WBC_ERROR_IS_OK(wbc_err)) {
602 return false;
603 }
604
605 if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
606 /*
607 * Winbind not around or we can't connect to the pipe. Fail
608 * immediately.
609 */
610 tevent_req_error(req, wbc_err);
611 return true;
612 }
613
614 state->num_retries -= 1;
615 if (state->num_retries == 0) {
616 tevent_req_error(req, wbc_err);
617 return true;
618 }
619
620 /*
621 * The transfer as such failed, retry after one second
622 */
623
624 if (state->wb_ctx->fd != -1) {
625 close(state->wb_ctx->fd);
626 state->wb_ctx->fd = -1;
627 }
628
629 subreq = tevent_wakeup_send(state, state->ev,
630 timeval_current_ofs(1, 0));
631 if (tevent_req_nomem(subreq, req)) {
632 return true;
633 }
634 tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req);
635 return true;
636 }
637
638 static void wb_trans_retry_wait_done(struct tevent_req *subreq)
/* [<][>][^][v][top][bottom][index][help] */
639 {
640 struct tevent_req *req = tevent_req_callback_data(
641 subreq, struct tevent_req);
642 struct wb_trans_state *state = tevent_req_data(
643 req, struct wb_trans_state);
644 bool ret;
645
646 ret = tevent_wakeup_recv(subreq);
647 TALLOC_FREE(subreq);
648 if (!ret) {
649 tevent_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
650 return;
651 }
652
653 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
654 state->need_priv);
655 if (tevent_req_nomem(subreq, req)) {
656 return;
657 }
658 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
659 }
660
661 static void wb_trans_connect_done(struct tevent_req *subreq)
/* [<][>][^][v][top][bottom][index][help] */
662 {
663 struct tevent_req *req = tevent_req_callback_data(
664 subreq, struct tevent_req);
665 struct wb_trans_state *state = tevent_req_data(
666 req, struct wb_trans_state);
667 wbcErr wbc_err;
668
669 wbc_err = wb_open_pipe_recv(subreq);
670 TALLOC_FREE(subreq);
671
672 if (wb_trans_retry(req, state, wbc_err)) {
673 return;
674 }
675
676 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
677 state->wb_req);
678 if (tevent_req_nomem(subreq, req)) {
679 return;
680 }
681 tevent_req_set_callback(subreq, wb_trans_done, req);
682 }
683
684 static void wb_trans_done(struct tevent_req *subreq)
/* [<][>][^][v][top][bottom][index][help] */
685 {
686 struct tevent_req *req = tevent_req_callback_data(
687 subreq, struct tevent_req);
688 struct wb_trans_state *state = tevent_req_data(
689 req, struct wb_trans_state);
690 wbcErr wbc_err;
691
692 wbc_err = wb_int_trans_recv(subreq, state, &state->wb_resp);
693 TALLOC_FREE(subreq);
694
695 if (wb_trans_retry(req, state, wbc_err)) {
696 return;
697 }
698
699 tevent_req_done(req);
700 }
701
702 wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
/* [<][>][^][v][top][bottom][index][help] */
703 struct winbindd_response **presponse)
704 {
705 struct wb_trans_state *state = tevent_req_data(
706 req, struct wb_trans_state);
707 wbcErr wbc_err;
708
709 if (tevent_req_is_wbcerr(req, &wbc_err)) {
710 return wbc_err;
711 }
712
713 *presponse = talloc_move(mem_ctx, &state->wb_resp);
714 return WBC_ERR_SUCCESS;
715 }