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"
23 static int make_nonstd_fd(int fd
)
41 for (i
=0; i
<num_fds
; i
++) {
50 /****************************************************************************
51 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
55 Set close on exec also.
56 ****************************************************************************/
58 static int make_safe_fd(int fd
)
61 int new_fd
= make_nonstd_fd(fd
);
67 /* Socket should be nonblocking. */
70 #define FLAG_TO_SET O_NONBLOCK
73 #define FLAG_TO_SET O_NDELAY
75 #define FLAG_TO_SET FNDELAY
79 if ((flags
= fcntl(new_fd
, F_GETFL
)) == -1) {
84 if (fcntl(new_fd
, F_SETFL
, flags
) == -1) {
90 /* Socket should be closed on exec() */
92 result
= flags
= fcntl(new_fd
, F_GETFD
, 0);
95 result
= fcntl( new_fd
, F_SETFD
, flags
);
105 int sys_errno
= errno
;
112 static bool winbind_closed_fd(int fd
)
125 if ((select(fd
+1, &r_fds
, NULL
, NULL
, &tv
) == -1)
126 || FD_ISSET(fd
, &r_fds
)) {
133 struct wb_context
*wb_context_init(TALLOC_CTX
*mem_ctx
)
135 struct wb_context
*result
;
137 result
= talloc(mem_ctx
, struct wb_context
);
138 if (result
== NULL
) {
141 result
->queue
= async_req_queue_init(result
);
142 if (result
->queue
== NULL
) {
150 static struct async_req
*wb_connect_send(TALLOC_CTX
*mem_ctx
,
151 struct tevent_context
*ev
,
152 struct wb_context
*wb_ctx
,
155 struct async_req
*req
;
156 struct sockaddr_un sunaddr
;
161 if (wb_ctx
->fd
!= -1) {
166 /* Check permissions on unix socket directory */
168 if (lstat(dir
, &st
) == -1) {
169 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
173 if (!S_ISDIR(st
.st_mode
) ||
174 (st
.st_uid
!= 0 && st
.st_uid
!= geteuid())) {
175 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
179 /* Connect to socket */
181 path
= talloc_asprintf(talloc_tos(), "%s/%s", dir
,
182 WINBINDD_SOCKET_NAME
);
187 sunaddr
.sun_family
= AF_UNIX
;
188 strlcpy(sunaddr
.sun_path
, path
, sizeof(sunaddr
.sun_path
));
191 /* If socket file doesn't exist, don't bother trying to connect
192 with retry. This is an attempt to make the system usable when
193 the winbindd daemon is not running. */
195 if ((lstat(sunaddr
.sun_path
, &st
) == -1)
196 || !S_ISSOCK(st
.st_mode
)
197 || (st
.st_uid
!= 0 && st
.st_uid
!= geteuid())) {
198 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
202 wb_ctx
->fd
= make_safe_fd(socket(AF_UNIX
, SOCK_STREAM
, 0));
203 if (wb_ctx
->fd
== -1) {
204 wbc_err
= map_wbc_err_from_errno(errno
);
208 req
= async_connect_send(mem_ctx
, ev
, wb_ctx
->fd
,
209 (struct sockaddr
*)&sunaddr
,
214 if (!async_req_set_timeout(req
, ev
, timeval_set(30, 0))) {
222 wbc_err
= WBC_ERR_NO_MEMORY
;
224 req
= async_req_new(mem_ctx
);
228 if (async_post_error(req
, ev
, wbc_err
)) {
235 static wbcErr
wb_connect_recv(struct async_req
*req
)
237 return async_req_simple_recv_wbcerr(req
);
240 static struct winbindd_request
*winbindd_request_copy(
242 const struct winbindd_request
*req
)
244 struct winbindd_request
*result
;
246 result
= (struct winbindd_request
*)TALLOC_MEMDUP(
247 mem_ctx
, req
, sizeof(struct winbindd_request
));
248 if (result
== NULL
) {
252 if (result
->extra_len
== 0) {
256 result
->extra_data
.data
= (char *)TALLOC_MEMDUP(
257 result
, result
->extra_data
.data
, result
->extra_len
);
258 if (result
->extra_data
.data
== NULL
) {
265 struct wb_int_trans_state
{
266 struct tevent_context
*ev
;
268 struct winbindd_request
*wb_req
;
269 struct winbindd_response
*wb_resp
;
272 static void wb_int_trans_write_done(struct async_req
*subreq
);
273 static void wb_int_trans_read_done(struct async_req
*subreq
);
275 static struct async_req
*wb_int_trans_send(TALLOC_CTX
*mem_ctx
,
276 struct tevent_context
*ev
, int fd
,
277 struct winbindd_request
*wb_req
)
279 struct async_req
*result
;
280 struct async_req
*subreq
;
281 struct wb_int_trans_state
*state
;
283 if (!async_req_setup(mem_ctx
, &result
, &state
,
284 struct wb_int_trans_state
)) {
288 if (winbind_closed_fd(fd
)) {
289 if (!async_post_error(result
, ev
,
290 WBC_ERR_WINBIND_NOT_AVAILABLE
)) {
298 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
, state
->fd
, state
->wb_req
);
304 if (subreq
== NULL
) {
307 subreq
->async
.fn
= wb_int_trans_write_done
;
308 subreq
->async
.priv
= result
;
317 static void wb_int_trans_write_done(struct async_req
*subreq
)
319 struct async_req
*req
= talloc_get_type_abort(
320 subreq
->async
.priv
, struct async_req
);
321 struct wb_int_trans_state
*state
= talloc_get_type_abort(
322 req
->private_data
, struct wb_int_trans_state
);
325 wbc_err
= wb_req_write_recv(subreq
);
327 if (!WBC_ERROR_IS_OK(wbc_err
)) {
328 async_req_error(req
, wbc_err
);
332 subreq
= wb_resp_read_send(state
, state
->ev
, state
->fd
);
333 if (subreq
== NULL
) {
334 async_req_error(req
, WBC_ERR_NO_MEMORY
);
336 subreq
->async
.fn
= wb_int_trans_read_done
;
337 subreq
->async
.priv
= req
;
340 static void wb_int_trans_read_done(struct async_req
*subreq
)
342 struct async_req
*req
= talloc_get_type_abort(
343 subreq
->async
.priv
, struct async_req
);
344 struct wb_int_trans_state
*state
= talloc_get_type_abort(
345 req
->private_data
, struct wb_int_trans_state
);
348 wbc_err
= wb_resp_read_recv(subreq
, state
, &state
->wb_resp
);
350 if (!WBC_ERROR_IS_OK(wbc_err
)) {
351 async_req_error(req
, wbc_err
);
358 static wbcErr
wb_int_trans_recv(struct async_req
*req
,
360 struct winbindd_response
**presponse
)
362 struct wb_int_trans_state
*state
= talloc_get_type_abort(
363 req
->private_data
, struct wb_int_trans_state
);
366 if (async_req_is_wbcerr(req
, &wbc_err
)) {
370 *presponse
= talloc_move(mem_ctx
, &state
->wb_resp
);
371 return WBC_ERR_SUCCESS
;
374 static const char *winbindd_socket_dir(void)
376 #ifdef SOCKET_WRAPPER
379 env_dir
= getenv(WINBINDD_SOCKET_DIR_ENVVAR
);
385 return WINBINDD_SOCKET_DIR
;
388 struct wb_open_pipe_state
{
389 struct wb_context
*wb_ctx
;
390 struct tevent_context
*ev
;
392 struct winbindd_request wb_req
;
395 static void wb_open_pipe_connect_nonpriv_done(struct async_req
*subreq
);
396 static void wb_open_pipe_ping_done(struct async_req
*subreq
);
397 static void wb_open_pipe_getpriv_done(struct async_req
*subreq
);
398 static void wb_open_pipe_connect_priv_done(struct async_req
*subreq
);
400 static struct async_req
*wb_open_pipe_send(TALLOC_CTX
*mem_ctx
,
401 struct tevent_context
*ev
,
402 struct wb_context
*wb_ctx
,
405 struct async_req
*result
;
406 struct async_req
*subreq
;
407 struct wb_open_pipe_state
*state
;
409 if (!async_req_setup(mem_ctx
, &result
, &state
,
410 struct wb_open_pipe_state
)) {
413 state
->wb_ctx
= wb_ctx
;
415 state
->need_priv
= need_priv
;
417 if (wb_ctx
->fd
!= -1) {
422 subreq
= wb_connect_send(state
, ev
, wb_ctx
, winbindd_socket_dir());
423 if (subreq
== NULL
) {
427 subreq
->async
.fn
= wb_open_pipe_connect_nonpriv_done
;
428 subreq
->async
.priv
= result
;
436 static void wb_open_pipe_connect_nonpriv_done(struct async_req
*subreq
)
438 struct async_req
*req
= talloc_get_type_abort(
439 subreq
->async
.priv
, struct async_req
);
440 struct wb_open_pipe_state
*state
= talloc_get_type_abort(
441 req
->private_data
, struct wb_open_pipe_state
);
444 wbc_err
= wb_connect_recv(subreq
);
446 if (!WBC_ERROR_IS_OK(wbc_err
)) {
447 state
->wb_ctx
->is_priv
= true;
448 async_req_error(req
, wbc_err
);
452 ZERO_STRUCT(state
->wb_req
);
453 state
->wb_req
.cmd
= WINBINDD_INTERFACE_VERSION
;
455 subreq
= wb_int_trans_send(state
, state
->ev
, state
->wb_ctx
->fd
,
457 if (async_req_nomem(subreq
, req
)) {
461 subreq
->async
.fn
= wb_open_pipe_ping_done
;
462 subreq
->async
.priv
= req
;
465 static void wb_open_pipe_ping_done(struct async_req
*subreq
)
467 struct async_req
*req
= talloc_get_type_abort(
468 subreq
->async
.priv
, struct async_req
);
469 struct wb_open_pipe_state
*state
= talloc_get_type_abort(
470 req
->private_data
, struct wb_open_pipe_state
);
471 struct winbindd_response
*wb_resp
;
474 wbc_err
= wb_int_trans_recv(subreq
, state
, &wb_resp
);
476 if (!WBC_ERROR_IS_OK(wbc_err
)) {
477 async_req_error(req
, wbc_err
);
481 if (!state
->need_priv
) {
486 state
->wb_req
.cmd
= WINBINDD_PRIV_PIPE_DIR
;
488 subreq
= wb_int_trans_send(state
, state
->ev
, state
->wb_ctx
->fd
,
490 if (async_req_nomem(subreq
, req
)) {
494 subreq
->async
.fn
= wb_open_pipe_getpriv_done
;
495 subreq
->async
.priv
= req
;
498 static void wb_open_pipe_getpriv_done(struct async_req
*subreq
)
500 struct async_req
*req
= talloc_get_type_abort(
501 subreq
->async
.priv
, struct async_req
);
502 struct wb_open_pipe_state
*state
= talloc_get_type_abort(
503 req
->private_data
, struct wb_open_pipe_state
);
504 struct winbindd_response
*wb_resp
= NULL
;
507 wbc_err
= wb_int_trans_recv(subreq
, state
, &wb_resp
);
509 if (!WBC_ERROR_IS_OK(wbc_err
)) {
510 async_req_error(req
, wbc_err
);
514 close(state
->wb_ctx
->fd
);
515 state
->wb_ctx
->fd
= -1;
517 subreq
= wb_connect_send(state
, state
->ev
, state
->wb_ctx
,
518 (char *)wb_resp
->extra_data
.data
);
519 TALLOC_FREE(wb_resp
);
520 if (async_req_nomem(subreq
, req
)) {
524 subreq
->async
.fn
= wb_open_pipe_connect_priv_done
;
525 subreq
->async
.priv
= req
;
528 static void wb_open_pipe_connect_priv_done(struct async_req
*subreq
)
530 struct async_req
*req
= talloc_get_type_abort(
531 subreq
->async
.priv
, struct async_req
);
532 struct wb_open_pipe_state
*state
= talloc_get_type_abort(
533 req
->private_data
, struct wb_open_pipe_state
);
536 wbc_err
= wb_connect_recv(subreq
);
538 if (!WBC_ERROR_IS_OK(wbc_err
)) {
539 async_req_error(req
, wbc_err
);
542 state
->wb_ctx
->is_priv
= true;
546 static wbcErr
wb_open_pipe_recv(struct async_req
*req
)
548 return async_req_simple_recv_wbcerr(req
);
551 struct wb_trans_state
{
552 struct wb_trans_state
*prev
, *next
;
553 struct wb_context
*wb_ctx
;
554 struct tevent_context
*ev
;
555 struct winbindd_request
*wb_req
;
556 struct winbindd_response
*wb_resp
;
561 static void wb_trans_connect_done(struct async_req
*subreq
);
562 static void wb_trans_done(struct async_req
*subreq
);
563 static void wb_trans_retry_wait_done(struct async_req
*subreq
);
565 static void wb_trigger_trans(struct async_req
*req
)
567 struct wb_trans_state
*state
= talloc_get_type_abort(
568 req
->private_data
, struct wb_trans_state
);
569 struct async_req
*subreq
;
571 if ((state
->wb_ctx
->fd
== -1)
572 || (state
->need_priv
&& !state
->wb_ctx
->is_priv
)) {
574 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
576 if (async_req_nomem(subreq
, req
)) {
579 subreq
->async
.fn
= wb_trans_connect_done
;
580 subreq
->async
.priv
= req
;
584 subreq
= wb_int_trans_send(state
, state
->ev
, state
->wb_ctx
->fd
,
586 if (async_req_nomem(subreq
, req
)) {
589 subreq
->async
.fn
= wb_trans_done
;
590 subreq
->async
.priv
= req
;
593 struct async_req
*wb_trans_send(TALLOC_CTX
*mem_ctx
, struct tevent_context
*ev
,
594 struct wb_context
*wb_ctx
, bool need_priv
,
595 const struct winbindd_request
*wb_req
)
597 struct async_req
*result
;
598 struct wb_trans_state
*state
;
600 if (!async_req_setup(mem_ctx
, &result
, &state
,
601 struct wb_trans_state
)) {
604 state
->wb_ctx
= wb_ctx
;
606 state
->wb_req
= winbindd_request_copy(state
, wb_req
);
607 if (state
->wb_req
== NULL
) {
610 state
->num_retries
= 10;
611 state
->need_priv
= need_priv
;
613 if (!async_req_enqueue(wb_ctx
->queue
, ev
, result
, wb_trigger_trans
)) {
623 static bool wb_trans_retry(struct async_req
*req
,
624 struct wb_trans_state
*state
,
627 struct async_req
*subreq
;
629 if (WBC_ERROR_IS_OK(wbc_err
)) {
633 if (wbc_err
== WBC_ERR_WINBIND_NOT_AVAILABLE
) {
635 * Winbind not around or we can't connect to the pipe. Fail
638 async_req_error(req
, wbc_err
);
642 state
->num_retries
-= 1;
643 if (state
->num_retries
== 0) {
644 async_req_error(req
, wbc_err
);
649 * The transfer as such failed, retry after one second
652 if (state
->wb_ctx
->fd
!= -1) {
653 close(state
->wb_ctx
->fd
);
654 state
->wb_ctx
->fd
= -1;
657 subreq
= async_wait_send(state
, state
->ev
, timeval_set(1, 0));
658 if (async_req_nomem(subreq
, req
)) {
662 subreq
->async
.fn
= wb_trans_retry_wait_done
;
663 subreq
->async
.priv
= req
;
667 static void wb_trans_retry_wait_done(struct async_req
*subreq
)
669 struct async_req
*req
= talloc_get_type_abort(
670 subreq
->async
.priv
, struct async_req
);
671 struct wb_trans_state
*state
= talloc_get_type_abort(
672 req
->private_data
, struct wb_trans_state
);
675 ret
= async_wait_recv(subreq
);
678 async_req_error(req
, WBC_ERR_UNKNOWN_FAILURE
);
682 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
684 if (async_req_nomem(subreq
, req
)) {
687 subreq
->async
.fn
= wb_trans_connect_done
;
688 subreq
->async
.priv
= req
;
691 static void wb_trans_connect_done(struct async_req
*subreq
)
693 struct async_req
*req
= talloc_get_type_abort(
694 subreq
->async
.priv
, struct async_req
);
695 struct wb_trans_state
*state
= talloc_get_type_abort(
696 req
->private_data
, struct wb_trans_state
);
699 wbc_err
= wb_open_pipe_recv(subreq
);
702 if (wb_trans_retry(req
, state
, wbc_err
)) {
706 subreq
= wb_int_trans_send(state
, state
->ev
, state
->wb_ctx
->fd
,
708 if (async_req_nomem(subreq
, req
)) {
712 subreq
->async
.fn
= wb_trans_done
;
713 subreq
->async
.priv
= req
;
716 static void wb_trans_done(struct async_req
*subreq
)
718 struct async_req
*req
= talloc_get_type_abort(
719 subreq
->async
.priv
, struct async_req
);
720 struct wb_trans_state
*state
= talloc_get_type_abort(
721 req
->private_data
, struct wb_trans_state
);
724 wbc_err
= wb_int_trans_recv(subreq
, state
, &state
->wb_resp
);
727 if (wb_trans_retry(req
, state
, wbc_err
)) {
734 wbcErr
wb_trans_recv(struct async_req
*req
, TALLOC_CTX
*mem_ctx
,
735 struct winbindd_response
**presponse
)
737 struct wb_trans_state
*state
= talloc_get_type_abort(
738 req
->private_data
, struct wb_trans_state
);
741 if (async_req_is_wbcerr(req
, &wbc_err
)) {
745 *presponse
= talloc_move(mem_ctx
, &state
->wb_resp
);
746 return WBC_ERR_SUCCESS
;