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"
31 #include "lib/async_req/async_sock.h"
32 #include "nsswitch/winbind_struct_protocol.h"
33 #include "nsswitch/libwbclient/wbclient.h"
34 #include "nsswitch/libwbclient/wbc_async.h"
36 wbcErr
map_wbc_err_from_errno(int error
)
41 return WBC_ERR_AUTH_ERROR
;
43 return WBC_ERR_NO_MEMORY
;
46 return WBC_ERR_UNKNOWN_FAILURE
;
50 bool tevent_req_is_wbcerr(struct tevent_req
*req
, wbcErr
*pwbc_err
)
52 enum tevent_req_state state
;
54 if (!tevent_req_is_error(req
, &state
, &error
)) {
55 *pwbc_err
= WBC_ERR_SUCCESS
;
60 case TEVENT_REQ_USER_ERROR
:
63 case TEVENT_REQ_TIMED_OUT
:
64 *pwbc_err
= WBC_ERR_UNKNOWN_FAILURE
;
66 case TEVENT_REQ_NO_MEMORY
:
67 *pwbc_err
= WBC_ERR_NO_MEMORY
;
70 *pwbc_err
= WBC_ERR_UNKNOWN_FAILURE
;
76 wbcErr
tevent_req_simple_recv_wbcerr(struct tevent_req
*req
)
80 if (tevent_req_is_wbcerr(req
, &wbc_err
)) {
84 return WBC_ERR_SUCCESS
;
88 struct tevent_queue
*queue
;
93 static int make_nonstd_fd(int fd
)
111 for (i
=0; i
<num_fds
; i
++) {
120 /****************************************************************************
121 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
125 Set close on exec also.
126 ****************************************************************************/
128 static int make_safe_fd(int fd
)
131 int new_fd
= make_nonstd_fd(fd
);
137 /* Socket should be nonblocking. */
140 #define FLAG_TO_SET O_NONBLOCK
143 #define FLAG_TO_SET O_NDELAY
145 #define FLAG_TO_SET FNDELAY
149 if ((flags
= fcntl(new_fd
, F_GETFL
)) == -1) {
153 flags
|= FLAG_TO_SET
;
154 if (fcntl(new_fd
, F_SETFL
, flags
) == -1) {
160 /* Socket should be closed on exec() */
162 result
= flags
= fcntl(new_fd
, F_GETFD
, 0);
165 result
= fcntl( new_fd
, F_SETFD
, flags
);
175 int sys_errno
= errno
;
182 struct wb_context
*wb_context_init(TALLOC_CTX
*mem_ctx
)
184 struct wb_context
*result
;
186 result
= talloc(mem_ctx
, struct wb_context
);
187 if (result
== NULL
) {
190 result
->queue
= tevent_queue_create(result
, "wb_trans");
191 if (result
->queue
== NULL
) {
196 result
->is_priv
= false;
200 struct wb_connect_state
{
204 static void wbc_connect_connected(struct tevent_req
*subreq
);
206 static struct tevent_req
*wb_connect_send(TALLOC_CTX
*mem_ctx
,
207 struct tevent_context
*ev
,
208 struct wb_context
*wb_ctx
,
211 struct tevent_req
*result
, *subreq
;
212 struct wb_connect_state
*state
;
213 struct sockaddr_un sunaddr
;
218 result
= tevent_req_create(mem_ctx
, &state
, struct wb_connect_state
);
219 if (result
== NULL
) {
223 if (wb_ctx
->fd
!= -1) {
228 /* Check permissions on unix socket directory */
230 if (lstat(dir
, &st
) == -1) {
231 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
235 if (!S_ISDIR(st
.st_mode
) ||
236 (st
.st_uid
!= 0 && st
.st_uid
!= geteuid())) {
237 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
241 /* Connect to socket */
243 path
= talloc_asprintf(talloc_tos(), "%s/%s", dir
,
244 WINBINDD_SOCKET_NAME
);
249 sunaddr
.sun_family
= AF_UNIX
;
250 strlcpy(sunaddr
.sun_path
, path
, sizeof(sunaddr
.sun_path
));
253 /* If socket file doesn't exist, don't bother trying to connect
254 with retry. This is an attempt to make the system usable when
255 the winbindd daemon is not running. */
257 if ((lstat(sunaddr
.sun_path
, &st
) == -1)
258 || !S_ISSOCK(st
.st_mode
)
259 || (st
.st_uid
!= 0 && st
.st_uid
!= geteuid())) {
260 wbc_err
= WBC_ERR_WINBIND_NOT_AVAILABLE
;
264 wb_ctx
->fd
= make_safe_fd(socket(AF_UNIX
, SOCK_STREAM
, 0));
265 if (wb_ctx
->fd
== -1) {
266 wbc_err
= map_wbc_err_from_errno(errno
);
270 subreq
= async_connect_send(mem_ctx
, ev
, wb_ctx
->fd
,
271 (struct sockaddr
*)(void *)&sunaddr
,
273 if (subreq
== NULL
) {
276 tevent_req_set_callback(subreq
, wbc_connect_connected
, result
);
280 tevent_req_error(result
, wbc_err
);
281 return tevent_req_post(result
, ev
);
287 static void wbc_connect_connected(struct tevent_req
*subreq
)
289 struct tevent_req
*req
= tevent_req_callback_data(
290 subreq
, struct tevent_req
);
293 res
= async_connect_recv(subreq
, &err
);
296 tevent_req_error(req
, map_wbc_err_from_errno(err
));
299 tevent_req_done(req
);
302 static wbcErr
wb_connect_recv(struct tevent_req
*req
)
304 return tevent_req_simple_recv_wbcerr(req
);
307 static const char *winbindd_socket_dir(void)
309 #ifdef SOCKET_WRAPPER
312 env_dir
= getenv(WINBINDD_SOCKET_DIR_ENVVAR
);
318 return WINBINDD_SOCKET_DIR
;
321 struct wb_open_pipe_state
{
322 struct wb_context
*wb_ctx
;
323 struct tevent_context
*ev
;
325 struct winbindd_request wb_req
;
328 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req
*subreq
);
329 static void wb_open_pipe_ping_done(struct tevent_req
*subreq
);
330 static void wb_open_pipe_getpriv_done(struct tevent_req
*subreq
);
331 static void wb_open_pipe_connect_priv_done(struct tevent_req
*subreq
);
333 static struct tevent_req
*wb_open_pipe_send(TALLOC_CTX
*mem_ctx
,
334 struct tevent_context
*ev
,
335 struct wb_context
*wb_ctx
,
338 struct tevent_req
*result
, *subreq
;
339 struct wb_open_pipe_state
*state
;
341 result
= tevent_req_create(mem_ctx
, &state
, struct wb_open_pipe_state
);
342 if (result
== NULL
) {
345 state
->wb_ctx
= wb_ctx
;
347 state
->need_priv
= need_priv
;
349 if (wb_ctx
->fd
!= -1) {
354 subreq
= wb_connect_send(state
, ev
, wb_ctx
, winbindd_socket_dir());
355 if (subreq
== NULL
) {
358 tevent_req_set_callback(subreq
, wb_open_pipe_connect_nonpriv_done
,
367 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req
*subreq
)
369 struct tevent_req
*req
= tevent_req_callback_data(
370 subreq
, struct tevent_req
);
371 struct wb_open_pipe_state
*state
= tevent_req_data(
372 req
, struct wb_open_pipe_state
);
375 wbc_err
= wb_connect_recv(subreq
);
377 if (!WBC_ERROR_IS_OK(wbc_err
)) {
378 state
->wb_ctx
->is_priv
= true;
379 tevent_req_error(req
, wbc_err
);
383 ZERO_STRUCT(state
->wb_req
);
384 state
->wb_req
.cmd
= WINBINDD_INTERFACE_VERSION
;
385 state
->wb_req
.pid
= getpid();
387 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
388 state
->wb_ctx
->fd
, &state
->wb_req
);
389 if (tevent_req_nomem(subreq
, req
)) {
392 tevent_req_set_callback(subreq
, wb_open_pipe_ping_done
, req
);
395 static void wb_open_pipe_ping_done(struct tevent_req
*subreq
)
397 struct tevent_req
*req
= tevent_req_callback_data(
398 subreq
, struct tevent_req
);
399 struct wb_open_pipe_state
*state
= tevent_req_data(
400 req
, struct wb_open_pipe_state
);
401 struct winbindd_response
*wb_resp
;
404 ret
= wb_simple_trans_recv(subreq
, state
, &wb_resp
, &err
);
407 tevent_req_error(req
, map_wbc_err_from_errno(err
));
411 if (!state
->need_priv
) {
412 tevent_req_done(req
);
416 state
->wb_req
.cmd
= WINBINDD_PRIV_PIPE_DIR
;
417 state
->wb_req
.pid
= getpid();
419 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
420 state
->wb_ctx
->fd
, &state
->wb_req
);
421 if (tevent_req_nomem(subreq
, req
)) {
424 tevent_req_set_callback(subreq
, wb_open_pipe_getpriv_done
, req
);
427 static void wb_open_pipe_getpriv_done(struct tevent_req
*subreq
)
429 struct tevent_req
*req
= tevent_req_callback_data(
430 subreq
, struct tevent_req
);
431 struct wb_open_pipe_state
*state
= tevent_req_data(
432 req
, struct wb_open_pipe_state
);
433 struct winbindd_response
*wb_resp
= NULL
;
436 ret
= wb_simple_trans_recv(subreq
, state
, &wb_resp
, &err
);
439 tevent_req_error(req
, map_wbc_err_from_errno(err
));
443 close(state
->wb_ctx
->fd
);
444 state
->wb_ctx
->fd
= -1;
446 subreq
= wb_connect_send(state
, state
->ev
, state
->wb_ctx
,
447 (char *)wb_resp
->extra_data
.data
);
448 TALLOC_FREE(wb_resp
);
449 if (tevent_req_nomem(subreq
, req
)) {
452 tevent_req_set_callback(subreq
, wb_open_pipe_connect_priv_done
, req
);
455 static void wb_open_pipe_connect_priv_done(struct tevent_req
*subreq
)
457 struct tevent_req
*req
= tevent_req_callback_data(
458 subreq
, struct tevent_req
);
459 struct wb_open_pipe_state
*state
= tevent_req_data(
460 req
, struct wb_open_pipe_state
);
463 wbc_err
= wb_connect_recv(subreq
);
465 if (!WBC_ERROR_IS_OK(wbc_err
)) {
466 tevent_req_error(req
, wbc_err
);
469 state
->wb_ctx
->is_priv
= true;
470 tevent_req_done(req
);
473 static wbcErr
wb_open_pipe_recv(struct tevent_req
*req
)
475 return tevent_req_simple_recv_wbcerr(req
);
478 struct wb_trans_state
{
479 struct wb_trans_state
*prev
, *next
;
480 struct wb_context
*wb_ctx
;
481 struct tevent_context
*ev
;
482 struct winbindd_request
*wb_req
;
483 struct winbindd_response
*wb_resp
;
487 static bool closed_fd(int fd
)
501 selret
= select(fd
+1, &r_fds
, NULL
, NULL
, &tv
);
508 return (FD_ISSET(fd
, &r_fds
));
511 static void wb_trans_trigger(struct tevent_req
*req
, void *private_data
);
512 static void wb_trans_connect_done(struct tevent_req
*subreq
);
513 static void wb_trans_done(struct tevent_req
*subreq
);
514 static void wb_trans_retry_wait_done(struct tevent_req
*subreq
);
516 struct tevent_req
*wb_trans_send(TALLOC_CTX
*mem_ctx
,
517 struct tevent_context
*ev
,
518 struct wb_context
*wb_ctx
, bool need_priv
,
519 struct winbindd_request
*wb_req
)
521 struct tevent_req
*req
;
522 struct wb_trans_state
*state
;
524 req
= tevent_req_create(mem_ctx
, &state
, struct wb_trans_state
);
528 state
->wb_ctx
= wb_ctx
;
530 state
->wb_req
= wb_req
;
531 state
->need_priv
= need_priv
;
533 if (!tevent_queue_add(wb_ctx
->queue
, ev
, req
, wb_trans_trigger
,
535 tevent_req_nomem(NULL
, req
);
536 return tevent_req_post(req
, ev
);
541 static void wb_trans_trigger(struct tevent_req
*req
, void *private_data
)
543 struct wb_trans_state
*state
= tevent_req_data(
544 req
, struct wb_trans_state
);
545 struct tevent_req
*subreq
;
547 if ((state
->wb_ctx
->fd
!= -1) && closed_fd(state
->wb_ctx
->fd
)) {
548 close(state
->wb_ctx
->fd
);
549 state
->wb_ctx
->fd
= -1;
552 if ((state
->wb_ctx
->fd
== -1)
553 || (state
->need_priv
&& !state
->wb_ctx
->is_priv
)) {
554 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
556 if (tevent_req_nomem(subreq
, req
)) {
559 tevent_req_set_callback(subreq
, wb_trans_connect_done
, req
);
563 state
->wb_req
->pid
= getpid();
565 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
566 state
->wb_ctx
->fd
, state
->wb_req
);
567 if (tevent_req_nomem(subreq
, req
)) {
570 tevent_req_set_callback(subreq
, wb_trans_done
, req
);
573 static bool wb_trans_retry(struct tevent_req
*req
,
574 struct wb_trans_state
*state
,
577 struct tevent_req
*subreq
;
579 if (WBC_ERROR_IS_OK(wbc_err
)) {
583 if (wbc_err
== WBC_ERR_WINBIND_NOT_AVAILABLE
) {
585 * Winbind not around or we can't connect to the pipe. Fail
588 tevent_req_error(req
, wbc_err
);
593 * The transfer as such failed, retry after one second
596 if (state
->wb_ctx
->fd
!= -1) {
597 close(state
->wb_ctx
->fd
);
598 state
->wb_ctx
->fd
= -1;
601 subreq
= tevent_wakeup_send(state
, state
->ev
,
602 timeval_current_ofs(1, 0));
603 if (tevent_req_nomem(subreq
, req
)) {
606 tevent_req_set_callback(subreq
, wb_trans_retry_wait_done
, req
);
610 static void wb_trans_retry_wait_done(struct tevent_req
*subreq
)
612 struct tevent_req
*req
= tevent_req_callback_data(
613 subreq
, struct tevent_req
);
614 struct wb_trans_state
*state
= tevent_req_data(
615 req
, struct wb_trans_state
);
618 ret
= tevent_wakeup_recv(subreq
);
621 tevent_req_error(req
, WBC_ERR_UNKNOWN_FAILURE
);
625 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
627 if (tevent_req_nomem(subreq
, req
)) {
630 tevent_req_set_callback(subreq
, wb_trans_connect_done
, req
);
633 static void wb_trans_connect_done(struct tevent_req
*subreq
)
635 struct tevent_req
*req
= tevent_req_callback_data(
636 subreq
, struct tevent_req
);
637 struct wb_trans_state
*state
= tevent_req_data(
638 req
, struct wb_trans_state
);
641 wbc_err
= wb_open_pipe_recv(subreq
);
644 if (wb_trans_retry(req
, state
, wbc_err
)) {
648 subreq
= wb_simple_trans_send(state
, state
->ev
, NULL
,
649 state
->wb_ctx
->fd
, state
->wb_req
);
650 if (tevent_req_nomem(subreq
, req
)) {
653 tevent_req_set_callback(subreq
, wb_trans_done
, req
);
656 static void wb_trans_done(struct tevent_req
*subreq
)
658 struct tevent_req
*req
= tevent_req_callback_data(
659 subreq
, struct tevent_req
);
660 struct wb_trans_state
*state
= tevent_req_data(
661 req
, struct wb_trans_state
);
664 ret
= wb_simple_trans_recv(subreq
, state
, &state
->wb_resp
, &err
);
667 && wb_trans_retry(req
, state
, map_wbc_err_from_errno(err
))) {
671 tevent_req_done(req
);
674 wbcErr
wb_trans_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
675 struct winbindd_response
**presponse
)
677 struct wb_trans_state
*state
= tevent_req_data(
678 req
, struct wb_trans_state
);
681 if (tevent_req_is_wbcerr(req
, &wbc_err
)) {
685 *presponse
= talloc_move(mem_ctx
, &state
->wb_resp
);
686 return WBC_ERR_SUCCESS
;