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