2 Unix SMB/CIFS implementation.
3 Infrastructure for async winbind requests
4 Copyright (C) Volker Lendecke 2008
6 ** NOTE! The following LGPL license applies to the wbclient
7 ** library. This does NOT imply that all of Samba is released
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 3 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Library General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "wbc_async.h"
27 wbcErr
map_wbc_err_from_errno(int error
)
32 return WBC_ERR_AUTH_ERROR
;
34 return WBC_ERR_NO_MEMORY
;
37 return WBC_ERR_UNKNOWN_FAILURE
;
41 bool tevent_req_is_wbcerr(struct tevent_req
*req
, wbcErr
*pwbc_err
)
43 enum tevent_req_state state
;
45 if (!tevent_req_is_error(req
, &state
, &error
)) {
46 *pwbc_err
= WBC_ERR_SUCCESS
;
51 case TEVENT_REQ_USER_ERROR
:
54 case TEVENT_REQ_TIMED_OUT
:
55 *pwbc_err
= WBC_ERR_UNKNOWN_FAILURE
;
57 case TEVENT_REQ_NO_MEMORY
:
58 *pwbc_err
= WBC_ERR_NO_MEMORY
;
61 *pwbc_err
= WBC_ERR_UNKNOWN_FAILURE
;
67 wbcErr
tevent_req_simple_recv_wbcerr(struct tevent_req
*req
)
71 if (tevent_req_is_wbcerr(req
, &wbc_err
)) {
75 return WBC_ERR_SUCCESS
;
79 struct tevent_queue
*queue
;
84 static int make_nonstd_fd(int fd
)
102 for (i
=0; i
<num_fds
; i
++) {
111 /****************************************************************************
112 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
116 Set close on exec also.
117 ****************************************************************************/
119 static int make_safe_fd(int fd
)
122 int new_fd
= make_nonstd_fd(fd
);
128 /* Socket should be nonblocking. */
131 #define FLAG_TO_SET O_NONBLOCK
134 #define FLAG_TO_SET O_NDELAY
136 #define FLAG_TO_SET FNDELAY
140 if ((flags
= fcntl(new_fd
, F_GETFL
)) == -1) {
144 flags
|= FLAG_TO_SET
;
145 if (fcntl(new_fd
, F_SETFL
, flags
) == -1) {
151 /* Socket should be closed on exec() */
153 result
= flags
= fcntl(new_fd
, F_GETFD
, 0);
156 result
= fcntl( new_fd
, F_SETFD
, flags
);
166 int sys_errno
= errno
;
173 struct wb_context
*wb_context_init(TALLOC_CTX
*mem_ctx
)
175 struct wb_context
*result
;
177 result
= talloc(mem_ctx
, struct wb_context
);
178 if (result
== NULL
) {
181 result
->queue
= tevent_queue_create(result
, "wb_trans");
182 if (result
->queue
== NULL
) {
187 result
->is_priv
= false;
191 struct wb_connect_state
{
195 static void wbc_connect_connected(struct tevent_req
*subreq
);
197 static struct tevent_req
*wb_connect_send(TALLOC_CTX
*mem_ctx
,
198 struct tevent_context
*ev
,
199 struct wb_context
*wb_ctx
,
202 struct tevent_req
*result
, *subreq
;
203 struct wb_connect_state
*state
;
204 struct sockaddr_un sunaddr
;
209 result
= tevent_req_create(mem_ctx
, &state
, struct wb_connect_state
);
210 if (result
== NULL
) {
214 if (wb_ctx
->fd
!= -1) {
219 /* Check permissions on unix socket directory */
221 if (lstat(dir
, &st
) == -1) {
222 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
226 if (!S_ISDIR(st
.st_mode
) ||
227 (st
.st_uid
!= 0 && st
.st_uid
!= geteuid())) {
228 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
232 /* Connect to socket */
234 path
= talloc_asprintf(talloc_tos(), "%s/%s", dir
,
235 WINBINDD_SOCKET_NAME
);
240 sunaddr
.sun_family
= AF_UNIX
;
241 strlcpy(sunaddr
.sun_path
, path
, sizeof(sunaddr
.sun_path
));
244 /* If socket file doesn't exist, don't bother trying to connect
245 with retry. This is an attempt to make the system usable when
246 the winbindd daemon is not running. */
248 if ((lstat(sunaddr
.sun_path
, &st
) == -1)
249 || !S_ISSOCK(st
.st_mode
)
250 || (st
.st_uid
!= 0 && st
.st_uid
!= geteuid())) {
251 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
255 wb_ctx
->fd
= make_safe_fd(socket(AF_UNIX
, SOCK_STREAM
, 0));
256 if (wb_ctx
->fd
== -1) {
257 wbc_err
= map_wbc_err_from_errno(errno
);
261 subreq
= async_connect_send(mem_ctx
, ev
, wb_ctx
->fd
,
262 (struct sockaddr
*)(void *)&sunaddr
,
264 if (subreq
== NULL
) {
267 tevent_req_set_callback(subreq
, wbc_connect_connected
, result
);
271 tevent_req_error(result
, wbc_err
);
272 return tevent_req_post(result
, ev
);
278 static void wbc_connect_connected(struct tevent_req
*subreq
)
280 struct tevent_req
*req
= tevent_req_callback_data(
281 subreq
, struct tevent_req
);
284 res
= async_connect_recv(subreq
, &err
);
287 tevent_req_error(req
, map_wbc_err_from_errno(err
));
290 tevent_req_done(req
);
293 static wbcErr
wb_connect_recv(struct tevent_req
*req
)
295 return tevent_req_simple_recv_wbcerr(req
);
298 static const char *winbindd_socket_dir(void)
300 #ifdef SOCKET_WRAPPER
303 env_dir
= getenv(WINBINDD_SOCKET_DIR_ENVVAR
);
309 return WINBINDD_SOCKET_DIR
;
312 struct wb_open_pipe_state
{
313 struct wb_context
*wb_ctx
;
314 struct tevent_context
*ev
;
316 struct winbindd_request wb_req
;
319 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req
*subreq
);
320 static void wb_open_pipe_ping_done(struct tevent_req
*subreq
);
321 static void wb_open_pipe_getpriv_done(struct tevent_req
*subreq
);
322 static void wb_open_pipe_connect_priv_done(struct tevent_req
*subreq
);
324 static struct tevent_req
*wb_open_pipe_send(TALLOC_CTX
*mem_ctx
,
325 struct tevent_context
*ev
,
326 struct wb_context
*wb_ctx
,
329 struct tevent_req
*result
, *subreq
;
330 struct wb_open_pipe_state
*state
;
332 result
= tevent_req_create(mem_ctx
, &state
, struct wb_open_pipe_state
);
333 if (result
== NULL
) {
336 state
->wb_ctx
= wb_ctx
;
338 state
->need_priv
= need_priv
;
340 if (wb_ctx
->fd
!= -1) {
345 subreq
= wb_connect_send(state
, ev
, wb_ctx
, winbindd_socket_dir());
346 if (subreq
== NULL
) {
349 tevent_req_set_callback(subreq
, wb_open_pipe_connect_nonpriv_done
,
358 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req
*subreq
)
360 struct tevent_req
*req
= tevent_req_callback_data(
361 subreq
, struct tevent_req
);
362 struct wb_open_pipe_state
*state
= tevent_req_data(
363 req
, struct wb_open_pipe_state
);
366 wbc_err
= wb_connect_recv(subreq
);
368 if (!WBC_ERROR_IS_OK(wbc_err
)) {
369 state
->wb_ctx
->is_priv
= true;
370 tevent_req_error(req
, wbc_err
);
374 ZERO_STRUCT(state
->wb_req
);
375 state
->wb_req
.cmd
= WINBINDD_INTERFACE_VERSION
;
376 state
->wb_req
.pid
= getpid();
378 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
379 state
->wb_ctx
->fd
, &state
->wb_req
);
380 if (tevent_req_nomem(subreq
, req
)) {
383 tevent_req_set_callback(subreq
, wb_open_pipe_ping_done
, req
);
386 static void wb_open_pipe_ping_done(struct tevent_req
*subreq
)
388 struct tevent_req
*req
= tevent_req_callback_data(
389 subreq
, struct tevent_req
);
390 struct wb_open_pipe_state
*state
= tevent_req_data(
391 req
, struct wb_open_pipe_state
);
392 struct winbindd_response
*wb_resp
;
395 ret
= wb_simple_trans_recv(subreq
, state
, &wb_resp
, &err
);
398 tevent_req_error(req
, map_wbc_err_from_errno(err
));
402 if (!state
->need_priv
) {
403 tevent_req_done(req
);
407 state
->wb_req
.cmd
= WINBINDD_PRIV_PIPE_DIR
;
408 state
->wb_req
.pid
= getpid();
410 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
411 state
->wb_ctx
->fd
, &state
->wb_req
);
412 if (tevent_req_nomem(subreq
, req
)) {
415 tevent_req_set_callback(subreq
, wb_open_pipe_getpriv_done
, req
);
418 static void wb_open_pipe_getpriv_done(struct tevent_req
*subreq
)
420 struct tevent_req
*req
= tevent_req_callback_data(
421 subreq
, struct tevent_req
);
422 struct wb_open_pipe_state
*state
= tevent_req_data(
423 req
, struct wb_open_pipe_state
);
424 struct winbindd_response
*wb_resp
= NULL
;
427 ret
= wb_simple_trans_recv(subreq
, state
, &wb_resp
, &err
);
430 tevent_req_error(req
, map_wbc_err_from_errno(err
));
434 close(state
->wb_ctx
->fd
);
435 state
->wb_ctx
->fd
= -1;
437 subreq
= wb_connect_send(state
, state
->ev
, state
->wb_ctx
,
438 (char *)wb_resp
->extra_data
.data
);
439 TALLOC_FREE(wb_resp
);
440 if (tevent_req_nomem(subreq
, req
)) {
443 tevent_req_set_callback(subreq
, wb_open_pipe_connect_priv_done
, req
);
446 static void wb_open_pipe_connect_priv_done(struct tevent_req
*subreq
)
448 struct tevent_req
*req
= tevent_req_callback_data(
449 subreq
, struct tevent_req
);
450 struct wb_open_pipe_state
*state
= tevent_req_data(
451 req
, struct wb_open_pipe_state
);
454 wbc_err
= wb_connect_recv(subreq
);
456 if (!WBC_ERROR_IS_OK(wbc_err
)) {
457 tevent_req_error(req
, wbc_err
);
460 state
->wb_ctx
->is_priv
= true;
461 tevent_req_done(req
);
464 static wbcErr
wb_open_pipe_recv(struct tevent_req
*req
)
466 return tevent_req_simple_recv_wbcerr(req
);
469 struct wb_trans_state
{
470 struct wb_trans_state
*prev
, *next
;
471 struct wb_context
*wb_ctx
;
472 struct tevent_context
*ev
;
473 struct winbindd_request
*wb_req
;
474 struct winbindd_response
*wb_resp
;
478 static bool closed_fd(int fd
)
492 selret
= select(fd
+1, &r_fds
, NULL
, NULL
, &tv
);
499 return (FD_ISSET(fd
, &r_fds
));
502 static void wb_trans_trigger(struct tevent_req
*req
, void *private_data
);
503 static void wb_trans_connect_done(struct tevent_req
*subreq
);
504 static void wb_trans_done(struct tevent_req
*subreq
);
505 static void wb_trans_retry_wait_done(struct tevent_req
*subreq
);
507 struct tevent_req
*wb_trans_send(TALLOC_CTX
*mem_ctx
,
508 struct tevent_context
*ev
,
509 struct wb_context
*wb_ctx
, bool need_priv
,
510 struct winbindd_request
*wb_req
)
512 struct tevent_req
*req
;
513 struct wb_trans_state
*state
;
515 req
= tevent_req_create(mem_ctx
, &state
, struct wb_trans_state
);
519 state
->wb_ctx
= wb_ctx
;
521 state
->wb_req
= wb_req
;
522 state
->need_priv
= need_priv
;
524 if (!tevent_queue_add(wb_ctx
->queue
, ev
, req
, wb_trans_trigger
,
526 tevent_req_nomem(NULL
, req
);
527 return tevent_req_post(req
, ev
);
532 static void wb_trans_trigger(struct tevent_req
*req
, void *private_data
)
534 struct wb_trans_state
*state
= tevent_req_data(
535 req
, struct wb_trans_state
);
536 struct tevent_req
*subreq
;
538 if ((state
->wb_ctx
->fd
!= -1) && closed_fd(state
->wb_ctx
->fd
)) {
539 close(state
->wb_ctx
->fd
);
540 state
->wb_ctx
->fd
= -1;
543 if ((state
->wb_ctx
->fd
== -1)
544 || (state
->need_priv
&& !state
->wb_ctx
->is_priv
)) {
545 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
547 if (tevent_req_nomem(subreq
, req
)) {
550 tevent_req_set_callback(subreq
, wb_trans_connect_done
, req
);
554 state
->wb_req
->pid
= getpid();
556 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
557 state
->wb_ctx
->fd
, state
->wb_req
);
558 if (tevent_req_nomem(subreq
, req
)) {
561 tevent_req_set_callback(subreq
, wb_trans_done
, req
);
564 static bool wb_trans_retry(struct tevent_req
*req
,
565 struct wb_trans_state
*state
,
568 struct tevent_req
*subreq
;
570 if (WBC_ERROR_IS_OK(wbc_err
)) {
574 if (wbc_err
== WBC_ERR_WINBIND_NOT_AVAILABLE
) {
576 * Winbind not around or we can't connect to the pipe. Fail
579 tevent_req_error(req
, wbc_err
);
584 * The transfer as such failed, retry after one second
587 if (state
->wb_ctx
->fd
!= -1) {
588 close(state
->wb_ctx
->fd
);
589 state
->wb_ctx
->fd
= -1;
592 subreq
= tevent_wakeup_send(state
, state
->ev
,
593 timeval_current_ofs(1, 0));
594 if (tevent_req_nomem(subreq
, req
)) {
597 tevent_req_set_callback(subreq
, wb_trans_retry_wait_done
, req
);
601 static void wb_trans_retry_wait_done(struct tevent_req
*subreq
)
603 struct tevent_req
*req
= tevent_req_callback_data(
604 subreq
, struct tevent_req
);
605 struct wb_trans_state
*state
= tevent_req_data(
606 req
, struct wb_trans_state
);
609 ret
= tevent_wakeup_recv(subreq
);
612 tevent_req_error(req
, WBC_ERR_UNKNOWN_FAILURE
);
616 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
618 if (tevent_req_nomem(subreq
, req
)) {
621 tevent_req_set_callback(subreq
, wb_trans_connect_done
, req
);
624 static void wb_trans_connect_done(struct tevent_req
*subreq
)
626 struct tevent_req
*req
= tevent_req_callback_data(
627 subreq
, struct tevent_req
);
628 struct wb_trans_state
*state
= tevent_req_data(
629 req
, struct wb_trans_state
);
632 wbc_err
= wb_open_pipe_recv(subreq
);
635 if (wb_trans_retry(req
, state
, wbc_err
)) {
639 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
640 state
->wb_ctx
->fd
, state
->wb_req
);
641 if (tevent_req_nomem(subreq
, req
)) {
644 tevent_req_set_callback(subreq
, wb_trans_done
, req
);
647 static void wb_trans_done(struct tevent_req
*subreq
)
649 struct tevent_req
*req
= tevent_req_callback_data(
650 subreq
, struct tevent_req
);
651 struct wb_trans_state
*state
= tevent_req_data(
652 req
, struct wb_trans_state
);
655 ret
= wb_simple_trans_recv(subreq
, state
, &state
->wb_resp
, &err
);
658 && wb_trans_retry(req
, state
, map_wbc_err_from_errno(err
))) {
662 tevent_req_done(req
);
665 wbcErr
wb_trans_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
666 struct winbindd_response
**presponse
)
668 struct wb_trans_state
*state
= tevent_req_data(
669 req
, struct wb_trans_state
);
672 if (tevent_req_is_wbcerr(req
, &wbc_err
)) {
676 *presponse
= talloc_move(mem_ctx
, &state
->wb_resp
);
677 return WBC_ERR_SUCCESS
;