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 tevent_req *wb_connect_send(TALLOC_CTX *mem_ctx,
157 struct tevent_context *ev,
158 struct wb_context *wb_ctx,
161 struct tevent_req *result, *subreq;
162 struct wb_connect_state *state;
163 struct sockaddr_un sunaddr;
168 result = tevent_req_create(mem_ctx, &state, struct wb_connect_state);
169 if (result == NULL) {
173 if (wb_ctx->fd != -1) {
178 /* Check permissions on unix socket directory */
180 if (lstat(dir, &st) == -1) {
181 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
185 if (!S_ISDIR(st.st_mode) ||
186 (st.st_uid != 0 && st.st_uid != geteuid())) {
187 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
191 /* Connect to socket */
193 path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
194 WINBINDD_SOCKET_NAME);
199 sunaddr.sun_family = AF_UNIX;
200 strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
203 /* If socket file doesn't exist, don't bother trying to connect
204 with retry. This is an attempt to make the system usable when
205 the winbindd daemon is not running. */
207 if ((lstat(sunaddr.sun_path, &st) == -1)
208 || !S_ISSOCK(st.st_mode)
209 || (st.st_uid != 0 && st.st_uid != geteuid())) {
210 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
214 wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
215 if (wb_ctx->fd == -1) {
216 wbc_err = map_wbc_err_from_errno(errno);
220 subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
221 (struct sockaddr *)&sunaddr,
223 if (subreq == NULL) {
226 tevent_req_set_callback(subreq, wbc_connect_connected, result);
228 if (!tevent_req_set_endtime(subreq, ev, timeval_current_ofs(30, 0))) {
235 tevent_req_error(result, wbc_err);
236 return tevent_req_post(result, ev);
242 static void wbc_connect_connected(struct tevent_req *subreq)
244 struct tevent_req *req = tevent_req_callback_data(
245 subreq, struct tevent_req);
248 res = async_connect_recv(subreq, &err);
251 tevent_req_error(req, map_wbc_err_from_errno(err));
254 tevent_req_done(req);
257 static wbcErr wb_connect_recv(struct tevent_req *req)
259 return tevent_req_simple_recv_wbcerr(req);
262 static struct winbindd_request *winbindd_request_copy(
264 const struct winbindd_request *req)
266 struct winbindd_request *result;
268 result = (struct winbindd_request *)TALLOC_MEMDUP(
269 mem_ctx, req, sizeof(struct winbindd_request));
270 if (result == NULL) {
274 if (result->extra_len == 0) {
278 result->extra_data.data = (char *)TALLOC_MEMDUP(
279 result, result->extra_data.data, result->extra_len);
280 if (result->extra_data.data == NULL) {
287 struct wb_int_trans_state {
288 struct tevent_context *ev;
290 struct winbindd_request *wb_req;
291 struct winbindd_response *wb_resp;
294 static void wb_int_trans_write_done(struct tevent_req *subreq);
295 static void wb_int_trans_read_done(struct tevent_req *subreq);
297 static struct tevent_req *wb_int_trans_send(TALLOC_CTX *mem_ctx,
298 struct tevent_context *ev,
299 struct tevent_queue *queue, int fd,
300 struct winbindd_request *wb_req)
302 struct tevent_req *result, *subreq;
303 struct wb_int_trans_state *state;
305 result = tevent_req_create(mem_ctx, &state,
306 struct wb_int_trans_state);
307 if (result == NULL) {
311 if (winbind_closed_fd(fd)) {
312 tevent_req_error(result, WBC_ERR_WINBIND_NOT_AVAILABLE);
313 return tevent_req_post(result, ev);
318 state->wb_req = wb_req;
319 state->wb_req->length = sizeof(struct winbindd_request);
320 state->wb_req->pid = getpid();
322 subreq = wb_req_write_send(state, state->ev, queue, state->fd,
324 if (subreq == NULL) {
327 tevent_req_set_callback(subreq, wb_int_trans_write_done, result);
336 static void wb_int_trans_write_done(struct tevent_req *subreq)
338 struct tevent_req *req = tevent_req_callback_data(
339 subreq, struct tevent_req);
340 struct wb_int_trans_state *state = tevent_req_data(
341 req, struct wb_int_trans_state);
344 wbc_err = wb_req_write_recv(subreq);
346 if (!WBC_ERROR_IS_OK(wbc_err)) {
347 tevent_req_error(req, wbc_err);
351 subreq = wb_resp_read_send(state, state->ev, state->fd);
352 if (tevent_req_nomem(subreq, req)) {
355 tevent_req_set_callback(subreq, wb_int_trans_read_done, req);
358 static void wb_int_trans_read_done(struct tevent_req *subreq)
360 struct tevent_req *req = tevent_req_callback_data(
361 subreq, struct tevent_req);
362 struct wb_int_trans_state *state = tevent_req_data(
363 req, struct wb_int_trans_state);
366 wbc_err = wb_resp_read_recv(subreq, state, &state->wb_resp);
368 if (!WBC_ERROR_IS_OK(wbc_err)) {
369 tevent_req_error(req, wbc_err);
373 tevent_req_done(req);
376 static wbcErr wb_int_trans_recv(struct tevent_req *req,
378 struct winbindd_response **presponse)
380 struct wb_int_trans_state *state = tevent_req_data(
381 req, struct wb_int_trans_state);
384 if (tevent_req_is_wbcerr(req, &wbc_err)) {
388 *presponse = talloc_move(mem_ctx, &state->wb_resp);
389 return WBC_ERR_SUCCESS;
392 static const char *winbindd_socket_dir(void)
394 #ifdef SOCKET_WRAPPER
397 env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
403 return WINBINDD_SOCKET_DIR;
406 struct wb_open_pipe_state {
407 struct wb_context *wb_ctx;
408 struct tevent_context *ev;
410 struct winbindd_request wb_req;
413 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq);
414 static void wb_open_pipe_ping_done(struct tevent_req *subreq);
415 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq);
416 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq);
418 static struct async_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
419 struct tevent_context *ev,
420 struct wb_context *wb_ctx,
423 struct async_req *result;
424 struct tevent_req *subreq;
425 struct wb_open_pipe_state *state;
427 if (!async_req_setup(mem_ctx, &result, &state,
428 struct wb_open_pipe_state)) {
431 state->wb_ctx = wb_ctx;
433 state->need_priv = need_priv;
435 if (wb_ctx->fd != -1) {
440 subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
441 if (subreq == NULL) {
444 tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
453 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
455 struct async_req *req = tevent_req_callback_data(
456 subreq, struct async_req);
457 struct wb_open_pipe_state *state = talloc_get_type_abort(
458 req->private_data, struct wb_open_pipe_state);
461 wbc_err = wb_connect_recv(subreq);
463 if (!WBC_ERROR_IS_OK(wbc_err)) {
464 state->wb_ctx->is_priv = true;
465 async_req_error(req, wbc_err);
469 ZERO_STRUCT(state->wb_req);
470 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
472 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
474 if (async_req_nomem(subreq, req)) {
477 tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
480 static void wb_open_pipe_ping_done(struct tevent_req *subreq)
482 struct async_req *req = tevent_req_callback_data(
483 subreq, struct async_req);
484 struct wb_open_pipe_state *state = talloc_get_type_abort(
485 req->private_data, struct wb_open_pipe_state);
486 struct winbindd_response *wb_resp;
489 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
491 if (!WBC_ERROR_IS_OK(wbc_err)) {
492 async_req_error(req, wbc_err);
496 if (!state->need_priv) {
501 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
503 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
505 if (async_req_nomem(subreq, req)) {
508 tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
511 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
513 struct async_req *req = tevent_req_callback_data(
514 subreq, struct async_req);
515 struct wb_open_pipe_state *state = talloc_get_type_abort(
516 req->private_data, struct wb_open_pipe_state);
517 struct winbindd_response *wb_resp = NULL;
520 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
522 if (!WBC_ERROR_IS_OK(wbc_err)) {
523 async_req_error(req, wbc_err);
527 close(state->wb_ctx->fd);
528 state->wb_ctx->fd = -1;
530 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
531 (char *)wb_resp->extra_data.data);
532 TALLOC_FREE(wb_resp);
533 if (async_req_nomem(subreq, req)) {
536 tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
539 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
541 struct async_req *req = tevent_req_callback_data(
542 subreq, struct async_req);
543 struct wb_open_pipe_state *state = talloc_get_type_abort(
544 req->private_data, struct wb_open_pipe_state);
547 wbc_err = wb_connect_recv(subreq);
549 if (!WBC_ERROR_IS_OK(wbc_err)) {
550 async_req_error(req, wbc_err);
553 state->wb_ctx->is_priv = true;
557 static wbcErr wb_open_pipe_recv(struct async_req *req)
559 return async_req_simple_recv_wbcerr(req);
562 struct wb_trans_state {
563 struct wb_trans_state *prev, *next;
564 struct wb_context *wb_ctx;
565 struct tevent_context *ev;
566 struct winbindd_request *wb_req;
567 struct winbindd_response *wb_resp;
572 static void wb_trans_connect_done(struct async_req *subreq);
573 static void wb_trans_done(struct tevent_req *subreq);
574 static void wb_trans_retry_wait_done(struct async_req *subreq);
576 static void wb_trigger_trans(struct async_req *req)
578 struct wb_trans_state *state = talloc_get_type_abort(
579 req->private_data, struct wb_trans_state);
580 struct tevent_req *subreq;
582 if ((state->wb_ctx->fd == -1)
583 || (state->need_priv && !state->wb_ctx->is_priv)) {
585 struct async_req *subreq2;
587 subreq2 = wb_open_pipe_send(state, state->ev, state->wb_ctx,
589 if (async_req_nomem(subreq2, req)) {
592 subreq2->async.fn = wb_trans_connect_done;
593 subreq2->async.priv = req;
597 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
599 if (async_req_nomem(subreq, req)) {
602 tevent_req_set_callback(subreq, wb_trans_done, req);
605 struct async_req *wb_trans_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
606 struct wb_context *wb_ctx, bool need_priv,
607 const struct winbindd_request *wb_req)
609 struct async_req *result;
610 struct wb_trans_state *state;
612 if (!async_req_setup(mem_ctx, &result, &state,
613 struct wb_trans_state)) {
616 state->wb_ctx = wb_ctx;
618 state->wb_req = winbindd_request_copy(state, wb_req);
619 if (state->wb_req == NULL) {
622 state->num_retries = 10;
623 state->need_priv = need_priv;
625 if (!async_req_enqueue(wb_ctx->queue, ev, result, wb_trigger_trans)) {
635 static bool wb_trans_retry(struct async_req *req,
636 struct wb_trans_state *state,
639 struct async_req *subreq;
641 if (WBC_ERROR_IS_OK(wbc_err)) {
645 if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
647 * Winbind not around or we can't connect to the pipe. Fail
650 async_req_error(req, wbc_err);
654 state->num_retries -= 1;
655 if (state->num_retries == 0) {
656 async_req_error(req, wbc_err);
661 * The transfer as such failed, retry after one second
664 if (state->wb_ctx->fd != -1) {
665 close(state->wb_ctx->fd);
666 state->wb_ctx->fd = -1;
669 subreq = async_wait_send(state, state->ev, timeval_set(1, 0));
670 if (async_req_nomem(subreq, req)) {
674 subreq->async.fn = wb_trans_retry_wait_done;
675 subreq->async.priv = req;
679 static void wb_trans_retry_wait_done(struct async_req *subreq)
681 struct async_req *req = talloc_get_type_abort(
682 subreq->async.priv, struct async_req);
683 struct wb_trans_state *state = talloc_get_type_abort(
684 req->private_data, struct wb_trans_state);
687 ret = async_wait_recv(subreq);
690 async_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
694 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
696 if (async_req_nomem(subreq, req)) {
699 subreq->async.fn = wb_trans_connect_done;
700 subreq->async.priv = req;
703 static void wb_trans_connect_done(struct async_req *subreq)
705 struct async_req *req = talloc_get_type_abort(
706 subreq->async.priv, struct async_req);
707 struct wb_trans_state *state = talloc_get_type_abort(
708 req->private_data, struct wb_trans_state);
709 struct tevent_req *subreq2;
712 wbc_err = wb_open_pipe_recv(subreq);
715 if (wb_trans_retry(req, state, wbc_err)) {
719 subreq2 = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
721 if (async_req_nomem(subreq2, req)) {
724 tevent_req_set_callback(subreq2, wb_trans_done, req);
727 static void wb_trans_done(struct tevent_req *subreq)
729 struct async_req *req = tevent_req_callback_data(
730 subreq, struct async_req);
731 struct wb_trans_state *state = talloc_get_type_abort(
732 req->private_data, struct wb_trans_state);
735 wbc_err = wb_int_trans_recv(subreq, state, &state->wb_resp);
738 if (wb_trans_retry(req, state, wbc_err)) {
745 wbcErr wb_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
746 struct winbindd_response **presponse)
748 struct wb_trans_state *state = talloc_get_type_abort(
749 req->private_data, struct wb_trans_state);
752 if (async_req_is_wbcerr(req, &wbc_err)) {
756 *presponse = talloc_move(mem_ctx, &state->wb_resp);
757 return WBC_ERR_SUCCESS;