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 result
= async_req_new(mem_ctx
);
293 if (result
== NULL
) {
296 state
= talloc(result
, struct wb_int_trans_state
);
300 result
->private_data
= state
;
302 if (winbind_closed_fd(fd
)) {
303 if (!async_post_status(result
, ev
,
304 NT_STATUS_PIPE_DISCONNECTED
)) {
312 state
->wb_req
= wb_req
;
314 state
->wb_req
->length
= sizeof(struct winbindd_request
);
315 state
->wb_req
->pid
= getpid();
317 subreq
= wb_req_write_send(state
, state
->ev
, state
->fd
, state
->wb_req
);
318 if (subreq
== NULL
) {
321 subreq
->async
.fn
= wb_int_trans_write_done
;
322 subreq
->async
.priv
= result
;
331 static void wb_int_trans_write_done(struct async_req
*subreq
)
333 struct async_req
*req
= talloc_get_type_abort(
334 subreq
->async
.priv
, struct async_req
);
335 struct wb_int_trans_state
*state
= talloc_get_type_abort(
336 req
->private_data
, struct wb_int_trans_state
);
339 status
= wb_req_write_recv(subreq
);
341 if (!NT_STATUS_IS_OK(status
)) {
342 async_req_error(req
, status
);
346 subreq
= wb_resp_read_send(state
, state
->ev
, state
->fd
);
347 if (subreq
== NULL
) {
348 async_req_error(req
, NT_STATUS_NO_MEMORY
);
350 subreq
->async
.fn
= wb_int_trans_read_done
;
351 subreq
->async
.priv
= req
;
354 static void wb_int_trans_read_done(struct async_req
*subreq
)
356 struct async_req
*req
= talloc_get_type_abort(
357 subreq
->async
.priv
, struct async_req
);
358 struct wb_int_trans_state
*state
= talloc_get_type_abort(
359 req
->private_data
, struct wb_int_trans_state
);
362 status
= wb_resp_read_recv(subreq
, state
, &state
->wb_resp
);
364 if (!NT_STATUS_IS_OK(status
)) {
365 async_req_error(req
, status
);
372 static NTSTATUS
wb_int_trans_recv(struct async_req
*req
,
374 struct winbindd_response
**presponse
)
376 struct wb_int_trans_state
*state
= talloc_get_type_abort(
377 req
->private_data
, struct wb_int_trans_state
);
380 if (async_req_is_error(req
, &status
)) {
384 *presponse
= talloc_move(mem_ctx
, &state
->wb_resp
);
388 static const char *winbindd_socket_dir(void)
390 #ifdef SOCKET_WRAPPER
393 env_dir
= getenv(WINBINDD_SOCKET_DIR_ENVVAR
);
399 return WINBINDD_SOCKET_DIR
;
402 struct wb_open_pipe_state
{
403 struct wb_context
*wb_ctx
;
404 struct event_context
*ev
;
406 struct winbindd_request wb_req
;
409 static void wb_open_pipe_connect_nonpriv_done(struct async_req
*subreq
);
410 static void wb_open_pipe_ping_done(struct async_req
*subreq
);
411 static void wb_open_pipe_getpriv_done(struct async_req
*subreq
);
412 static void wb_open_pipe_connect_priv_done(struct async_req
*subreq
);
414 static struct async_req
*wb_open_pipe_send(TALLOC_CTX
*mem_ctx
,
415 struct event_context
*ev
,
416 struct wb_context
*wb_ctx
,
419 struct async_req
*result
;
420 struct async_req
*subreq
;
421 struct wb_open_pipe_state
*state
;
423 result
= async_req_new(mem_ctx
);
424 if (result
== NULL
) {
427 state
= talloc(result
, struct wb_open_pipe_state
);
431 result
->private_data
= state
;
433 state
->wb_ctx
= wb_ctx
;
435 state
->need_priv
= need_priv
;
437 if (wb_ctx
->fd
!= -1) {
442 subreq
= wb_connect_send(state
, ev
, wb_ctx
, winbindd_socket_dir());
443 if (subreq
== NULL
) {
447 subreq
->async
.fn
= wb_open_pipe_connect_nonpriv_done
;
448 subreq
->async
.priv
= result
;
456 static void wb_open_pipe_connect_nonpriv_done(struct async_req
*subreq
)
458 struct async_req
*req
= talloc_get_type_abort(
459 subreq
->async
.priv
, struct async_req
);
460 struct wb_open_pipe_state
*state
= talloc_get_type_abort(
461 req
->private_data
, struct wb_open_pipe_state
);
464 status
= wb_connect_recv(subreq
);
466 if (!NT_STATUS_IS_OK(status
)) {
467 state
->wb_ctx
->is_priv
= true;
468 async_req_error(req
, status
);
472 ZERO_STRUCT(state
->wb_req
);
473 state
->wb_req
.cmd
= WINBINDD_INTERFACE_VERSION
;
475 subreq
= wb_int_trans_send(state
, state
->ev
, state
->wb_ctx
->fd
,
477 if (async_req_nomem(subreq
, req
)) {
481 subreq
->async
.fn
= wb_open_pipe_ping_done
;
482 subreq
->async
.priv
= req
;
485 static void wb_open_pipe_ping_done(struct async_req
*subreq
)
487 struct async_req
*req
= talloc_get_type_abort(
488 subreq
->async
.priv
, struct async_req
);
489 struct wb_open_pipe_state
*state
= talloc_get_type_abort(
490 req
->private_data
, struct wb_open_pipe_state
);
491 struct winbindd_response
*wb_resp
;
494 status
= wb_int_trans_recv(subreq
, state
, &wb_resp
);
496 if (!NT_STATUS_IS_OK(status
)) {
497 async_req_error(req
, status
);
501 if (!state
->need_priv
) {
506 state
->wb_req
.cmd
= WINBINDD_PRIV_PIPE_DIR
;
508 subreq
= wb_int_trans_send(state
, state
->ev
, state
->wb_ctx
->fd
,
510 if (async_req_nomem(subreq
, req
)) {
514 subreq
->async
.fn
= wb_open_pipe_getpriv_done
;
515 subreq
->async
.priv
= req
;
518 static void wb_open_pipe_getpriv_done(struct async_req
*subreq
)
520 struct async_req
*req
= talloc_get_type_abort(
521 subreq
->async
.priv
, struct async_req
);
522 struct wb_open_pipe_state
*state
= talloc_get_type_abort(
523 req
->private_data
, struct wb_open_pipe_state
);
524 struct winbindd_response
*wb_resp
= NULL
;
527 status
= wb_int_trans_recv(subreq
, state
, &wb_resp
);
529 if (!NT_STATUS_IS_OK(status
)) {
530 async_req_error(req
, status
);
534 close(state
->wb_ctx
->fd
);
535 state
->wb_ctx
->fd
= -1;
537 subreq
= wb_connect_send(state
, state
->ev
, state
->wb_ctx
,
538 (char *)wb_resp
->extra_data
.data
);
539 TALLOC_FREE(wb_resp
);
540 if (async_req_nomem(subreq
, req
)) {
544 subreq
->async
.fn
= wb_open_pipe_connect_priv_done
;
545 subreq
->async
.priv
= req
;
548 static void wb_open_pipe_connect_priv_done(struct async_req
*subreq
)
550 struct async_req
*req
= talloc_get_type_abort(
551 subreq
->async
.priv
, struct async_req
);
552 struct wb_open_pipe_state
*state
= talloc_get_type_abort(
553 req
->private_data
, struct wb_open_pipe_state
);
556 status
= wb_connect_recv(subreq
);
558 if (!NT_STATUS_IS_OK(status
)) {
559 async_req_error(req
, status
);
562 state
->wb_ctx
->is_priv
= true;
566 static NTSTATUS
wb_open_pipe_recv(struct async_req
*req
)
568 return async_req_simple_recv(req
);
571 struct wb_trans_state
{
572 struct wb_trans_state
*prev
, *next
;
573 struct wb_context
*wb_ctx
;
574 struct event_context
*ev
;
575 struct winbindd_request
*wb_req
;
576 struct winbindd_response
*wb_resp
;
581 static void wb_trans_connect_done(struct async_req
*subreq
);
582 static void wb_trans_done(struct async_req
*subreq
);
583 static void wb_trans_retry_wait_done(struct async_req
*subreq
);
585 static void wb_trigger_trans(struct async_req
*req
)
587 struct wb_trans_state
*state
= talloc_get_type_abort(
588 req
->private_data
, struct wb_trans_state
);
589 struct async_req
*subreq
;
591 if ((state
->wb_ctx
->fd
== -1)
592 || (state
->need_priv
&& !state
->wb_ctx
->is_priv
)) {
594 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
596 if (async_req_nomem(subreq
, req
)) {
599 subreq
->async
.fn
= wb_trans_connect_done
;
600 subreq
->async
.priv
= req
;
604 subreq
= wb_int_trans_send(state
, state
->ev
, state
->wb_ctx
->fd
,
606 if (async_req_nomem(subreq
, req
)) {
609 subreq
->async
.fn
= wb_trans_done
;
610 subreq
->async
.priv
= req
;
613 struct async_req
*wb_trans_send(TALLOC_CTX
*mem_ctx
, struct event_context
*ev
,
614 struct wb_context
*wb_ctx
, bool need_priv
,
615 const struct winbindd_request
*wb_req
)
617 struct async_req
*result
;
618 struct wb_trans_state
*state
;
620 result
= async_req_new(mem_ctx
);
621 if (result
== NULL
) {
624 state
= talloc(result
, struct wb_trans_state
);
628 result
->private_data
= state
;
630 state
->wb_ctx
= wb_ctx
;
632 state
->wb_req
= winbindd_request_copy(state
, wb_req
);
633 if (state
->wb_req
== NULL
) {
636 state
->num_retries
= 10;
637 state
->need_priv
= need_priv
;
639 if (!async_req_enqueue(wb_ctx
->queue
, ev
, result
, wb_trigger_trans
)) {
649 static bool wb_trans_retry(struct async_req
*req
,
650 struct wb_trans_state
*state
,
653 struct async_req
*subreq
;
655 if (NT_STATUS_IS_OK(status
)) {
659 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)
660 || NT_STATUS_EQUAL(status
, NT_STATUS_ACCESS_DENIED
)) {
662 * Winbind not around or we can't connect to the pipe. Fail
665 async_req_error(req
, status
);
669 state
->num_retries
-= 1;
670 if (state
->num_retries
== 0) {
671 async_req_error(req
, status
);
676 * The transfer as such failed, retry after one second
679 if (state
->wb_ctx
->fd
!= -1) {
680 close(state
->wb_ctx
->fd
);
681 state
->wb_ctx
->fd
= -1;
684 subreq
= async_wait_send(state
, state
->ev
, timeval_set(1, 0));
685 if (async_req_nomem(subreq
, req
)) {
689 subreq
->async
.fn
= wb_trans_retry_wait_done
;
690 subreq
->async
.priv
= req
;
694 static void wb_trans_retry_wait_done(struct async_req
*subreq
)
696 struct async_req
*req
= talloc_get_type_abort(
697 subreq
->async
.priv
, struct async_req
);
698 struct wb_trans_state
*state
= talloc_get_type_abort(
699 req
->private_data
, struct wb_trans_state
);
702 status
= async_wait_recv(subreq
);
704 if (!NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
)) {
705 async_req_error(req
, status
);
709 subreq
= wb_open_pipe_send(state
, state
->ev
, state
->wb_ctx
,
711 if (async_req_nomem(subreq
, req
)) {
714 subreq
->async
.fn
= wb_trans_connect_done
;
715 subreq
->async
.priv
= req
;
718 static void wb_trans_connect_done(struct async_req
*subreq
)
720 struct async_req
*req
= talloc_get_type_abort(
721 subreq
->async
.priv
, struct async_req
);
722 struct wb_trans_state
*state
= talloc_get_type_abort(
723 req
->private_data
, struct wb_trans_state
);
726 status
= wb_open_pipe_recv(subreq
);
729 if (wb_trans_retry(req
, state
, status
)) {
733 subreq
= wb_int_trans_send(state
, state
->ev
, state
->wb_ctx
->fd
,
735 if (async_req_nomem(subreq
, req
)) {
739 subreq
->async
.fn
= wb_trans_done
;
740 subreq
->async
.priv
= req
;
743 static void wb_trans_done(struct async_req
*subreq
)
745 struct async_req
*req
= talloc_get_type_abort(
746 subreq
->async
.priv
, struct async_req
);
747 struct wb_trans_state
*state
= talloc_get_type_abort(
748 req
->private_data
, struct wb_trans_state
);
751 status
= wb_int_trans_recv(subreq
, state
, &state
->wb_resp
);
754 if (wb_trans_retry(req
, state
, status
)) {
761 NTSTATUS
wb_trans_recv(struct async_req
*req
, TALLOC_CTX
*mem_ctx
,
762 struct winbindd_response
**presponse
)
764 struct wb_trans_state
*state
= talloc_get_type_abort(
765 req
->private_data
, struct wb_trans_state
);
768 if (async_req_is_error(req
, &status
)) {
772 *presponse
= talloc_move(mem_ctx
, &state
->wb_resp
);