2 Unix SMB/CIFS implementation.
3 Infrastructure for async winbind requests
4 Copyright (C) Volker Lendecke 2008
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.
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.
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/>.
21 #include "wbc_async.h"
24 struct async_req_queue
*queue
;
29 static int make_nonstd_fd(int fd
)
47 for (i
=0; i
<num_fds
; i
++) {
56 /****************************************************************************
57 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
61 Set close on exec also.
62 ****************************************************************************/
64 static int make_safe_fd(int fd
)
67 int new_fd
= make_nonstd_fd(fd
);
73 /* Socket should be nonblocking. */
76 #define FLAG_TO_SET O_NONBLOCK
79 #define FLAG_TO_SET O_NDELAY
81 #define FLAG_TO_SET FNDELAY
85 if ((flags
= fcntl(new_fd
, F_GETFL
)) == -1) {
90 if (fcntl(new_fd
, F_SETFL
, flags
) == -1) {
96 /* Socket should be closed on exec() */
98 result
= flags
= fcntl(new_fd
, F_GETFD
, 0);
101 result
= fcntl( new_fd
, F_SETFD
, flags
);
111 int sys_errno
= errno
;
118 static bool winbind_closed_fd(int fd
)
131 if ((select(fd
+1, &r_fds
, NULL
, NULL
, &tv
) == -1)
132 || FD_ISSET(fd
, &r_fds
)) {
139 struct wb_context
*wb_context_init(TALLOC_CTX
*mem_ctx
)
141 struct wb_context
*result
;
143 result
= talloc(mem_ctx
, struct wb_context
);
144 if (result
== NULL
) {
147 result
->queue
= async_req_queue_init(result
);
148 if (result
->queue
== NULL
) {
156 struct wb_connect_state
{
160 static void wbc_connect_connected(struct tevent_req
*subreq
);
162 static struct tevent_req
*wb_connect_send(TALLOC_CTX
*mem_ctx
,
163 struct tevent_context
*ev
,
164 struct wb_context
*wb_ctx
,
167 struct tevent_req
*result
, *subreq
;
168 struct wb_connect_state
*state
;
169 struct sockaddr_un sunaddr
;
174 result
= tevent_req_create(mem_ctx
, &state
, struct wb_connect_state
);
175 if (result
== NULL
) {
179 if (wb_ctx
->fd
!= -1) {
184 /* Check permissions on unix socket directory */
186 if (lstat(dir
, &st
) == -1) {
187 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
191 if (!S_ISDIR(st
.st_mode
) ||
192 (st
.st_uid
!= 0 && st
.st_uid
!= geteuid())) {
193 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
197 /* Connect to socket */
199 path
= talloc_asprintf(talloc_tos(), "%s/%s", dir
,
200 WINBINDD_SOCKET_NAME
);
205 sunaddr
.sun_family
= AF_UNIX
;
206 strlcpy(sunaddr
.sun_path
, path
, sizeof(sunaddr
.sun_path
));
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. */
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
;
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
);
226 subreq
= async_connect_send(mem_ctx
, ev
, wb_ctx
->fd
,
227 (struct sockaddr
*)&sunaddr
,
229 if (subreq
== NULL
) {
232 tevent_req_set_callback(subreq
, wbc_connect_connected
, result
);
234 if (!tevent_req_set_endtime(subreq
, ev
, timeval_current_ofs(30, 0))) {
241 tevent_req_error(result
, wbc_err
);
242 return tevent_req_post(result
, ev
);
248 static void wbc_connect_connected(struct tevent_req
*subreq
)
250 struct tevent_req
*req
= tevent_req_callback_data(
251 subreq
, struct tevent_req
);
254 res
= async_connect_recv(subreq
, &err
);
257 tevent_req_error(req
, map_wbc_err_from_errno(err
));
260 tevent_req_done(req
);
263 static wbcErr
wb_connect_recv(struct tevent_req
*req
)
265 return tevent_req_simple_recv_wbcerr(req
);
268 struct wb_int_trans_state
{
269 struct tevent_context
*ev
;
271 struct winbindd_request
*wb_req
;
272 struct winbindd_response
*wb_resp
;
275 static void wb_int_trans_write_done(struct tevent_req
*subreq
);
276 static void wb_int_trans_read_done(struct tevent_req
*subreq
);
278 static struct tevent_req
*wb_int_trans_send(TALLOC_CTX
*mem_ctx
,
279 struct tevent_context
*ev
,
280 struct tevent_queue
*queue
, int fd
,
281 struct winbindd_request
*wb_req
)
283 struct tevent_req
*result
, *subreq
;
284 struct wb_int_trans_state
*state
;
286 result
= tevent_req_create(mem_ctx
, &state
,
287 struct wb_int_trans_state
);
288 if (result
== NULL
) {
292 if (winbind_closed_fd(fd
)) {
293 tevent_req_error(result
, WBC_ERR_WINBIND_NOT_AVAILABLE
);
294 return tevent_req_post(result
, ev
);
299 state
->wb_req
= wb_req
;
300 state
->wb_req
->length
= sizeof(struct winbindd_request
);
301 state
->wb_req
->pid
= getpid();
303 subreq
= wb_req_write_send(state
, state
->ev
, queue
, state
->fd
,
305 if (subreq
== NULL
) {
308 tevent_req_set_callback(subreq
, wb_int_trans_write_done
, result
);
317 static void wb_int_trans_write_done(struct tevent_req
*subreq
)
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
);
325 wbc_err
= wb_req_write_recv(subreq
);
327 if (!WBC_ERROR_IS_OK(wbc_err
)) {
328 tevent_req_error(req
, wbc_err
);
332 subreq
= wb_resp_read_send(state
, state
->ev
, state
->fd
);
333 if (tevent_req_nomem(subreq
, req
)) {
336 tevent_req_set_callback(subreq
, wb_int_trans_read_done
, req
);
339 static void wb_int_trans_read_done(struct tevent_req
*subreq
)
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
);
347 wbc_err
= wb_resp_read_recv(subreq
, state
, &state
->wb_resp
);
349 if (!WBC_ERROR_IS_OK(wbc_err
)) {
350 tevent_req_error(req
, wbc_err
);
354 tevent_req_done(req
);
357 static wbcErr
wb_int_trans_recv(struct tevent_req
*req
,
359 struct winbindd_response
**presponse
)
361 struct wb_int_trans_state
*state
= tevent_req_data(
362 req
, struct wb_int_trans_state
);
365 if (tevent_req_is_wbcerr(req
, &wbc_err
)) {
369 *presponse
= talloc_move(mem_ctx
, &state
->wb_resp
);
370 return WBC_ERR_SUCCESS
;
373 static const char *winbindd_socket_dir(void)
375 #ifdef SOCKET_WRAPPER
378 env_dir
= getenv(WINBINDD_SOCKET_DIR_ENVVAR
);
384 return WINBINDD_SOCKET_DIR
;
387 struct wb_open_pipe_state
{
388 struct wb_context
*wb_ctx
;
389 struct tevent_context
*ev
;
391 struct winbindd_request wb_req
;
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
);
399 static struct tevent_req
*wb_open_pipe_send(TALLOC_CTX
*mem_ctx
,
400 struct tevent_context
*ev
,
401 struct wb_context
*wb_ctx
,
404 struct tevent_req
*result
, *subreq
;
405 struct wb_open_pipe_state
*state
;
407 result
= tevent_req_create(mem_ctx
, &state
, struct wb_open_pipe_state
);
408 if (result
== NULL
) {
411 state
->wb_ctx
= wb_ctx
;
413 state
->need_priv
= need_priv
;
415 if (wb_ctx
->fd
!= -1) {
420 subreq
= wb_connect_send(state
, ev
, wb_ctx
, winbindd_socket_dir());
421 if (subreq
== NULL
) {
424 tevent_req_set_callback(subreq
, wb_open_pipe_connect_nonpriv_done
,
433 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req
*subreq
)
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
);
441 wbc_err
= wb_connect_recv(subreq
);
443 if (!WBC_ERROR_IS_OK(wbc_err
)) {
444 state
->wb_ctx
->is_priv
= true;
445 tevent_req_error(req
, wbc_err
);
449 ZERO_STRUCT(state
->wb_req
);
450 state
->wb_req
.cmd
= WINBINDD_INTERFACE_VERSION
;
452 subreq
= wb_int_trans_send(state
, state
->ev
, NULL
, state
->wb_ctx
->fd
,
454 if (tevent_req_nomem(subreq
, req
)) {
457 tevent_req_set_callback(subreq
, wb_open_pipe_ping_done
, req
);
460 static void wb_open_pipe_ping_done(struct tevent_req
*subreq
)
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
;
469 wbc_err
= wb_int_trans_recv(subreq
, state
, &wb_resp
);
471 if (!WBC_ERROR_IS_OK(wbc_err
)) {
472 tevent_req_error(req
, wbc_err
);
476 if (!state
->need_priv
) {
477 tevent_req_done(req
);
481 state
->wb_req
.cmd
= WINBINDD_PRIV_PIPE_DIR
;
483 subreq
= wb_int_trans_send(state
, state
->ev
, NULL
, state
->wb_ctx
->fd
,
485 if (tevent_req_nomem(subreq
, req
)) {
488 tevent_req_set_callback(subreq
, wb_open_pipe_getpriv_done
, req
);
491 static void wb_open_pipe_getpriv_done(struct tevent_req
*subreq
)
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
;
500 wbc_err
= wb_int_trans_recv(subreq
, state
, &wb_resp
);
502 if (!WBC_ERROR_IS_OK(wbc_err
)) {
503 tevent_req_error(req
, wbc_err
);
507 close(state
->wb_ctx
->fd
);
508 state
->wb_ctx
->fd
= -1;
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
)) {
516 tevent_req_set_callback(subreq
, wb_open_pipe_connect_priv_done
, req
);
519 static void wb_open_pipe_connect_priv_done(struct tevent_req
*subreq
)
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
);
527 wbc_err
= wb_connect_recv(subreq
);
529 if (!WBC_ERROR_IS_OK(wbc_err
)) {
530 tevent_req_error(req
, wbc_err
);
533 state
->wb_ctx
->is_priv
= true;
534 tevent_req_done(req
);
537 static wbcErr
wb_open_pipe_recv(struct tevent_req
*req
)
539 return tevent_req_simple_recv_wbcerr(req
);
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
;
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
);
556 static void wb_trigger_trans(struct async_req
*req
)
558 struct wb_trans_state
*state
= talloc_get_type_abort(
559 req
->private_data
, struct wb_trans_state
);
560 struct tevent_req
*subreq
;
562 if ((state
->wb_ctx
->fd
== -1)
563 || (state
->need_priv
&& !state
->wb_ctx
->is_priv
)) {
565 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
567 if (async_req_nomem(subreq
, req
)) {
570 tevent_req_set_callback(subreq
, wb_trans_connect_done
, req
);
574 subreq
= wb_int_trans_send(state
, state
->ev
, NULL
, state
->wb_ctx
->fd
,
576 if (async_req_nomem(subreq
, req
)) {
579 tevent_req_set_callback(subreq
, wb_trans_done
, req
);
582 struct async_req
*wb_trans_send(TALLOC_CTX
*mem_ctx
, struct tevent_context
*ev
,
583 struct wb_context
*wb_ctx
, bool need_priv
,
584 struct winbindd_request
*wb_req
)
586 struct async_req
*result
;
587 struct wb_trans_state
*state
;
589 if (!async_req_setup(mem_ctx
, &result
, &state
,
590 struct wb_trans_state
)) {
593 state
->wb_ctx
= wb_ctx
;
595 state
->wb_req
= wb_req
;
596 state
->num_retries
= 10;
597 state
->need_priv
= need_priv
;
599 if (!async_req_enqueue(wb_ctx
->queue
, ev
, result
, wb_trigger_trans
)) {
609 static bool wb_trans_retry(struct async_req
*req
,
610 struct wb_trans_state
*state
,
613 struct tevent_req
*subreq
;
615 if (WBC_ERROR_IS_OK(wbc_err
)) {
619 if (wbc_err
== WBC_ERR_WINBIND_NOT_AVAILABLE
) {
621 * Winbind not around or we can't connect to the pipe. Fail
624 async_req_error(req
, wbc_err
);
628 state
->num_retries
-= 1;
629 if (state
->num_retries
== 0) {
630 async_req_error(req
, wbc_err
);
635 * The transfer as such failed, retry after one second
638 if (state
->wb_ctx
->fd
!= -1) {
639 close(state
->wb_ctx
->fd
);
640 state
->wb_ctx
->fd
= -1;
643 subreq
= tevent_wakeup_send(state
, state
->ev
,
644 timeval_current_ofs(1, 0));
645 if (async_req_nomem(subreq
, req
)) {
648 tevent_req_set_callback(subreq
, wb_trans_retry_wait_done
, req
);
652 static void wb_trans_retry_wait_done(struct tevent_req
*subreq
)
654 struct async_req
*req
= tevent_req_callback_data(
655 subreq
, struct async_req
);
656 struct wb_trans_state
*state
= talloc_get_type_abort(
657 req
->private_data
, struct wb_trans_state
);
660 ret
= tevent_wakeup_recv(subreq
);
663 async_req_error(req
, WBC_ERR_UNKNOWN_FAILURE
);
667 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
669 if (async_req_nomem(subreq
, req
)) {
672 tevent_req_set_callback(subreq
, wb_trans_connect_done
, req
);
675 static void wb_trans_connect_done(struct tevent_req
*subreq
)
677 struct async_req
*req
= tevent_req_callback_data(
678 subreq
, struct async_req
);
679 struct wb_trans_state
*state
= talloc_get_type_abort(
680 req
->private_data
, struct wb_trans_state
);
683 wbc_err
= wb_open_pipe_recv(subreq
);
686 if (wb_trans_retry(req
, state
, wbc_err
)) {
690 subreq
= wb_int_trans_send(state
, state
->ev
, NULL
, state
->wb_ctx
->fd
,
692 if (async_req_nomem(subreq
, req
)) {
695 tevent_req_set_callback(subreq
, wb_trans_done
, req
);
698 static void wb_trans_done(struct tevent_req
*subreq
)
700 struct async_req
*req
= tevent_req_callback_data(
701 subreq
, struct async_req
);
702 struct wb_trans_state
*state
= talloc_get_type_abort(
703 req
->private_data
, struct wb_trans_state
);
706 wbc_err
= wb_int_trans_recv(subreq
, state
, &state
->wb_resp
);
709 if (wb_trans_retry(req
, state
, wbc_err
)) {
716 wbcErr
wb_trans_recv(struct async_req
*req
, TALLOC_CTX
*mem_ctx
,
717 struct winbindd_response
**presponse
)
719 struct wb_trans_state
*state
= talloc_get_type_abort(
720 req
->private_data
, struct wb_trans_state
);
723 if (async_req_is_wbcerr(req
, &wbc_err
)) {
727 *presponse
= talloc_move(mem_ctx
, &state
->wb_resp
);
728 return WBC_ERR_SUCCESS
;