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 "winbindd/winbindd.h"
22 #include "winbindd/winbindd_proto.h"
24 static int make_nonstd_fd(int fd
)
42 for (i
=0; i
<num_fds
; i
++) {
51 /****************************************************************************
52 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
56 Set close on exec also.
57 ****************************************************************************/
59 static int make_safe_fd(int fd
)
62 int new_fd
= make_nonstd_fd(fd
);
68 /* Socket should be nonblocking. */
71 #define FLAG_TO_SET O_NONBLOCK
74 #define FLAG_TO_SET O_NDELAY
76 #define FLAG_TO_SET FNDELAY
80 if ((flags
= fcntl(new_fd
, F_GETFL
)) == -1) {
85 if (fcntl(new_fd
, F_SETFL
, flags
) == -1) {
91 /* Socket should be closed on exec() */
93 result
= flags
= fcntl(new_fd
, F_GETFD
, 0);
96 result
= fcntl( new_fd
, F_SETFD
, flags
);
106 int sys_errno
= errno
;
113 static bool winbind_closed_fd(int fd
)
126 if ((select(fd
+1, &r_fds
, NULL
, NULL
, &tv
) == -1)
127 || FD_ISSET(fd
, &r_fds
)) {
135 struct async_req_queue
*queue
;
140 struct wb_context
*wb_context_init(TALLOC_CTX
*mem_ctx
)
142 struct wb_context
*result
;
144 result
= talloc(mem_ctx
, struct wb_context
);
145 if (result
== NULL
) {
148 result
->queue
= async_req_queue_init(result
);
149 if (result
->queue
== NULL
) {
157 static struct async_req
*wb_connect_send(TALLOC_CTX
*mem_ctx
,
158 struct event_context
*ev
,
159 struct wb_context
*wb_ctx
,
162 struct async_req
*req
;
163 struct sockaddr_un sunaddr
;
168 if (wb_ctx
->fd
!= -1) {
173 /* Check permissions on unix socket directory */
175 if (lstat(dir
, &st
) == -1) {
176 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
180 if (!S_ISDIR(st
.st_mode
) ||
181 (st
.st_uid
!= 0 && st
.st_uid
!= geteuid())) {
182 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
186 /* Connect to socket */
188 path
= talloc_asprintf(talloc_tos(), "%s/%s", dir
,
189 WINBINDD_SOCKET_NAME
);
194 sunaddr
.sun_family
= AF_UNIX
;
195 strlcpy(sunaddr
.sun_path
, path
, sizeof(sunaddr
.sun_path
));
198 /* If socket file doesn't exist, don't bother trying to connect
199 with retry. This is an attempt to make the system usable when
200 the winbindd daemon is not running. */
202 if ((lstat(sunaddr
.sun_path
, &st
) == -1)
203 || !S_ISSOCK(st
.st_mode
)
204 || (st
.st_uid
!= 0 && st
.st_uid
!= geteuid())) {
205 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
209 wb_ctx
->fd
= make_safe_fd(socket(AF_UNIX
, SOCK_STREAM
, 0));
210 if (wb_ctx
->fd
== -1) {
211 status
= map_nt_error_from_unix(errno
);
215 req
= async_connect_send(mem_ctx
, ev
, wb_ctx
->fd
,
216 (struct sockaddr
*)&sunaddr
,
221 if (!async_req_set_timeout(req
, ev
, timeval_set(30, 0))) {
229 status
= NT_STATUS_NO_MEMORY
;
231 req
= async_req_new(mem_ctx
);
235 if (async_post_status(req
, ev
, status
)) {
242 static NTSTATUS
wb_connect_recv(struct async_req
*req
)
246 return async_connect_recv(req
, &dummy
);
249 static struct winbindd_request
*winbindd_request_copy(
251 const struct winbindd_request
*req
)
253 struct winbindd_request
*result
;
255 result
= (struct winbindd_request
*)TALLOC_MEMDUP(
256 mem_ctx
, req
, sizeof(struct winbindd_request
));
257 if (result
== NULL
) {
261 if (result
->extra_len
== 0) {
265 result
->extra_data
.data
= (char *)TALLOC_MEMDUP(
266 result
, result
->extra_data
.data
, result
->extra_len
);
267 if (result
->extra_data
.data
== NULL
) {
274 struct wb_int_trans_state
{
275 struct event_context
*ev
;
277 struct winbindd_request
*wb_req
;
278 struct winbindd_response
*wb_resp
;
281 static void wb_int_trans_write_done(struct async_req
*subreq
);
282 static void wb_int_trans_read_done(struct async_req
*subreq
);
284 static struct async_req
*wb_int_trans_send(TALLOC_CTX
*mem_ctx
,
285 struct event_context
*ev
, int fd
,
286 struct winbindd_request
*wb_req
)
288 struct async_req
*result
;
289 struct async_req
*subreq
;
290 struct wb_int_trans_state
*state
;
292 if (!async_req_setup(mem_ctx
, &result
, &state
,
293 struct wb_int_trans_state
)) {
297 if (winbind_closed_fd(fd
)) {
298 if (!async_post_status(result
, ev
,
299 NT_STATUS_PIPE_DISCONNECTED
)) {
307 state
->wb_req
= wb_req
;
309 state
->wb_req
->length
= sizeof(struct winbindd_request
);
310 state
->wb_req
->pid
= getpid();
312 subreq
= wb_req_write_send(state
, state
->ev
, state
->fd
, state
->wb_req
);
313 if (subreq
== NULL
) {
316 subreq
->async
.fn
= wb_int_trans_write_done
;
317 subreq
->async
.priv
= result
;
326 static void wb_int_trans_write_done(struct async_req
*subreq
)
328 struct async_req
*req
= talloc_get_type_abort(
329 subreq
->async
.priv
, struct async_req
);
330 struct wb_int_trans_state
*state
= talloc_get_type_abort(
331 req
->private_data
, struct wb_int_trans_state
);
334 status
= wb_req_write_recv(subreq
);
336 if (!NT_STATUS_IS_OK(status
)) {
337 async_req_error(req
, status
);
341 subreq
= wb_resp_read_send(state
, state
->ev
, state
->fd
);
342 if (subreq
== NULL
) {
343 async_req_error(req
, NT_STATUS_NO_MEMORY
);
345 subreq
->async
.fn
= wb_int_trans_read_done
;
346 subreq
->async
.priv
= req
;
349 static void wb_int_trans_read_done(struct async_req
*subreq
)
351 struct async_req
*req
= talloc_get_type_abort(
352 subreq
->async
.priv
, struct async_req
);
353 struct wb_int_trans_state
*state
= talloc_get_type_abort(
354 req
->private_data
, struct wb_int_trans_state
);
357 status
= wb_resp_read_recv(subreq
, state
, &state
->wb_resp
);
359 if (!NT_STATUS_IS_OK(status
)) {
360 async_req_error(req
, status
);
367 static NTSTATUS
wb_int_trans_recv(struct async_req
*req
,
369 struct winbindd_response
**presponse
)
371 struct wb_int_trans_state
*state
= talloc_get_type_abort(
372 req
->private_data
, struct wb_int_trans_state
);
375 if (async_req_is_error(req
, &status
)) {
379 *presponse
= talloc_move(mem_ctx
, &state
->wb_resp
);
383 static const char *winbindd_socket_dir(void)
385 #ifdef SOCKET_WRAPPER
388 env_dir
= getenv(WINBINDD_SOCKET_DIR_ENVVAR
);
394 return WINBINDD_SOCKET_DIR
;
397 struct wb_open_pipe_state
{
398 struct wb_context
*wb_ctx
;
399 struct event_context
*ev
;
401 struct winbindd_request wb_req
;
404 static void wb_open_pipe_connect_nonpriv_done(struct async_req
*subreq
);
405 static void wb_open_pipe_ping_done(struct async_req
*subreq
);
406 static void wb_open_pipe_getpriv_done(struct async_req
*subreq
);
407 static void wb_open_pipe_connect_priv_done(struct async_req
*subreq
);
409 static struct async_req
*wb_open_pipe_send(TALLOC_CTX
*mem_ctx
,
410 struct event_context
*ev
,
411 struct wb_context
*wb_ctx
,
414 struct async_req
*result
;
415 struct async_req
*subreq
;
416 struct wb_open_pipe_state
*state
;
418 if (!async_req_setup(mem_ctx
, &result
, &state
,
419 struct wb_open_pipe_state
)) {
422 state
->wb_ctx
= wb_ctx
;
424 state
->need_priv
= need_priv
;
426 if (wb_ctx
->fd
!= -1) {
431 subreq
= wb_connect_send(state
, ev
, wb_ctx
, winbindd_socket_dir());
432 if (subreq
== NULL
) {
436 subreq
->async
.fn
= wb_open_pipe_connect_nonpriv_done
;
437 subreq
->async
.priv
= result
;
445 static void wb_open_pipe_connect_nonpriv_done(struct async_req
*subreq
)
447 struct async_req
*req
= talloc_get_type_abort(
448 subreq
->async
.priv
, struct async_req
);
449 struct wb_open_pipe_state
*state
= talloc_get_type_abort(
450 req
->private_data
, struct wb_open_pipe_state
);
453 status
= wb_connect_recv(subreq
);
455 if (!NT_STATUS_IS_OK(status
)) {
456 state
->wb_ctx
->is_priv
= true;
457 async_req_error(req
, status
);
461 ZERO_STRUCT(state
->wb_req
);
462 state
->wb_req
.cmd
= WINBINDD_INTERFACE_VERSION
;
464 subreq
= wb_int_trans_send(state
, state
->ev
, state
->wb_ctx
->fd
,
466 if (async_req_nomem(subreq
, req
)) {
470 subreq
->async
.fn
= wb_open_pipe_ping_done
;
471 subreq
->async
.priv
= req
;
474 static void wb_open_pipe_ping_done(struct async_req
*subreq
)
476 struct async_req
*req
= talloc_get_type_abort(
477 subreq
->async
.priv
, struct async_req
);
478 struct wb_open_pipe_state
*state
= talloc_get_type_abort(
479 req
->private_data
, struct wb_open_pipe_state
);
480 struct winbindd_response
*wb_resp
;
483 status
= wb_int_trans_recv(subreq
, state
, &wb_resp
);
485 if (!NT_STATUS_IS_OK(status
)) {
486 async_req_error(req
, status
);
490 if (!state
->need_priv
) {
495 state
->wb_req
.cmd
= WINBINDD_PRIV_PIPE_DIR
;
497 subreq
= wb_int_trans_send(state
, state
->ev
, state
->wb_ctx
->fd
,
499 if (async_req_nomem(subreq
, req
)) {
503 subreq
->async
.fn
= wb_open_pipe_getpriv_done
;
504 subreq
->async
.priv
= req
;
507 static void wb_open_pipe_getpriv_done(struct async_req
*subreq
)
509 struct async_req
*req
= talloc_get_type_abort(
510 subreq
->async
.priv
, struct async_req
);
511 struct wb_open_pipe_state
*state
= talloc_get_type_abort(
512 req
->private_data
, struct wb_open_pipe_state
);
513 struct winbindd_response
*wb_resp
= NULL
;
516 status
= wb_int_trans_recv(subreq
, state
, &wb_resp
);
518 if (!NT_STATUS_IS_OK(status
)) {
519 async_req_error(req
, status
);
523 close(state
->wb_ctx
->fd
);
524 state
->wb_ctx
->fd
= -1;
526 subreq
= wb_connect_send(state
, state
->ev
, state
->wb_ctx
,
527 (char *)wb_resp
->extra_data
.data
);
528 TALLOC_FREE(wb_resp
);
529 if (async_req_nomem(subreq
, req
)) {
533 subreq
->async
.fn
= wb_open_pipe_connect_priv_done
;
534 subreq
->async
.priv
= req
;
537 static void wb_open_pipe_connect_priv_done(struct async_req
*subreq
)
539 struct async_req
*req
= talloc_get_type_abort(
540 subreq
->async
.priv
, struct async_req
);
541 struct wb_open_pipe_state
*state
= talloc_get_type_abort(
542 req
->private_data
, struct wb_open_pipe_state
);
545 status
= wb_connect_recv(subreq
);
547 if (!NT_STATUS_IS_OK(status
)) {
548 async_req_error(req
, status
);
551 state
->wb_ctx
->is_priv
= true;
555 static NTSTATUS
wb_open_pipe_recv(struct async_req
*req
)
557 return async_req_simple_recv(req
);
560 struct wb_trans_state
{
561 struct wb_trans_state
*prev
, *next
;
562 struct wb_context
*wb_ctx
;
563 struct event_context
*ev
;
564 struct winbindd_request
*wb_req
;
565 struct winbindd_response
*wb_resp
;
570 static void wb_trans_connect_done(struct async_req
*subreq
);
571 static void wb_trans_done(struct async_req
*subreq
);
572 static void wb_trans_retry_wait_done(struct async_req
*subreq
);
574 static void wb_trigger_trans(struct async_req
*req
)
576 struct wb_trans_state
*state
= talloc_get_type_abort(
577 req
->private_data
, struct wb_trans_state
);
578 struct async_req
*subreq
;
580 if ((state
->wb_ctx
->fd
== -1)
581 || (state
->need_priv
&& !state
->wb_ctx
->is_priv
)) {
583 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
585 if (async_req_nomem(subreq
, req
)) {
588 subreq
->async
.fn
= wb_trans_connect_done
;
589 subreq
->async
.priv
= req
;
593 subreq
= wb_int_trans_send(state
, state
->ev
, state
->wb_ctx
->fd
,
595 if (async_req_nomem(subreq
, req
)) {
598 subreq
->async
.fn
= wb_trans_done
;
599 subreq
->async
.priv
= req
;
602 struct async_req
*wb_trans_send(TALLOC_CTX
*mem_ctx
, struct event_context
*ev
,
603 struct wb_context
*wb_ctx
, bool need_priv
,
604 const struct winbindd_request
*wb_req
)
606 struct async_req
*result
;
607 struct wb_trans_state
*state
;
609 if (!async_req_setup(mem_ctx
, &result
, &state
,
610 struct wb_trans_state
)) {
613 state
->wb_ctx
= wb_ctx
;
615 state
->wb_req
= winbindd_request_copy(state
, wb_req
);
616 if (state
->wb_req
== NULL
) {
619 state
->num_retries
= 10;
620 state
->need_priv
= need_priv
;
622 if (!async_req_enqueue(wb_ctx
->queue
, ev
, result
, wb_trigger_trans
)) {
632 static bool wb_trans_retry(struct async_req
*req
,
633 struct wb_trans_state
*state
,
636 struct async_req
*subreq
;
638 if (NT_STATUS_IS_OK(status
)) {
642 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)
643 || NT_STATUS_EQUAL(status
, NT_STATUS_ACCESS_DENIED
)) {
645 * Winbind not around or we can't connect to the pipe. Fail
648 async_req_error(req
, status
);
652 state
->num_retries
-= 1;
653 if (state
->num_retries
== 0) {
654 async_req_error(req
, status
);
659 * The transfer as such failed, retry after one second
662 if (state
->wb_ctx
->fd
!= -1) {
663 close(state
->wb_ctx
->fd
);
664 state
->wb_ctx
->fd
= -1;
667 subreq
= async_wait_send(state
, state
->ev
, timeval_set(1, 0));
668 if (async_req_nomem(subreq
, req
)) {
672 subreq
->async
.fn
= wb_trans_retry_wait_done
;
673 subreq
->async
.priv
= req
;
677 static void wb_trans_retry_wait_done(struct async_req
*subreq
)
679 struct async_req
*req
= talloc_get_type_abort(
680 subreq
->async
.priv
, struct async_req
);
681 struct wb_trans_state
*state
= talloc_get_type_abort(
682 req
->private_data
, struct wb_trans_state
);
685 status
= async_wait_recv(subreq
);
687 if (!NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
)) {
688 async_req_error(req
, status
);
692 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
694 if (async_req_nomem(subreq
, req
)) {
697 subreq
->async
.fn
= wb_trans_connect_done
;
698 subreq
->async
.priv
= req
;
701 static void wb_trans_connect_done(struct async_req
*subreq
)
703 struct async_req
*req
= talloc_get_type_abort(
704 subreq
->async
.priv
, struct async_req
);
705 struct wb_trans_state
*state
= talloc_get_type_abort(
706 req
->private_data
, struct wb_trans_state
);
709 status
= wb_open_pipe_recv(subreq
);
712 if (wb_trans_retry(req
, state
, status
)) {
716 subreq
= wb_int_trans_send(state
, state
->ev
, state
->wb_ctx
->fd
,
718 if (async_req_nomem(subreq
, req
)) {
722 subreq
->async
.fn
= wb_trans_done
;
723 subreq
->async
.priv
= req
;
726 static void wb_trans_done(struct async_req
*subreq
)
728 struct async_req
*req
= talloc_get_type_abort(
729 subreq
->async
.priv
, struct async_req
);
730 struct wb_trans_state
*state
= talloc_get_type_abort(
731 req
->private_data
, struct wb_trans_state
);
734 status
= wb_int_trans_recv(subreq
, state
, &state
->wb_resp
);
737 if (wb_trans_retry(req
, state
, status
)) {
744 NTSTATUS
wb_trans_recv(struct async_req
*req
, TALLOC_CTX
*mem_ctx
,
745 struct winbindd_response
**presponse
)
747 struct wb_trans_state
*state
= talloc_get_type_abort(
748 req
->private_data
, struct wb_trans_state
);
751 if (async_req_is_error(req
, &status
)) {
755 *presponse
= talloc_move(mem_ctx
, &state
->wb_resp
);