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 wbcErr
map_wbc_err_from_errno(int error
)
28 return WBC_ERR_AUTH_ERROR
;
30 return WBC_ERR_NO_MEMORY
;
33 return WBC_ERR_UNKNOWN_FAILURE
;
37 bool tevent_req_is_wbcerr(struct tevent_req
*req
, wbcErr
*pwbc_err
)
39 enum tevent_req_state state
;
41 if (!tevent_req_is_error(req
, &state
, &error
)) {
42 *pwbc_err
= WBC_ERR_SUCCESS
;
47 case TEVENT_REQ_USER_ERROR
:
50 case TEVENT_REQ_TIMED_OUT
:
51 *pwbc_err
= WBC_ERR_UNKNOWN_FAILURE
;
53 case TEVENT_REQ_NO_MEMORY
:
54 *pwbc_err
= WBC_ERR_NO_MEMORY
;
57 *pwbc_err
= WBC_ERR_UNKNOWN_FAILURE
;
63 wbcErr
tevent_req_simple_recv_wbcerr(struct tevent_req
*req
)
67 if (tevent_req_is_wbcerr(req
, &wbc_err
)) {
71 return WBC_ERR_SUCCESS
;
75 struct tevent_queue
*queue
;
80 static int make_nonstd_fd(int fd
)
98 for (i
=0; i
<num_fds
; i
++) {
107 /****************************************************************************
108 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
112 Set close on exec also.
113 ****************************************************************************/
115 static int make_safe_fd(int fd
)
118 int new_fd
= make_nonstd_fd(fd
);
124 /* Socket should be nonblocking. */
127 #define FLAG_TO_SET O_NONBLOCK
130 #define FLAG_TO_SET O_NDELAY
132 #define FLAG_TO_SET FNDELAY
136 if ((flags
= fcntl(new_fd
, F_GETFL
)) == -1) {
140 flags
|= FLAG_TO_SET
;
141 if (fcntl(new_fd
, F_SETFL
, flags
) == -1) {
147 /* Socket should be closed on exec() */
149 result
= flags
= fcntl(new_fd
, F_GETFD
, 0);
152 result
= fcntl( new_fd
, F_SETFD
, flags
);
162 int sys_errno
= errno
;
169 struct wb_context
*wb_context_init(TALLOC_CTX
*mem_ctx
)
171 struct wb_context
*result
;
173 result
= talloc(mem_ctx
, struct wb_context
);
174 if (result
== NULL
) {
177 result
->queue
= tevent_queue_create(result
, "wb_trans");
178 if (result
->queue
== NULL
) {
183 result
->is_priv
= false;
187 struct wb_connect_state
{
191 static void wbc_connect_connected(struct tevent_req
*subreq
);
193 static struct tevent_req
*wb_connect_send(TALLOC_CTX
*mem_ctx
,
194 struct tevent_context
*ev
,
195 struct wb_context
*wb_ctx
,
198 struct tevent_req
*result
, *subreq
;
199 struct wb_connect_state
*state
;
200 struct sockaddr_un sunaddr
;
205 result
= tevent_req_create(mem_ctx
, &state
, struct wb_connect_state
);
206 if (result
== NULL
) {
210 if (wb_ctx
->fd
!= -1) {
215 /* Check permissions on unix socket directory */
217 if (lstat(dir
, &st
) == -1) {
218 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
222 if (!S_ISDIR(st
.st_mode
) ||
223 (st
.st_uid
!= 0 && st
.st_uid
!= geteuid())) {
224 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
228 /* Connect to socket */
230 path
= talloc_asprintf(talloc_tos(), "%s/%s", dir
,
231 WINBINDD_SOCKET_NAME
);
236 sunaddr
.sun_family
= AF_UNIX
;
237 strlcpy(sunaddr
.sun_path
, path
, sizeof(sunaddr
.sun_path
));
240 /* If socket file doesn't exist, don't bother trying to connect
241 with retry. This is an attempt to make the system usable when
242 the winbindd daemon is not running. */
244 if ((lstat(sunaddr
.sun_path
, &st
) == -1)
245 || !S_ISSOCK(st
.st_mode
)
246 || (st
.st_uid
!= 0 && st
.st_uid
!= geteuid())) {
247 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
251 wb_ctx
->fd
= make_safe_fd(socket(AF_UNIX
, SOCK_STREAM
, 0));
252 if (wb_ctx
->fd
== -1) {
253 wbc_err
= map_wbc_err_from_errno(errno
);
257 subreq
= async_connect_send(mem_ctx
, ev
, wb_ctx
->fd
,
258 (struct sockaddr
*)(void *)&sunaddr
,
260 if (subreq
== NULL
) {
263 tevent_req_set_callback(subreq
, wbc_connect_connected
, result
);
267 tevent_req_error(result
, wbc_err
);
268 return tevent_req_post(result
, ev
);
274 static void wbc_connect_connected(struct tevent_req
*subreq
)
276 struct tevent_req
*req
= tevent_req_callback_data(
277 subreq
, struct tevent_req
);
280 res
= async_connect_recv(subreq
, &err
);
283 tevent_req_error(req
, map_wbc_err_from_errno(err
));
286 tevent_req_done(req
);
289 static wbcErr
wb_connect_recv(struct tevent_req
*req
)
291 return tevent_req_simple_recv_wbcerr(req
);
294 static const char *winbindd_socket_dir(void)
296 #ifdef SOCKET_WRAPPER
299 env_dir
= getenv(WINBINDD_SOCKET_DIR_ENVVAR
);
305 return WINBINDD_SOCKET_DIR
;
308 struct wb_open_pipe_state
{
309 struct wb_context
*wb_ctx
;
310 struct tevent_context
*ev
;
312 struct winbindd_request wb_req
;
315 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req
*subreq
);
316 static void wb_open_pipe_ping_done(struct tevent_req
*subreq
);
317 static void wb_open_pipe_getpriv_done(struct tevent_req
*subreq
);
318 static void wb_open_pipe_connect_priv_done(struct tevent_req
*subreq
);
320 static struct tevent_req
*wb_open_pipe_send(TALLOC_CTX
*mem_ctx
,
321 struct tevent_context
*ev
,
322 struct wb_context
*wb_ctx
,
325 struct tevent_req
*result
, *subreq
;
326 struct wb_open_pipe_state
*state
;
328 result
= tevent_req_create(mem_ctx
, &state
, struct wb_open_pipe_state
);
329 if (result
== NULL
) {
332 state
->wb_ctx
= wb_ctx
;
334 state
->need_priv
= need_priv
;
336 if (wb_ctx
->fd
!= -1) {
341 subreq
= wb_connect_send(state
, ev
, wb_ctx
, winbindd_socket_dir());
342 if (subreq
== NULL
) {
345 tevent_req_set_callback(subreq
, wb_open_pipe_connect_nonpriv_done
,
354 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req
*subreq
)
356 struct tevent_req
*req
= tevent_req_callback_data(
357 subreq
, struct tevent_req
);
358 struct wb_open_pipe_state
*state
= tevent_req_data(
359 req
, struct wb_open_pipe_state
);
362 wbc_err
= wb_connect_recv(subreq
);
364 if (!WBC_ERROR_IS_OK(wbc_err
)) {
365 state
->wb_ctx
->is_priv
= true;
366 tevent_req_error(req
, wbc_err
);
370 ZERO_STRUCT(state
->wb_req
);
371 state
->wb_req
.cmd
= WINBINDD_INTERFACE_VERSION
;
372 state
->wb_req
.pid
= getpid();
374 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
375 state
->wb_ctx
->fd
, &state
->wb_req
);
376 if (tevent_req_nomem(subreq
, req
)) {
379 tevent_req_set_callback(subreq
, wb_open_pipe_ping_done
, req
);
382 static void wb_open_pipe_ping_done(struct tevent_req
*subreq
)
384 struct tevent_req
*req
= tevent_req_callback_data(
385 subreq
, struct tevent_req
);
386 struct wb_open_pipe_state
*state
= tevent_req_data(
387 req
, struct wb_open_pipe_state
);
388 struct winbindd_response
*wb_resp
;
391 ret
= wb_simple_trans_recv(subreq
, state
, &wb_resp
, &err
);
394 tevent_req_error(req
, map_wbc_err_from_errno(err
));
398 if (!state
->need_priv
) {
399 tevent_req_done(req
);
403 state
->wb_req
.cmd
= WINBINDD_PRIV_PIPE_DIR
;
404 state
->wb_req
.pid
= getpid();
406 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
407 state
->wb_ctx
->fd
, &state
->wb_req
);
408 if (tevent_req_nomem(subreq
, req
)) {
411 tevent_req_set_callback(subreq
, wb_open_pipe_getpriv_done
, req
);
414 static void wb_open_pipe_getpriv_done(struct tevent_req
*subreq
)
416 struct tevent_req
*req
= tevent_req_callback_data(
417 subreq
, struct tevent_req
);
418 struct wb_open_pipe_state
*state
= tevent_req_data(
419 req
, struct wb_open_pipe_state
);
420 struct winbindd_response
*wb_resp
= NULL
;
423 ret
= wb_simple_trans_recv(subreq
, state
, &wb_resp
, &err
);
426 tevent_req_error(req
, map_wbc_err_from_errno(err
));
430 close(state
->wb_ctx
->fd
);
431 state
->wb_ctx
->fd
= -1;
433 subreq
= wb_connect_send(state
, state
->ev
, state
->wb_ctx
,
434 (char *)wb_resp
->extra_data
.data
);
435 TALLOC_FREE(wb_resp
);
436 if (tevent_req_nomem(subreq
, req
)) {
439 tevent_req_set_callback(subreq
, wb_open_pipe_connect_priv_done
, req
);
442 static void wb_open_pipe_connect_priv_done(struct tevent_req
*subreq
)
444 struct tevent_req
*req
= tevent_req_callback_data(
445 subreq
, struct tevent_req
);
446 struct wb_open_pipe_state
*state
= tevent_req_data(
447 req
, struct wb_open_pipe_state
);
450 wbc_err
= wb_connect_recv(subreq
);
452 if (!WBC_ERROR_IS_OK(wbc_err
)) {
453 tevent_req_error(req
, wbc_err
);
456 state
->wb_ctx
->is_priv
= true;
457 tevent_req_done(req
);
460 static wbcErr
wb_open_pipe_recv(struct tevent_req
*req
)
462 return tevent_req_simple_recv_wbcerr(req
);
465 struct wb_trans_state
{
466 struct wb_trans_state
*prev
, *next
;
467 struct wb_context
*wb_ctx
;
468 struct tevent_context
*ev
;
469 struct winbindd_request
*wb_req
;
470 struct winbindd_response
*wb_resp
;
474 static bool closed_fd(int fd
)
488 selret
= select(fd
+1, &r_fds
, NULL
, NULL
, &tv
);
495 return (FD_ISSET(fd
, &r_fds
));
498 static void wb_trans_trigger(struct tevent_req
*req
, void *private_data
);
499 static void wb_trans_connect_done(struct tevent_req
*subreq
);
500 static void wb_trans_done(struct tevent_req
*subreq
);
501 static void wb_trans_retry_wait_done(struct tevent_req
*subreq
);
503 struct tevent_req
*wb_trans_send(TALLOC_CTX
*mem_ctx
,
504 struct tevent_context
*ev
,
505 struct wb_context
*wb_ctx
, bool need_priv
,
506 struct winbindd_request
*wb_req
)
508 struct tevent_req
*req
;
509 struct wb_trans_state
*state
;
511 req
= tevent_req_create(mem_ctx
, &state
, struct wb_trans_state
);
515 state
->wb_ctx
= wb_ctx
;
517 state
->wb_req
= wb_req
;
518 state
->need_priv
= need_priv
;
520 if (!tevent_queue_add(wb_ctx
->queue
, ev
, req
, wb_trans_trigger
,
522 tevent_req_nomem(NULL
, req
);
523 return tevent_req_post(req
, ev
);
528 static void wb_trans_trigger(struct tevent_req
*req
, void *private_data
)
530 struct wb_trans_state
*state
= tevent_req_data(
531 req
, struct wb_trans_state
);
532 struct tevent_req
*subreq
;
534 if ((state
->wb_ctx
->fd
!= -1) && closed_fd(state
->wb_ctx
->fd
)) {
535 close(state
->wb_ctx
->fd
);
536 state
->wb_ctx
->fd
= -1;
539 if ((state
->wb_ctx
->fd
== -1)
540 || (state
->need_priv
&& !state
->wb_ctx
->is_priv
)) {
541 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
543 if (tevent_req_nomem(subreq
, req
)) {
546 tevent_req_set_callback(subreq
, wb_trans_connect_done
, req
);
550 state
->wb_req
->pid
= getpid();
552 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
553 state
->wb_ctx
->fd
, state
->wb_req
);
554 if (tevent_req_nomem(subreq
, req
)) {
557 tevent_req_set_callback(subreq
, wb_trans_done
, req
);
560 static bool wb_trans_retry(struct tevent_req
*req
,
561 struct wb_trans_state
*state
,
564 struct tevent_req
*subreq
;
566 if (WBC_ERROR_IS_OK(wbc_err
)) {
570 if (wbc_err
== WBC_ERR_WINBIND_NOT_AVAILABLE
) {
572 * Winbind not around or we can't connect to the pipe. Fail
575 tevent_req_error(req
, wbc_err
);
580 * The transfer as such failed, retry after one second
583 if (state
->wb_ctx
->fd
!= -1) {
584 close(state
->wb_ctx
->fd
);
585 state
->wb_ctx
->fd
= -1;
588 subreq
= tevent_wakeup_send(state
, state
->ev
,
589 timeval_current_ofs(1, 0));
590 if (tevent_req_nomem(subreq
, req
)) {
593 tevent_req_set_callback(subreq
, wb_trans_retry_wait_done
, req
);
597 static void wb_trans_retry_wait_done(struct tevent_req
*subreq
)
599 struct tevent_req
*req
= tevent_req_callback_data(
600 subreq
, struct tevent_req
);
601 struct wb_trans_state
*state
= tevent_req_data(
602 req
, struct wb_trans_state
);
605 ret
= tevent_wakeup_recv(subreq
);
608 tevent_req_error(req
, WBC_ERR_UNKNOWN_FAILURE
);
612 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
614 if (tevent_req_nomem(subreq
, req
)) {
617 tevent_req_set_callback(subreq
, wb_trans_connect_done
, req
);
620 static void wb_trans_connect_done(struct tevent_req
*subreq
)
622 struct tevent_req
*req
= tevent_req_callback_data(
623 subreq
, struct tevent_req
);
624 struct wb_trans_state
*state
= tevent_req_data(
625 req
, struct wb_trans_state
);
628 wbc_err
= wb_open_pipe_recv(subreq
);
631 if (wb_trans_retry(req
, state
, wbc_err
)) {
635 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
636 state
->wb_ctx
->fd
, state
->wb_req
);
637 if (tevent_req_nomem(subreq
, req
)) {
640 tevent_req_set_callback(subreq
, wb_trans_done
, req
);
643 static void wb_trans_done(struct tevent_req
*subreq
)
645 struct tevent_req
*req
= tevent_req_callback_data(
646 subreq
, struct tevent_req
);
647 struct wb_trans_state
*state
= tevent_req_data(
648 req
, struct wb_trans_state
);
651 ret
= wb_simple_trans_recv(subreq
, state
, &state
->wb_resp
, &err
);
654 && wb_trans_retry(req
, state
, map_wbc_err_from_errno(err
))) {
658 tevent_req_done(req
);
661 wbcErr
wb_trans_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
662 struct winbindd_response
**presponse
)
664 struct wb_trans_state
*state
= tevent_req_data(
665 req
, struct wb_trans_state
);
668 if (tevent_req_is_wbcerr(req
, &wbc_err
)) {
672 *presponse
= talloc_move(mem_ctx
, &state
->wb_resp
);
673 return WBC_ERR_SUCCESS
;