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
);
265 if (!tevent_req_set_endtime(subreq
, ev
, timeval_current_ofs(30, 0))) {
272 tevent_req_error(result
, wbc_err
);
273 return tevent_req_post(result
, ev
);
279 static void wbc_connect_connected(struct tevent_req
*subreq
)
281 struct tevent_req
*req
= tevent_req_callback_data(
282 subreq
, struct tevent_req
);
285 res
= async_connect_recv(subreq
, &err
);
288 tevent_req_error(req
, map_wbc_err_from_errno(err
));
291 tevent_req_done(req
);
294 static wbcErr
wb_connect_recv(struct tevent_req
*req
)
296 return tevent_req_simple_recv_wbcerr(req
);
299 static const char *winbindd_socket_dir(void)
301 #ifdef SOCKET_WRAPPER
304 env_dir
= getenv(WINBINDD_SOCKET_DIR_ENVVAR
);
310 return WINBINDD_SOCKET_DIR
;
313 struct wb_open_pipe_state
{
314 struct wb_context
*wb_ctx
;
315 struct tevent_context
*ev
;
317 struct winbindd_request wb_req
;
320 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req
*subreq
);
321 static void wb_open_pipe_ping_done(struct tevent_req
*subreq
);
322 static void wb_open_pipe_getpriv_done(struct tevent_req
*subreq
);
323 static void wb_open_pipe_connect_priv_done(struct tevent_req
*subreq
);
325 static struct tevent_req
*wb_open_pipe_send(TALLOC_CTX
*mem_ctx
,
326 struct tevent_context
*ev
,
327 struct wb_context
*wb_ctx
,
330 struct tevent_req
*result
, *subreq
;
331 struct wb_open_pipe_state
*state
;
333 result
= tevent_req_create(mem_ctx
, &state
, struct wb_open_pipe_state
);
334 if (result
== NULL
) {
337 state
->wb_ctx
= wb_ctx
;
339 state
->need_priv
= need_priv
;
341 if (wb_ctx
->fd
!= -1) {
346 subreq
= wb_connect_send(state
, ev
, wb_ctx
, winbindd_socket_dir());
347 if (subreq
== NULL
) {
350 tevent_req_set_callback(subreq
, wb_open_pipe_connect_nonpriv_done
,
359 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req
*subreq
)
361 struct tevent_req
*req
= tevent_req_callback_data(
362 subreq
, struct tevent_req
);
363 struct wb_open_pipe_state
*state
= tevent_req_data(
364 req
, struct wb_open_pipe_state
);
367 wbc_err
= wb_connect_recv(subreq
);
369 if (!WBC_ERROR_IS_OK(wbc_err
)) {
370 state
->wb_ctx
->is_priv
= true;
371 tevent_req_error(req
, wbc_err
);
375 ZERO_STRUCT(state
->wb_req
);
376 state
->wb_req
.cmd
= WINBINDD_INTERFACE_VERSION
;
377 state
->wb_req
.pid
= getpid();
379 subreq
= wb_simple_trans_send(state
, state
->ev
, state
->wb_ctx
->queue
,
380 state
->wb_ctx
->fd
, &state
->wb_req
);
381 if (tevent_req_nomem(subreq
, req
)) {
384 tevent_req_set_callback(subreq
, wb_open_pipe_ping_done
, req
);
387 static void wb_open_pipe_ping_done(struct tevent_req
*subreq
)
389 struct tevent_req
*req
= tevent_req_callback_data(
390 subreq
, struct tevent_req
);
391 struct wb_open_pipe_state
*state
= tevent_req_data(
392 req
, struct wb_open_pipe_state
);
393 struct winbindd_response
*wb_resp
;
396 ret
= wb_simple_trans_recv(subreq
, state
, &wb_resp
, &err
);
399 tevent_req_error(req
, map_wbc_err_from_errno(err
));
403 if (!state
->need_priv
) {
404 tevent_req_done(req
);
408 state
->wb_req
.cmd
= WINBINDD_PRIV_PIPE_DIR
;
409 state
->wb_req
.pid
= getpid();
411 subreq
= wb_simple_trans_send(state
, state
->ev
, state
->wb_ctx
->queue
,
412 state
->wb_ctx
->fd
, &state
->wb_req
);
413 if (tevent_req_nomem(subreq
, req
)) {
416 tevent_req_set_callback(subreq
, wb_open_pipe_getpriv_done
, req
);
419 static void wb_open_pipe_getpriv_done(struct tevent_req
*subreq
)
421 struct tevent_req
*req
= tevent_req_callback_data(
422 subreq
, struct tevent_req
);
423 struct wb_open_pipe_state
*state
= tevent_req_data(
424 req
, struct wb_open_pipe_state
);
425 struct winbindd_response
*wb_resp
= NULL
;
428 ret
= wb_simple_trans_recv(subreq
, state
, &wb_resp
, &err
);
431 tevent_req_error(req
, map_wbc_err_from_errno(err
));
435 close(state
->wb_ctx
->fd
);
436 state
->wb_ctx
->fd
= -1;
438 subreq
= wb_connect_send(state
, state
->ev
, state
->wb_ctx
,
439 (char *)wb_resp
->extra_data
.data
);
440 TALLOC_FREE(wb_resp
);
441 if (tevent_req_nomem(subreq
, req
)) {
444 tevent_req_set_callback(subreq
, wb_open_pipe_connect_priv_done
, req
);
447 static void wb_open_pipe_connect_priv_done(struct tevent_req
*subreq
)
449 struct tevent_req
*req
= tevent_req_callback_data(
450 subreq
, struct tevent_req
);
451 struct wb_open_pipe_state
*state
= tevent_req_data(
452 req
, struct wb_open_pipe_state
);
455 wbc_err
= wb_connect_recv(subreq
);
457 if (!WBC_ERROR_IS_OK(wbc_err
)) {
458 tevent_req_error(req
, wbc_err
);
461 state
->wb_ctx
->is_priv
= true;
462 tevent_req_done(req
);
465 static wbcErr
wb_open_pipe_recv(struct tevent_req
*req
)
467 return tevent_req_simple_recv_wbcerr(req
);
470 struct wb_trans_state
{
471 struct wb_trans_state
*prev
, *next
;
472 struct wb_context
*wb_ctx
;
473 struct tevent_context
*ev
;
474 struct winbindd_request
*wb_req
;
475 struct winbindd_response
*wb_resp
;
480 static void wb_trans_connect_done(struct tevent_req
*subreq
);
481 static void wb_trans_done(struct tevent_req
*subreq
);
482 static void wb_trans_retry_wait_done(struct tevent_req
*subreq
);
484 struct tevent_req
*wb_trans_send(TALLOC_CTX
*mem_ctx
,
485 struct tevent_context
*ev
,
486 struct wb_context
*wb_ctx
, bool need_priv
,
487 struct winbindd_request
*wb_req
)
489 struct tevent_req
*req
, *subreq
;
490 struct wb_trans_state
*state
;
492 req
= tevent_req_create(mem_ctx
, &state
, struct wb_trans_state
);
496 state
->wb_ctx
= wb_ctx
;
498 state
->wb_req
= wb_req
;
499 state
->num_retries
= 10;
500 state
->need_priv
= need_priv
;
502 if ((wb_ctx
->fd
== -1) || (need_priv
&& !wb_ctx
->is_priv
)) {
503 subreq
= wb_open_pipe_send(state
, ev
, wb_ctx
, need_priv
);
504 if (subreq
== NULL
) {
507 tevent_req_set_callback(subreq
, wb_trans_connect_done
, req
);
511 state
->wb_req
->pid
= getpid();
513 subreq
= wb_simple_trans_send(state
, ev
, wb_ctx
->queue
, wb_ctx
->fd
,
515 if (subreq
== NULL
) {
518 tevent_req_set_callback(subreq
, wb_trans_done
, req
);
525 static bool wb_trans_retry(struct tevent_req
*req
,
526 struct wb_trans_state
*state
,
529 struct tevent_req
*subreq
;
531 if (WBC_ERROR_IS_OK(wbc_err
)) {
535 if (wbc_err
== WBC_ERR_WINBIND_NOT_AVAILABLE
) {
537 * Winbind not around or we can't connect to the pipe. Fail
540 tevent_req_error(req
, wbc_err
);
544 state
->num_retries
-= 1;
545 if (state
->num_retries
== 0) {
546 tevent_req_error(req
, wbc_err
);
551 * The transfer as such failed, retry after one second
554 if (state
->wb_ctx
->fd
!= -1) {
555 close(state
->wb_ctx
->fd
);
556 state
->wb_ctx
->fd
= -1;
559 subreq
= tevent_wakeup_send(state
, state
->ev
,
560 timeval_current_ofs(1, 0));
561 if (tevent_req_nomem(subreq
, req
)) {
564 tevent_req_set_callback(subreq
, wb_trans_retry_wait_done
, req
);
568 static void wb_trans_retry_wait_done(struct tevent_req
*subreq
)
570 struct tevent_req
*req
= tevent_req_callback_data(
571 subreq
, struct tevent_req
);
572 struct wb_trans_state
*state
= tevent_req_data(
573 req
, struct wb_trans_state
);
576 ret
= tevent_wakeup_recv(subreq
);
579 tevent_req_error(req
, WBC_ERR_UNKNOWN_FAILURE
);
583 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
585 if (tevent_req_nomem(subreq
, req
)) {
588 tevent_req_set_callback(subreq
, wb_trans_connect_done
, req
);
591 static void wb_trans_connect_done(struct tevent_req
*subreq
)
593 struct tevent_req
*req
= tevent_req_callback_data(
594 subreq
, struct tevent_req
);
595 struct wb_trans_state
*state
= tevent_req_data(
596 req
, struct wb_trans_state
);
599 wbc_err
= wb_open_pipe_recv(subreq
);
602 if (wb_trans_retry(req
, state
, wbc_err
)) {
606 subreq
= wb_simple_trans_send(state
, state
->ev
, state
->wb_ctx
->queue
,
607 state
->wb_ctx
->fd
, state
->wb_req
);
608 if (tevent_req_nomem(subreq
, req
)) {
611 tevent_req_set_callback(subreq
, wb_trans_done
, req
);
614 static void wb_trans_done(struct tevent_req
*subreq
)
616 struct tevent_req
*req
= tevent_req_callback_data(
617 subreq
, struct tevent_req
);
618 struct wb_trans_state
*state
= tevent_req_data(
619 req
, struct wb_trans_state
);
622 ret
= wb_simple_trans_recv(subreq
, state
, &state
->wb_resp
, &err
);
625 if (wb_trans_retry(req
, state
, map_wbc_err_from_errno(err
))) {
629 tevent_req_done(req
);
632 wbcErr
wb_trans_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
633 struct winbindd_response
**presponse
)
635 struct wb_trans_state
*state
= tevent_req_data(
636 req
, struct wb_trans_state
);
639 if (tevent_req_is_wbcerr(req
, &wbc_err
)) {
643 *presponse
= talloc_move(mem_ctx
, &state
->wb_resp
);
644 return WBC_ERR_SUCCESS
;