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 tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
419 struct tevent_context *ev,
420 struct wb_context *wb_ctx,
423 struct tevent_req *result, *subreq;
424 struct wb_open_pipe_state *state;
426 result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
427 if (result == NULL) {
430 state->wb_ctx = wb_ctx;
432 state->need_priv = need_priv;
434 if (wb_ctx->fd != -1) {
439 subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
440 if (subreq == NULL) {
443 tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
452 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
454 struct tevent_req *req = tevent_req_callback_data(
455 subreq, struct tevent_req);
456 struct wb_open_pipe_state *state = tevent_req_data(
457 req, struct wb_open_pipe_state);
460 wbc_err = wb_connect_recv(subreq);
462 if (!WBC_ERROR_IS_OK(wbc_err)) {
463 state->wb_ctx->is_priv = true;
464 tevent_req_error(req, wbc_err);
468 ZERO_STRUCT(state->wb_req);
469 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
471 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
473 if (tevent_req_nomem(subreq, req)) {
476 tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
479 static void wb_open_pipe_ping_done(struct tevent_req *subreq)
481 struct tevent_req *req = tevent_req_callback_data(
482 subreq, struct tevent_req);
483 struct wb_open_pipe_state *state = tevent_req_data(
484 req, struct wb_open_pipe_state);
485 struct winbindd_response *wb_resp;
488 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
490 if (!WBC_ERROR_IS_OK(wbc_err)) {
491 tevent_req_error(req, wbc_err);
495 if (!state->need_priv) {
496 tevent_req_done(req);
500 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
502 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
504 if (tevent_req_nomem(subreq, req)) {
507 tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
510 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
512 struct tevent_req *req = tevent_req_callback_data(
513 subreq, struct tevent_req);
514 struct wb_open_pipe_state *state = tevent_req_data(
515 req, struct wb_open_pipe_state);
516 struct winbindd_response *wb_resp = NULL;
519 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
521 if (!WBC_ERROR_IS_OK(wbc_err)) {
522 tevent_req_error(req, wbc_err);
526 close(state->wb_ctx->fd);
527 state->wb_ctx->fd = -1;
529 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
530 (char *)wb_resp->extra_data.data);
531 TALLOC_FREE(wb_resp);
532 if (tevent_req_nomem(subreq, req)) {
535 tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
538 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
540 struct tevent_req *req = tevent_req_callback_data(
541 subreq, struct tevent_req);
542 struct wb_open_pipe_state *state = tevent_req_data(
543 req, struct wb_open_pipe_state);
546 wbc_err = wb_connect_recv(subreq);
548 if (!WBC_ERROR_IS_OK(wbc_err)) {
549 tevent_req_error(req, wbc_err);
552 state->wb_ctx->is_priv = true;
553 tevent_req_done(req);
556 static wbcErr wb_open_pipe_recv(struct tevent_req *req)
558 return tevent_req_simple_recv_wbcerr(req);
561 struct wb_trans_state {
562 struct wb_trans_state *prev, *next;
563 struct wb_context *wb_ctx;
564 struct tevent_context *ev;
565 struct winbindd_request *wb_req;
566 struct winbindd_response *wb_resp;
571 static void wb_trans_connect_done(struct tevent_req *subreq);
572 static void wb_trans_done(struct tevent_req *subreq);
573 static void wb_trans_retry_wait_done(struct async_req *subreq);
575 static void wb_trigger_trans(struct async_req *req)
577 struct wb_trans_state *state = talloc_get_type_abort(
578 req->private_data, struct wb_trans_state);
579 struct tevent_req *subreq;
581 if ((state->wb_ctx->fd == -1)
582 || (state->need_priv && !state->wb_ctx->is_priv)) {
584 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
586 if (async_req_nomem(subreq, req)) {
589 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
593 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
595 if (async_req_nomem(subreq, req)) {
598 tevent_req_set_callback(subreq, wb_trans_done, req);
601 struct async_req *wb_trans_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
602 struct wb_context *wb_ctx, bool need_priv,
603 const struct winbindd_request *wb_req)
605 struct async_req *result;
606 struct wb_trans_state *state;
608 if (!async_req_setup(mem_ctx, &result, &state,
609 struct wb_trans_state)) {
612 state->wb_ctx = wb_ctx;
614 state->wb_req = winbindd_request_copy(state, wb_req);
615 if (state->wb_req == NULL) {
618 state->num_retries = 10;
619 state->need_priv = need_priv;
621 if (!async_req_enqueue(wb_ctx->queue, ev, result, wb_trigger_trans)) {
631 static bool wb_trans_retry(struct async_req *req,
632 struct wb_trans_state *state,
635 struct async_req *subreq;
637 if (WBC_ERROR_IS_OK(wbc_err)) {
641 if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
643 * Winbind not around or we can't connect to the pipe. Fail
646 async_req_error(req, wbc_err);
650 state->num_retries -= 1;
651 if (state->num_retries == 0) {
652 async_req_error(req, wbc_err);
657 * The transfer as such failed, retry after one second
660 if (state->wb_ctx->fd != -1) {
661 close(state->wb_ctx->fd);
662 state->wb_ctx->fd = -1;
665 subreq = async_wait_send(state, state->ev, timeval_set(1, 0));
666 if (async_req_nomem(subreq, req)) {
670 subreq->async.fn = wb_trans_retry_wait_done;
671 subreq->async.priv = req;
675 static void wb_trans_retry_wait_done(struct async_req *subreq)
677 struct async_req *req = talloc_get_type_abort(
678 subreq->async.priv, struct async_req);
679 struct wb_trans_state *state = talloc_get_type_abort(
680 req->private_data, struct wb_trans_state);
681 struct tevent_req *subreq2;
684 ret = async_wait_recv(subreq);
687 async_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
691 subreq2 = wb_open_pipe_send(state, state->ev, state->wb_ctx,
693 if (async_req_nomem(subreq2, req)) {
696 tevent_req_set_callback(subreq2, wb_trans_connect_done, req);
699 static void wb_trans_connect_done(struct tevent_req *subreq)
701 struct async_req *req = tevent_req_callback_data(
702 subreq, struct async_req);
703 struct wb_trans_state *state = talloc_get_type_abort(
704 req->private_data, struct wb_trans_state);
705 struct tevent_req *subreq2;
708 wbc_err = wb_open_pipe_recv(subreq);
711 if (wb_trans_retry(req, state, wbc_err)) {
715 subreq2 = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
717 if (async_req_nomem(subreq2, req)) {
720 tevent_req_set_callback(subreq2, wb_trans_done, req);
723 static void wb_trans_done(struct tevent_req *subreq)
725 struct async_req *req = tevent_req_callback_data(
726 subreq, struct async_req);
727 struct wb_trans_state *state = talloc_get_type_abort(
728 req->private_data, struct wb_trans_state);
731 wbc_err = wb_int_trans_recv(subreq, state, &state->wb_resp);
734 if (wb_trans_retry(req, state, wbc_err)) {
741 wbcErr wb_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
742 struct winbindd_response **presponse)
744 struct wb_trans_state *state = talloc_get_type_abort(
745 req->private_data, struct wb_trans_state);
748 if (async_req_is_wbcerr(req, &wbc_err)) {
752 *presponse = talloc_move(mem_ctx, &state->wb_resp);
753 return WBC_ERR_SUCCESS;