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 "wbc_async.h"
23 static int make_nonstd_fd(int fd)
41 for (i=0; i<num_fds; i++) {
50 /****************************************************************************
51 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
55 Set close on exec also.
56 ****************************************************************************/
58 static int make_safe_fd(int fd)
61 int new_fd = make_nonstd_fd(fd);
67 /* Socket should be nonblocking. */
70 #define FLAG_TO_SET O_NONBLOCK
73 #define FLAG_TO_SET O_NDELAY
75 #define FLAG_TO_SET FNDELAY
79 if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
84 if (fcntl(new_fd, F_SETFL, flags) == -1) {
90 /* Socket should be closed on exec() */
92 result = flags = fcntl(new_fd, F_GETFD, 0);
95 result = fcntl( new_fd, F_SETFD, flags );
105 int sys_errno = errno;
112 static bool winbind_closed_fd(int fd)
125 if ((select(fd+1, &r_fds, NULL, NULL, &tv) == -1)
126 || FD_ISSET(fd, &r_fds)) {
133 struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx)
135 struct wb_context *result;
137 result = talloc(mem_ctx, struct wb_context);
138 if (result == NULL) {
141 result->queue = async_req_queue_init(result);
142 if (result->queue == NULL) {
150 struct wb_connect_state {
154 static void wbc_connect_connected(struct tevent_req *subreq);
156 static struct async_req *wb_connect_send(TALLOC_CTX *mem_ctx,
157 struct tevent_context *ev,
158 struct wb_context *wb_ctx,
161 struct async_req *result;
162 struct tevent_req *subreq;
163 struct wb_connect_state *state;
164 struct sockaddr_un sunaddr;
169 if (!async_req_setup(mem_ctx, &result, &state,
170 struct wb_connect_state)) {
174 if (wb_ctx->fd != -1) {
179 /* Check permissions on unix socket directory */
181 if (lstat(dir, &st) == -1) {
182 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
186 if (!S_ISDIR(st.st_mode) ||
187 (st.st_uid != 0 && st.st_uid != geteuid())) {
188 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
192 /* Connect to socket */
194 path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
195 WINBINDD_SOCKET_NAME);
200 sunaddr.sun_family = AF_UNIX;
201 strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
204 /* If socket file doesn't exist, don't bother trying to connect
205 with retry. This is an attempt to make the system usable when
206 the winbindd daemon is not running. */
208 if ((lstat(sunaddr.sun_path, &st) == -1)
209 || !S_ISSOCK(st.st_mode)
210 || (st.st_uid != 0 && st.st_uid != geteuid())) {
211 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
215 wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
216 if (wb_ctx->fd == -1) {
217 wbc_err = map_wbc_err_from_errno(errno);
221 subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
222 (struct sockaddr *)&sunaddr,
224 if (subreq == NULL) {
227 subreq->async.fn = wbc_connect_connected;
228 subreq->async.private_data = result;
230 if (!tevent_req_set_endtime(subreq, ev, timeval_current_ofs(30, 0))) {
237 wbc_err = WBC_ERR_NO_MEMORY;
239 if (async_post_error(result, ev, wbc_err)) {
246 static void wbc_connect_connected(struct tevent_req *subreq)
248 struct async_req *req = talloc_get_type_abort(
249 subreq->async.private_data, struct async_req);
252 res = async_connect_recv(subreq, &err);
255 async_req_error(req, map_wbc_err_from_errno(err));
261 static wbcErr wb_connect_recv(struct async_req *req)
263 return async_req_simple_recv_wbcerr(req);
266 static struct winbindd_request *winbindd_request_copy(
268 const struct winbindd_request *req)
270 struct winbindd_request *result;
272 result = (struct winbindd_request *)TALLOC_MEMDUP(
273 mem_ctx, req, sizeof(struct winbindd_request));
274 if (result == NULL) {
278 if (result->extra_len == 0) {
282 result->extra_data.data = (char *)TALLOC_MEMDUP(
283 result, result->extra_data.data, result->extra_len);
284 if (result->extra_data.data == NULL) {
291 struct wb_int_trans_state {
292 struct tevent_context *ev;
294 struct winbindd_request *wb_req;
295 struct winbindd_response *wb_resp;
298 static void wb_int_trans_write_done(struct async_req *subreq);
299 static void wb_int_trans_read_done(struct async_req *subreq);
301 static struct async_req *wb_int_trans_send(TALLOC_CTX *mem_ctx,
302 struct tevent_context *ev, int fd,
303 struct winbindd_request *wb_req)
305 struct async_req *result;
306 struct async_req *subreq;
307 struct wb_int_trans_state *state;
309 if (!async_req_setup(mem_ctx, &result, &state,
310 struct wb_int_trans_state)) {
314 if (winbind_closed_fd(fd)) {
315 if (!async_post_error(result, ev,
316 WBC_ERR_WINBIND_NOT_AVAILABLE)) {
324 state->wb_req = wb_req;
326 state->wb_req->length = sizeof(struct winbindd_request);
327 state->wb_req->pid = getpid();
329 subreq = wb_req_write_send(state, state->ev, state->fd, state->wb_req);
330 if (subreq == NULL) {
333 subreq->async.fn = wb_int_trans_write_done;
334 subreq->async.priv = result;
343 static void wb_int_trans_write_done(struct async_req *subreq)
345 struct async_req *req = talloc_get_type_abort(
346 subreq->async.priv, struct async_req);
347 struct wb_int_trans_state *state = talloc_get_type_abort(
348 req->private_data, struct wb_int_trans_state);
351 wbc_err = wb_req_write_recv(subreq);
353 if (!WBC_ERROR_IS_OK(wbc_err)) {
354 async_req_error(req, wbc_err);
358 subreq = wb_resp_read_send(state, state->ev, state->fd);
359 if (subreq == NULL) {
360 async_req_error(req, WBC_ERR_NO_MEMORY);
362 subreq->async.fn = wb_int_trans_read_done;
363 subreq->async.priv = req;
366 static void wb_int_trans_read_done(struct async_req *subreq)
368 struct async_req *req = talloc_get_type_abort(
369 subreq->async.priv, struct async_req);
370 struct wb_int_trans_state *state = talloc_get_type_abort(
371 req->private_data, struct wb_int_trans_state);
374 wbc_err = wb_resp_read_recv(subreq, state, &state->wb_resp);
376 if (!WBC_ERROR_IS_OK(wbc_err)) {
377 async_req_error(req, wbc_err);
384 static wbcErr wb_int_trans_recv(struct async_req *req,
386 struct winbindd_response **presponse)
388 struct wb_int_trans_state *state = talloc_get_type_abort(
389 req->private_data, struct wb_int_trans_state);
392 if (async_req_is_wbcerr(req, &wbc_err)) {
396 *presponse = talloc_move(mem_ctx, &state->wb_resp);
397 return WBC_ERR_SUCCESS;
400 static const char *winbindd_socket_dir(void)
402 #ifdef SOCKET_WRAPPER
405 env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
411 return WINBINDD_SOCKET_DIR;
414 struct wb_open_pipe_state {
415 struct wb_context *wb_ctx;
416 struct tevent_context *ev;
418 struct winbindd_request wb_req;
421 static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq);
422 static void wb_open_pipe_ping_done(struct async_req *subreq);
423 static void wb_open_pipe_getpriv_done(struct async_req *subreq);
424 static void wb_open_pipe_connect_priv_done(struct async_req *subreq);
426 static struct async_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
427 struct tevent_context *ev,
428 struct wb_context *wb_ctx,
431 struct async_req *result;
432 struct async_req *subreq;
433 struct wb_open_pipe_state *state;
435 if (!async_req_setup(mem_ctx, &result, &state,
436 struct wb_open_pipe_state)) {
439 state->wb_ctx = wb_ctx;
441 state->need_priv = need_priv;
443 if (wb_ctx->fd != -1) {
448 subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
449 if (subreq == NULL) {
453 subreq->async.fn = wb_open_pipe_connect_nonpriv_done;
454 subreq->async.priv = result;
462 static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq)
464 struct async_req *req = talloc_get_type_abort(
465 subreq->async.priv, struct async_req);
466 struct wb_open_pipe_state *state = talloc_get_type_abort(
467 req->private_data, struct wb_open_pipe_state);
470 wbc_err = wb_connect_recv(subreq);
472 if (!WBC_ERROR_IS_OK(wbc_err)) {
473 state->wb_ctx->is_priv = true;
474 async_req_error(req, wbc_err);
478 ZERO_STRUCT(state->wb_req);
479 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
481 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
483 if (async_req_nomem(subreq, req)) {
487 subreq->async.fn = wb_open_pipe_ping_done;
488 subreq->async.priv = req;
491 static void wb_open_pipe_ping_done(struct async_req *subreq)
493 struct async_req *req = talloc_get_type_abort(
494 subreq->async.priv, struct async_req);
495 struct wb_open_pipe_state *state = talloc_get_type_abort(
496 req->private_data, struct wb_open_pipe_state);
497 struct winbindd_response *wb_resp;
500 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
502 if (!WBC_ERROR_IS_OK(wbc_err)) {
503 async_req_error(req, wbc_err);
507 if (!state->need_priv) {
512 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
514 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
516 if (async_req_nomem(subreq, req)) {
520 subreq->async.fn = wb_open_pipe_getpriv_done;
521 subreq->async.priv = req;
524 static void wb_open_pipe_getpriv_done(struct async_req *subreq)
526 struct async_req *req = talloc_get_type_abort(
527 subreq->async.priv, struct async_req);
528 struct wb_open_pipe_state *state = talloc_get_type_abort(
529 req->private_data, struct wb_open_pipe_state);
530 struct winbindd_response *wb_resp = NULL;
533 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
535 if (!WBC_ERROR_IS_OK(wbc_err)) {
536 async_req_error(req, wbc_err);
540 close(state->wb_ctx->fd);
541 state->wb_ctx->fd = -1;
543 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
544 (char *)wb_resp->extra_data.data);
545 TALLOC_FREE(wb_resp);
546 if (async_req_nomem(subreq, req)) {
550 subreq->async.fn = wb_open_pipe_connect_priv_done;
551 subreq->async.priv = req;
554 static void wb_open_pipe_connect_priv_done(struct async_req *subreq)
556 struct async_req *req = talloc_get_type_abort(
557 subreq->async.priv, struct async_req);
558 struct wb_open_pipe_state *state = talloc_get_type_abort(
559 req->private_data, struct wb_open_pipe_state);
562 wbc_err = wb_connect_recv(subreq);
564 if (!WBC_ERROR_IS_OK(wbc_err)) {
565 async_req_error(req, wbc_err);
568 state->wb_ctx->is_priv = true;
572 static wbcErr wb_open_pipe_recv(struct async_req *req)
574 return async_req_simple_recv_wbcerr(req);
577 struct wb_trans_state {
578 struct wb_trans_state *prev, *next;
579 struct wb_context *wb_ctx;
580 struct tevent_context *ev;
581 struct winbindd_request *wb_req;
582 struct winbindd_response *wb_resp;
587 static void wb_trans_connect_done(struct async_req *subreq);
588 static void wb_trans_done(struct async_req *subreq);
589 static void wb_trans_retry_wait_done(struct async_req *subreq);
591 static void wb_trigger_trans(struct async_req *req)
593 struct wb_trans_state *state = talloc_get_type_abort(
594 req->private_data, struct wb_trans_state);
595 struct async_req *subreq;
597 if ((state->wb_ctx->fd == -1)
598 || (state->need_priv && !state->wb_ctx->is_priv)) {
600 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
602 if (async_req_nomem(subreq, req)) {
605 subreq->async.fn = wb_trans_connect_done;
606 subreq->async.priv = req;
610 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
612 if (async_req_nomem(subreq, req)) {
615 subreq->async.fn = wb_trans_done;
616 subreq->async.priv = req;
619 struct async_req *wb_trans_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
620 struct wb_context *wb_ctx, bool need_priv,
621 const struct winbindd_request *wb_req)
623 struct async_req *result;
624 struct wb_trans_state *state;
626 if (!async_req_setup(mem_ctx, &result, &state,
627 struct wb_trans_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 (WBC_ERROR_IS_OK(wbc_err)) {
659 if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
661 * Winbind not around or we can't connect to the pipe. Fail
664 async_req_error(req, wbc_err);
668 state->num_retries -= 1;
669 if (state->num_retries == 0) {
670 async_req_error(req, wbc_err);
675 * The transfer as such failed, retry after one second
678 if (state->wb_ctx->fd != -1) {
679 close(state->wb_ctx->fd);
680 state->wb_ctx->fd = -1;
683 subreq = async_wait_send(state, state->ev, timeval_set(1, 0));
684 if (async_req_nomem(subreq, req)) {
688 subreq->async.fn = wb_trans_retry_wait_done;
689 subreq->async.priv = req;
693 static void wb_trans_retry_wait_done(struct async_req *subreq)
695 struct async_req *req = talloc_get_type_abort(
696 subreq->async.priv, struct async_req);
697 struct wb_trans_state *state = talloc_get_type_abort(
698 req->private_data, struct wb_trans_state);
701 ret = async_wait_recv(subreq);
704 async_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
708 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
710 if (async_req_nomem(subreq, req)) {
713 subreq->async.fn = wb_trans_connect_done;
714 subreq->async.priv = req;
717 static void wb_trans_connect_done(struct async_req *subreq)
719 struct async_req *req = talloc_get_type_abort(
720 subreq->async.priv, struct async_req);
721 struct wb_trans_state *state = talloc_get_type_abort(
722 req->private_data, struct wb_trans_state);
725 wbc_err = wb_open_pipe_recv(subreq);
728 if (wb_trans_retry(req, state, wbc_err)) {
732 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
734 if (async_req_nomem(subreq, req)) {
738 subreq->async.fn = wb_trans_done;
739 subreq->async.priv = req;
742 static void wb_trans_done(struct async_req *subreq)
744 struct async_req *req = talloc_get_type_abort(
745 subreq->async.priv, struct async_req);
746 struct wb_trans_state *state = talloc_get_type_abort(
747 req->private_data, struct wb_trans_state);
750 wbc_err = wb_int_trans_recv(subreq, state, &state->wb_resp);
753 if (wb_trans_retry(req, state, wbc_err)) {
760 wbcErr wb_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
761 struct winbindd_response **presponse)
763 struct wb_trans_state *state = talloc_get_type_abort(
764 req->private_data, struct wb_trans_state);
767 if (async_req_is_wbcerr(req, &wbc_err)) {
771 *presponse = talloc_move(mem_ctx, &state->wb_resp);
772 return WBC_ERR_SUCCESS;