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);