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"
24 struct tevent_queue *queue;
29 static int make_nonstd_fd(int fd)
47 for (i=0; i<num_fds; i++) {
56 /****************************************************************************
57 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
61 Set close on exec also.
62 ****************************************************************************/
64 static int make_safe_fd(int fd)
67 int new_fd = make_nonstd_fd(fd);
73 /* Socket should be nonblocking. */
76 #define FLAG_TO_SET O_NONBLOCK
79 #define FLAG_TO_SET O_NDELAY
81 #define FLAG_TO_SET FNDELAY
85 if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
90 if (fcntl(new_fd, F_SETFL, flags) == -1) {
96 /* Socket should be closed on exec() */
98 result = flags = fcntl(new_fd, F_GETFD, 0);
101 result = fcntl( new_fd, F_SETFD, flags );
111 int sys_errno = errno;
118 static bool winbind_closed_fd(int fd)
131 if ((select(fd+1, &r_fds, NULL, NULL, &tv) == -1)
132 || FD_ISSET(fd, &r_fds)) {
139 struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx)
141 struct wb_context *result;
143 result = talloc(mem_ctx, struct wb_context);
144 if (result == NULL) {
147 result->queue = tevent_queue_create(result, "wb_trans");
148 if (result->queue == NULL) {
156 struct wb_connect_state {
160 static void wbc_connect_connected(struct tevent_req *subreq);
162 static struct tevent_req *wb_connect_send(TALLOC_CTX *mem_ctx,
163 struct tevent_context *ev,
164 struct wb_context *wb_ctx,
167 struct tevent_req *result, *subreq;
168 struct wb_connect_state *state;
169 struct sockaddr_un sunaddr;
174 result = tevent_req_create(mem_ctx, &state, struct wb_connect_state);
175 if (result == NULL) {
179 if (wb_ctx->fd != -1) {
184 /* Check permissions on unix socket directory */
186 if (lstat(dir, &st) == -1) {
187 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
191 if (!S_ISDIR(st.st_mode) ||
192 (st.st_uid != 0 && st.st_uid != geteuid())) {
193 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
197 /* Connect to socket */
199 path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
200 WINBINDD_SOCKET_NAME);
205 sunaddr.sun_family = AF_UNIX;
206 strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
209 /* If socket file doesn't exist, don't bother trying to connect
210 with retry. This is an attempt to make the system usable when
211 the winbindd daemon is not running. */
213 if ((lstat(sunaddr.sun_path, &st) == -1)
214 || !S_ISSOCK(st.st_mode)
215 || (st.st_uid != 0 && st.st_uid != geteuid())) {
216 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
220 wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
221 if (wb_ctx->fd == -1) {
222 wbc_err = map_wbc_err_from_errno(errno);
226 subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
227 (struct sockaddr *)&sunaddr,
229 if (subreq == NULL) {
232 tevent_req_set_callback(subreq, wbc_connect_connected, result);
234 if (!tevent_req_set_endtime(subreq, ev, timeval_current_ofs(30, 0))) {
241 tevent_req_error(result, wbc_err);
242 return tevent_req_post(result, ev);
248 static void wbc_connect_connected(struct tevent_req *subreq)
250 struct tevent_req *req = tevent_req_callback_data(
251 subreq, struct tevent_req);
254 res = async_connect_recv(subreq, &err);
257 tevent_req_error(req, map_wbc_err_from_errno(err));
260 tevent_req_done(req);
263 static wbcErr wb_connect_recv(struct tevent_req *req)
265 return tevent_req_simple_recv_wbcerr(req);
268 struct wb_int_trans_state {
269 struct tevent_context *ev;
271 struct winbindd_request *wb_req;
272 struct winbindd_response *wb_resp;
275 static void wb_int_trans_write_done(struct tevent_req *subreq);
276 static void wb_int_trans_read_done(struct tevent_req *subreq);
278 static struct tevent_req *wb_int_trans_send(TALLOC_CTX *mem_ctx,
279 struct tevent_context *ev,
280 struct tevent_queue *queue, int fd,
281 struct winbindd_request *wb_req)
283 struct tevent_req *result, *subreq;
284 struct wb_int_trans_state *state;
286 result = tevent_req_create(mem_ctx, &state,
287 struct wb_int_trans_state);
288 if (result == NULL) {
292 if (winbind_closed_fd(fd)) {
293 tevent_req_error(result, WBC_ERR_WINBIND_NOT_AVAILABLE);
294 return tevent_req_post(result, ev);
299 state->wb_req = wb_req;
300 state->wb_req->length = sizeof(struct winbindd_request);
301 state->wb_req->pid = getpid();
303 subreq = wb_req_write_send(state, state->ev, queue, state->fd,
305 if (subreq == NULL) {
308 tevent_req_set_callback(subreq, wb_int_trans_write_done, result);
317 static void wb_int_trans_write_done(struct tevent_req *subreq)
319 struct tevent_req *req = tevent_req_callback_data(
320 subreq, struct tevent_req);
321 struct wb_int_trans_state *state = tevent_req_data(
322 req, struct wb_int_trans_state);
325 wbc_err = wb_req_write_recv(subreq);
327 if (!WBC_ERROR_IS_OK(wbc_err)) {
328 tevent_req_error(req, wbc_err);
332 subreq = wb_resp_read_send(state, state->ev, state->fd);
333 if (tevent_req_nomem(subreq, req)) {
336 tevent_req_set_callback(subreq, wb_int_trans_read_done, req);
339 static void wb_int_trans_read_done(struct tevent_req *subreq)
341 struct tevent_req *req = tevent_req_callback_data(
342 subreq, struct tevent_req);
343 struct wb_int_trans_state *state = tevent_req_data(
344 req, struct wb_int_trans_state);
347 wbc_err = wb_resp_read_recv(subreq, state, &state->wb_resp);
349 if (!WBC_ERROR_IS_OK(wbc_err)) {
350 tevent_req_error(req, wbc_err);
354 tevent_req_done(req);
357 static wbcErr wb_int_trans_recv(struct tevent_req *req,
359 struct winbindd_response **presponse)
361 struct wb_int_trans_state *state = tevent_req_data(
362 req, struct wb_int_trans_state);
365 if (tevent_req_is_wbcerr(req, &wbc_err)) {
369 *presponse = talloc_move(mem_ctx, &state->wb_resp);
370 return WBC_ERR_SUCCESS;
373 static const char *winbindd_socket_dir(void)
375 #ifdef SOCKET_WRAPPER
378 env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
384 return WINBINDD_SOCKET_DIR;
387 struct wb_open_pipe_state {
388 struct wb_context *wb_ctx;
389 struct tevent_context *ev;
391 struct winbindd_request wb_req;
394 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq);
395 static void wb_open_pipe_ping_done(struct tevent_req *subreq);
396 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq);
397 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq);
399 static struct tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
400 struct tevent_context *ev,
401 struct wb_context *wb_ctx,
404 struct tevent_req *result, *subreq;
405 struct wb_open_pipe_state *state;
407 result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
408 if (result == NULL) {
411 state->wb_ctx = wb_ctx;
413 state->need_priv = need_priv;
415 if (wb_ctx->fd != -1) {
420 subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
421 if (subreq == NULL) {
424 tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
433 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
435 struct tevent_req *req = tevent_req_callback_data(
436 subreq, struct tevent_req);
437 struct wb_open_pipe_state *state = tevent_req_data(
438 req, struct wb_open_pipe_state);
441 wbc_err = wb_connect_recv(subreq);
443 if (!WBC_ERROR_IS_OK(wbc_err)) {
444 state->wb_ctx->is_priv = true;
445 tevent_req_error(req, wbc_err);
449 ZERO_STRUCT(state->wb_req);
450 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
452 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
454 if (tevent_req_nomem(subreq, req)) {
457 tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
460 static void wb_open_pipe_ping_done(struct tevent_req *subreq)
462 struct tevent_req *req = tevent_req_callback_data(
463 subreq, struct tevent_req);
464 struct wb_open_pipe_state *state = tevent_req_data(
465 req, struct wb_open_pipe_state);
466 struct winbindd_response *wb_resp;
469 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
471 if (!WBC_ERROR_IS_OK(wbc_err)) {
472 tevent_req_error(req, wbc_err);
476 if (!state->need_priv) {
477 tevent_req_done(req);
481 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
483 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
485 if (tevent_req_nomem(subreq, req)) {
488 tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
491 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
493 struct tevent_req *req = tevent_req_callback_data(
494 subreq, struct tevent_req);
495 struct wb_open_pipe_state *state = tevent_req_data(
496 req, struct wb_open_pipe_state);
497 struct winbindd_response *wb_resp = NULL;
500 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
502 if (!WBC_ERROR_IS_OK(wbc_err)) {
503 tevent_req_error(req, wbc_err);
507 close(state->wb_ctx->fd);
508 state->wb_ctx->fd = -1;
510 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
511 (char *)wb_resp->extra_data.data);
512 TALLOC_FREE(wb_resp);
513 if (tevent_req_nomem(subreq, req)) {
516 tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
519 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
521 struct tevent_req *req = tevent_req_callback_data(
522 subreq, struct tevent_req);
523 struct wb_open_pipe_state *state = tevent_req_data(
524 req, struct wb_open_pipe_state);
527 wbc_err = wb_connect_recv(subreq);
529 if (!WBC_ERROR_IS_OK(wbc_err)) {
530 tevent_req_error(req, wbc_err);
533 state->wb_ctx->is_priv = true;
534 tevent_req_done(req);
537 static wbcErr wb_open_pipe_recv(struct tevent_req *req)
539 return tevent_req_simple_recv_wbcerr(req);
542 struct wb_trans_state {
543 struct wb_trans_state *prev, *next;
544 struct wb_context *wb_ctx;
545 struct tevent_context *ev;
546 struct winbindd_request *wb_req;
547 struct winbindd_response *wb_resp;
552 static void wb_trans_connect_done(struct tevent_req *subreq);
553 static void wb_trans_done(struct tevent_req *subreq);
554 static void wb_trans_retry_wait_done(struct tevent_req *subreq);
556 struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
557 struct tevent_context *ev,
558 struct wb_context *wb_ctx, bool need_priv,
559 struct winbindd_request *wb_req)
561 struct tevent_req *req, *subreq;
562 struct wb_trans_state *state;
564 req = tevent_req_create(mem_ctx, &state, struct wb_trans_state);
568 state->wb_ctx = wb_ctx;
570 state->wb_req = wb_req;
571 state->num_retries = 10;
572 state->need_priv = need_priv;
574 if ((wb_ctx->fd == -1) || (need_priv && !wb_ctx->is_priv)) {
575 subreq = wb_open_pipe_send(state, ev, wb_ctx, need_priv);
576 if (subreq == NULL) {
579 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
583 subreq = wb_int_trans_send(state, ev, wb_ctx->queue, wb_ctx->fd,
585 if (subreq == NULL) {
588 tevent_req_set_callback(subreq, wb_trans_done, req);
595 static bool wb_trans_retry(struct tevent_req *req,
596 struct wb_trans_state *state,
599 struct tevent_req *subreq;
601 if (WBC_ERROR_IS_OK(wbc_err)) {
605 if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
607 * Winbind not around or we can't connect to the pipe. Fail
610 tevent_req_error(req, wbc_err);
614 state->num_retries -= 1;
615 if (state->num_retries == 0) {
616 tevent_req_error(req, wbc_err);
621 * The transfer as such failed, retry after one second
624 if (state->wb_ctx->fd != -1) {
625 close(state->wb_ctx->fd);
626 state->wb_ctx->fd = -1;
629 subreq = tevent_wakeup_send(state, state->ev,
630 timeval_current_ofs(1, 0));
631 if (tevent_req_nomem(subreq, req)) {
634 tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req);
638 static void wb_trans_retry_wait_done(struct tevent_req *subreq)
640 struct tevent_req *req = tevent_req_callback_data(
641 subreq, struct tevent_req);
642 struct wb_trans_state *state = tevent_req_data(
643 req, struct wb_trans_state);
646 ret = tevent_wakeup_recv(subreq);
649 tevent_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
653 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
655 if (tevent_req_nomem(subreq, req)) {
658 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
661 static void wb_trans_connect_done(struct tevent_req *subreq)
663 struct tevent_req *req = tevent_req_callback_data(
664 subreq, struct tevent_req);
665 struct wb_trans_state *state = tevent_req_data(
666 req, struct wb_trans_state);
669 wbc_err = wb_open_pipe_recv(subreq);
672 if (wb_trans_retry(req, state, wbc_err)) {
676 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
678 if (tevent_req_nomem(subreq, req)) {
681 tevent_req_set_callback(subreq, wb_trans_done, req);
684 static void wb_trans_done(struct tevent_req *subreq)
686 struct tevent_req *req = tevent_req_callback_data(
687 subreq, struct tevent_req);
688 struct wb_trans_state *state = tevent_req_data(
689 req, struct wb_trans_state);
692 wbc_err = wb_int_trans_recv(subreq, state, &state->wb_resp);
695 if (wb_trans_retry(req, state, wbc_err)) {
699 tevent_req_done(req);
702 wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
703 struct winbindd_response **presponse)
705 struct wb_trans_state *state = tevent_req_data(
706 req, struct wb_trans_state);
709 if (tevent_req_is_wbcerr(req, &wbc_err)) {
713 *presponse = talloc_move(mem_ctx, &state->wb_resp);
714 return WBC_ERR_SUCCESS;