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 "system/filesys.h"
26 #include "system/network.h"
29 #include "lib/async_req/async_sock.h"
30 #include "nsswitch/winbind_struct_protocol.h"
31 #include "nsswitch/libwbclient/wbclient.h"
32 #include "nsswitch/libwbclient/wbc_async.h"
34 wbcErr
map_wbc_err_from_errno(int error
)
39 return WBC_ERR_AUTH_ERROR
;
41 return WBC_ERR_NO_MEMORY
;
44 return WBC_ERR_UNKNOWN_FAILURE
;
48 bool tevent_req_is_wbcerr(struct tevent_req
*req
, wbcErr
*pwbc_err
)
50 enum tevent_req_state state
;
52 if (!tevent_req_is_error(req
, &state
, &error
)) {
53 *pwbc_err
= WBC_ERR_SUCCESS
;
58 case TEVENT_REQ_USER_ERROR
:
61 case TEVENT_REQ_TIMED_OUT
:
62 *pwbc_err
= WBC_ERR_UNKNOWN_FAILURE
;
64 case TEVENT_REQ_NO_MEMORY
:
65 *pwbc_err
= WBC_ERR_NO_MEMORY
;
68 *pwbc_err
= WBC_ERR_UNKNOWN_FAILURE
;
74 wbcErr
tevent_req_simple_recv_wbcerr(struct tevent_req
*req
)
78 if (tevent_req_is_wbcerr(req
, &wbc_err
)) {
82 return WBC_ERR_SUCCESS
;
85 struct wbc_debug_ops
{
86 void (*debug
)(void *context
, enum wbcDebugLevel level
,
87 const char *fmt
, va_list ap
) PRINTF_ATTRIBUTE(3,0);
92 struct tevent_queue
*queue
;
96 struct wbc_debug_ops debug_ops
;
99 static int make_nonstd_fd(int fd
)
117 for (i
=0; i
<num_fds
; i
++) {
126 /****************************************************************************
127 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
131 Set close on exec also.
132 ****************************************************************************/
134 static int make_safe_fd(int fd
)
137 int new_fd
= make_nonstd_fd(fd
);
143 /* Socket should be nonblocking. */
146 #define FLAG_TO_SET O_NONBLOCK
149 #define FLAG_TO_SET O_NDELAY
151 #define FLAG_TO_SET FNDELAY
155 if ((flags
= fcntl(new_fd
, F_GETFL
)) == -1) {
159 flags
|= FLAG_TO_SET
;
160 if (fcntl(new_fd
, F_SETFL
, flags
) == -1) {
166 /* Socket should be closed on exec() */
168 result
= flags
= fcntl(new_fd
, F_GETFD
, 0);
171 result
= fcntl( new_fd
, F_SETFD
, flags
);
181 int sys_errno
= errno
;
188 /* Just put a prototype to avoid moving the whole function around */
189 static const char *winbindd_socket_dir(void);
191 struct wb_context
*wb_context_init(TALLOC_CTX
*mem_ctx
, const char* dir
)
193 struct wb_context
*result
;
195 result
= talloc_zero(mem_ctx
, struct wb_context
);
196 if (result
== NULL
) {
199 result
->queue
= tevent_queue_create(result
, "wb_trans");
200 if (result
->queue
== NULL
) {
205 result
->is_priv
= false;
208 result
->dir
= talloc_strdup(result
, dir
);
210 result
->dir
= winbindd_socket_dir();
212 if (result
->dir
== NULL
) {
219 struct wb_connect_state
{
223 static void wbc_connect_connected(struct tevent_req
*subreq
);
225 static struct tevent_req
*wb_connect_send(TALLOC_CTX
*mem_ctx
,
226 struct tevent_context
*ev
,
227 struct wb_context
*wb_ctx
,
230 struct tevent_req
*result
, *subreq
;
231 struct wb_connect_state
*state
;
232 struct sockaddr_un sunaddr
;
237 result
= tevent_req_create(mem_ctx
, &state
, struct wb_connect_state
);
238 if (result
== NULL
) {
242 if (wb_ctx
->fd
!= -1) {
247 /* Check permissions on unix socket directory */
249 if (lstat(dir
, &st
) == -1) {
250 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
254 if (!S_ISDIR(st
.st_mode
) ||
255 (st
.st_uid
!= 0 && st
.st_uid
!= geteuid())) {
256 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
260 /* Connect to socket */
262 path
= talloc_asprintf(mem_ctx
, "%s/%s", dir
,
263 WINBINDD_SOCKET_NAME
);
268 sunaddr
.sun_family
= AF_UNIX
;
269 strlcpy(sunaddr
.sun_path
, path
, sizeof(sunaddr
.sun_path
));
272 /* If socket file doesn't exist, don't bother trying to connect
273 with retry. This is an attempt to make the system usable when
274 the winbindd daemon is not running. */
276 if ((lstat(sunaddr
.sun_path
, &st
) == -1)
277 || !S_ISSOCK(st
.st_mode
)
278 || (st
.st_uid
!= 0 && st
.st_uid
!= geteuid())) {
279 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
283 wb_ctx
->fd
= make_safe_fd(socket(AF_UNIX
, SOCK_STREAM
, 0));
284 if (wb_ctx
->fd
== -1) {
285 wbc_err
= map_wbc_err_from_errno(errno
);
289 subreq
= async_connect_send(mem_ctx
, ev
, wb_ctx
->fd
,
290 (struct sockaddr
*)(void *)&sunaddr
,
292 if (subreq
== NULL
) {
295 tevent_req_set_callback(subreq
, wbc_connect_connected
, result
);
299 tevent_req_error(result
, wbc_err
);
300 return tevent_req_post(result
, ev
);
306 static void wbc_connect_connected(struct tevent_req
*subreq
)
308 struct tevent_req
*req
= tevent_req_callback_data(
309 subreq
, struct tevent_req
);
312 res
= async_connect_recv(subreq
, &err
);
315 tevent_req_error(req
, map_wbc_err_from_errno(err
));
318 tevent_req_done(req
);
321 static wbcErr
wb_connect_recv(struct tevent_req
*req
)
323 return tevent_req_simple_recv_wbcerr(req
);
326 static const char *winbindd_socket_dir(void)
328 #ifdef SOCKET_WRAPPER
331 env_dir
= getenv(WINBINDD_SOCKET_DIR_ENVVAR
);
337 return WINBINDD_SOCKET_DIR
;
340 struct wb_open_pipe_state
{
341 struct wb_context
*wb_ctx
;
342 struct tevent_context
*ev
;
344 struct winbindd_request wb_req
;
347 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req
*subreq
);
348 static void wb_open_pipe_ping_done(struct tevent_req
*subreq
);
349 static void wb_open_pipe_getpriv_done(struct tevent_req
*subreq
);
350 static void wb_open_pipe_connect_priv_done(struct tevent_req
*subreq
);
352 static struct tevent_req
*wb_open_pipe_send(TALLOC_CTX
*mem_ctx
,
353 struct tevent_context
*ev
,
354 struct wb_context
*wb_ctx
,
357 struct tevent_req
*result
, *subreq
;
358 struct wb_open_pipe_state
*state
;
360 result
= tevent_req_create(mem_ctx
, &state
, struct wb_open_pipe_state
);
361 if (result
== NULL
) {
364 state
->wb_ctx
= wb_ctx
;
366 state
->need_priv
= need_priv
;
368 if (wb_ctx
->fd
!= -1) {
373 subreq
= wb_connect_send(state
, ev
, wb_ctx
, wb_ctx
->dir
);
374 if (subreq
== NULL
) {
377 tevent_req_set_callback(subreq
, wb_open_pipe_connect_nonpriv_done
,
386 static void wb_open_pipe_connect_nonpriv_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
);
394 wbc_err
= wb_connect_recv(subreq
);
396 if (!WBC_ERROR_IS_OK(wbc_err
)) {
397 state
->wb_ctx
->is_priv
= true;
398 tevent_req_error(req
, wbc_err
);
402 ZERO_STRUCT(state
->wb_req
);
403 state
->wb_req
.cmd
= WINBINDD_INTERFACE_VERSION
;
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_ping_done
, req
);
414 static void wb_open_pipe_ping_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
;
423 ret
= wb_simple_trans_recv(subreq
, state
, &wb_resp
, &err
);
426 tevent_req_error(req
, map_wbc_err_from_errno(err
));
430 if (!state
->need_priv
) {
431 tevent_req_done(req
);
435 state
->wb_req
.cmd
= WINBINDD_PRIV_PIPE_DIR
;
436 state
->wb_req
.pid
= getpid();
438 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
439 state
->wb_ctx
->fd
, &state
->wb_req
);
440 if (tevent_req_nomem(subreq
, req
)) {
443 tevent_req_set_callback(subreq
, wb_open_pipe_getpriv_done
, req
);
446 static void wb_open_pipe_getpriv_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
);
452 struct winbindd_response
*wb_resp
= NULL
;
455 ret
= wb_simple_trans_recv(subreq
, state
, &wb_resp
, &err
);
458 tevent_req_error(req
, map_wbc_err_from_errno(err
));
462 close(state
->wb_ctx
->fd
);
463 state
->wb_ctx
->fd
= -1;
465 subreq
= wb_connect_send(state
, state
->ev
, state
->wb_ctx
,
466 (char *)wb_resp
->extra_data
.data
);
467 TALLOC_FREE(wb_resp
);
468 if (tevent_req_nomem(subreq
, req
)) {
471 tevent_req_set_callback(subreq
, wb_open_pipe_connect_priv_done
, req
);
474 static void wb_open_pipe_connect_priv_done(struct tevent_req
*subreq
)
476 struct tevent_req
*req
= tevent_req_callback_data(
477 subreq
, struct tevent_req
);
478 struct wb_open_pipe_state
*state
= tevent_req_data(
479 req
, struct wb_open_pipe_state
);
482 wbc_err
= wb_connect_recv(subreq
);
484 if (!WBC_ERROR_IS_OK(wbc_err
)) {
485 tevent_req_error(req
, wbc_err
);
488 state
->wb_ctx
->is_priv
= true;
489 tevent_req_done(req
);
492 static wbcErr
wb_open_pipe_recv(struct tevent_req
*req
)
494 return tevent_req_simple_recv_wbcerr(req
);
497 struct wb_trans_state
{
498 struct wb_trans_state
*prev
, *next
;
499 struct wb_context
*wb_ctx
;
500 struct tevent_context
*ev
;
501 struct winbindd_request
*wb_req
;
502 struct winbindd_response
*wb_resp
;
506 static bool closed_fd(int fd
)
520 selret
= select(fd
+1, &r_fds
, NULL
, NULL
, &tv
);
527 return (FD_ISSET(fd
, &r_fds
));
530 static void wb_trans_trigger(struct tevent_req
*req
, void *private_data
);
531 static void wb_trans_connect_done(struct tevent_req
*subreq
);
532 static void wb_trans_done(struct tevent_req
*subreq
);
533 static void wb_trans_retry_wait_done(struct tevent_req
*subreq
);
535 struct tevent_req
*wb_trans_send(TALLOC_CTX
*mem_ctx
,
536 struct tevent_context
*ev
,
537 struct wb_context
*wb_ctx
, bool need_priv
,
538 struct winbindd_request
*wb_req
)
540 struct tevent_req
*req
;
541 struct wb_trans_state
*state
;
543 req
= tevent_req_create(mem_ctx
, &state
, struct wb_trans_state
);
547 state
->wb_ctx
= wb_ctx
;
549 state
->wb_req
= wb_req
;
550 state
->need_priv
= need_priv
;
552 if (!tevent_queue_add(wb_ctx
->queue
, ev
, req
, wb_trans_trigger
,
554 tevent_req_nomem(NULL
, req
);
555 return tevent_req_post(req
, ev
);
560 static void wb_trans_trigger(struct tevent_req
*req
, void *private_data
)
562 struct wb_trans_state
*state
= tevent_req_data(
563 req
, struct wb_trans_state
);
564 struct tevent_req
*subreq
;
566 if ((state
->wb_ctx
->fd
!= -1) && closed_fd(state
->wb_ctx
->fd
)) {
567 close(state
->wb_ctx
->fd
);
568 state
->wb_ctx
->fd
= -1;
571 if ((state
->wb_ctx
->fd
== -1)
572 || (state
->need_priv
&& !state
->wb_ctx
->is_priv
)) {
573 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
575 if (tevent_req_nomem(subreq
, req
)) {
578 tevent_req_set_callback(subreq
, wb_trans_connect_done
, req
);
582 state
->wb_req
->pid
= getpid();
584 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
585 state
->wb_ctx
->fd
, state
->wb_req
);
586 if (tevent_req_nomem(subreq
, req
)) {
589 tevent_req_set_callback(subreq
, wb_trans_done
, req
);
592 static bool wb_trans_retry(struct tevent_req
*req
,
593 struct wb_trans_state
*state
,
596 struct tevent_req
*subreq
;
598 if (WBC_ERROR_IS_OK(wbc_err
)) {
602 if (wbc_err
== WBC_ERR_WINBIND_NOT_AVAILABLE
) {
604 * Winbind not around or we can't connect to the pipe. Fail
607 tevent_req_error(req
, wbc_err
);
612 * The transfer as such failed, retry after one second
615 if (state
->wb_ctx
->fd
!= -1) {
616 close(state
->wb_ctx
->fd
);
617 state
->wb_ctx
->fd
= -1;
620 subreq
= tevent_wakeup_send(state
, state
->ev
,
621 tevent_timeval_current_ofs(1, 0));
622 if (tevent_req_nomem(subreq
, req
)) {
625 tevent_req_set_callback(subreq
, wb_trans_retry_wait_done
, req
);
629 static void wb_trans_retry_wait_done(struct tevent_req
*subreq
)
631 struct tevent_req
*req
= tevent_req_callback_data(
632 subreq
, struct tevent_req
);
633 struct wb_trans_state
*state
= tevent_req_data(
634 req
, struct wb_trans_state
);
637 ret
= tevent_wakeup_recv(subreq
);
640 tevent_req_error(req
, WBC_ERR_UNKNOWN_FAILURE
);
644 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
646 if (tevent_req_nomem(subreq
, req
)) {
649 tevent_req_set_callback(subreq
, wb_trans_connect_done
, req
);
652 static void wb_trans_connect_done(struct tevent_req
*subreq
)
654 struct tevent_req
*req
= tevent_req_callback_data(
655 subreq
, struct tevent_req
);
656 struct wb_trans_state
*state
= tevent_req_data(
657 req
, struct wb_trans_state
);
660 wbc_err
= wb_open_pipe_recv(subreq
);
663 if (wb_trans_retry(req
, state
, wbc_err
)) {
667 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
668 state
->wb_ctx
->fd
, state
->wb_req
);
669 if (tevent_req_nomem(subreq
, req
)) {
672 tevent_req_set_callback(subreq
, wb_trans_done
, req
);
675 static void wb_trans_done(struct tevent_req
*subreq
)
677 struct tevent_req
*req
= tevent_req_callback_data(
678 subreq
, struct tevent_req
);
679 struct wb_trans_state
*state
= tevent_req_data(
680 req
, struct wb_trans_state
);
683 ret
= wb_simple_trans_recv(subreq
, state
, &state
->wb_resp
, &err
);
686 && wb_trans_retry(req
, state
, map_wbc_err_from_errno(err
))) {
690 tevent_req_done(req
);
693 wbcErr
wb_trans_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
694 struct winbindd_response
**presponse
)
696 struct wb_trans_state
*state
= tevent_req_data(
697 req
, struct wb_trans_state
);
700 if (tevent_req_is_wbcerr(req
, &wbc_err
)) {
704 *presponse
= talloc_move(mem_ctx
, &state
->wb_resp
);
705 return WBC_ERR_SUCCESS
;
708 /********************************************************************
709 * Debug wrapper functions, modeled (with lot's of code copied as is)
710 * after the tevent debug wrapper functions
711 ********************************************************************/
714 this allows the user to choose their own debug function
716 int wbcSetDebug(struct wb_context
*wb_ctx
,
717 void (*debug
)(void *context
,
718 enum wbcDebugLevel level
,
720 va_list ap
) PRINTF_ATTRIBUTE(3,0),
723 wb_ctx
->debug_ops
.debug
= debug
;
724 wb_ctx
->debug_ops
.context
= context
;
729 debug function for wbcSetDebugStderr
731 static void wbcDebugStderr(void *private_data
,
732 enum wbcDebugLevel level
,
734 va_list ap
) PRINTF_ATTRIBUTE(3,0);
735 static void wbcDebugStderr(void *private_data
,
736 enum wbcDebugLevel level
,
737 const char *fmt
, va_list ap
)
739 if (level
<= WBC_DEBUG_WARNING
) {
740 vfprintf(stderr
, fmt
, ap
);
745 convenience function to setup debug messages on stderr
746 messages of level WBC_DEBUG_WARNING and higher are printed
748 int wbcSetDebugStderr(struct wb_context
*wb_ctx
)
750 return wbcSetDebug(wb_ctx
, wbcDebugStderr
, wb_ctx
);
756 * The default debug action is to ignore debugging messages.
757 * This is the most appropriate action for a library.
758 * Applications using the library must decide where to
759 * redirect debugging messages
761 void wbcDebug(struct wb_context
*wb_ctx
, enum wbcDebugLevel level
,
762 const char *fmt
, ...)
768 if (wb_ctx
->debug_ops
.debug
== NULL
) {
772 wb_ctx
->debug_ops
.debug(wb_ctx
->debug_ops
.context
, level
, fmt
, ap
);