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)
123 if (fd == -1 || fd >= FD_SETSIZE) {
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);
225 if (wb_ctx->fd >= FD_SETSIZE) {
229 wbc_err = map_wbc_err_from_errno(errno);
233 subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
234 (struct sockaddr *)&sunaddr,
236 if (subreq == NULL) {
239 tevent_req_set_callback(subreq, wbc_connect_connected, result);
241 if (!tevent_req_set_endtime(subreq, ev, timeval_current_ofs(30, 0))) {
248 tevent_req_error(result, wbc_err);
249 return tevent_req_post(result, ev);
255 static void wbc_connect_connected(struct tevent_req *subreq)
257 struct tevent_req *req = tevent_req_callback_data(
258 subreq, struct tevent_req);
261 res = async_connect_recv(subreq, &err);
264 tevent_req_error(req, map_wbc_err_from_errno(err));
267 tevent_req_done(req);
270 static wbcErr wb_connect_recv(struct tevent_req *req)
272 return tevent_req_simple_recv_wbcerr(req);
275 struct wb_int_trans_state {
276 struct tevent_context *ev;
278 struct winbindd_request *wb_req;
279 struct winbindd_response *wb_resp;
282 static void wb_int_trans_write_done(struct tevent_req *subreq);
283 static void wb_int_trans_read_done(struct tevent_req *subreq);
285 static struct tevent_req *wb_int_trans_send(TALLOC_CTX *mem_ctx,
286 struct tevent_context *ev,
287 struct tevent_queue *queue, int fd,
288 struct winbindd_request *wb_req)
290 struct tevent_req *result, *subreq;
291 struct wb_int_trans_state *state;
293 result = tevent_req_create(mem_ctx, &state,
294 struct wb_int_trans_state);
295 if (result == NULL) {
299 if (winbind_closed_fd(fd)) {
300 tevent_req_error(result, WBC_ERR_WINBIND_NOT_AVAILABLE);
301 return tevent_req_post(result, ev);
306 state->wb_req = wb_req;
307 state->wb_req->length = sizeof(struct winbindd_request);
308 state->wb_req->pid = getpid();
310 subreq = wb_req_write_send(state, state->ev, queue, state->fd,
312 if (subreq == NULL) {
315 tevent_req_set_callback(subreq, wb_int_trans_write_done, result);
324 static void wb_int_trans_write_done(struct tevent_req *subreq)
326 struct tevent_req *req = tevent_req_callback_data(
327 subreq, struct tevent_req);
328 struct wb_int_trans_state *state = tevent_req_data(
329 req, struct wb_int_trans_state);
332 wbc_err = wb_req_write_recv(subreq);
334 if (!WBC_ERROR_IS_OK(wbc_err)) {
335 tevent_req_error(req, wbc_err);
339 subreq = wb_resp_read_send(state, state->ev, state->fd);
340 if (tevent_req_nomem(subreq, req)) {
343 tevent_req_set_callback(subreq, wb_int_trans_read_done, req);
346 static void wb_int_trans_read_done(struct tevent_req *subreq)
348 struct tevent_req *req = tevent_req_callback_data(
349 subreq, struct tevent_req);
350 struct wb_int_trans_state *state = tevent_req_data(
351 req, struct wb_int_trans_state);
354 wbc_err = wb_resp_read_recv(subreq, state, &state->wb_resp);
356 if (!WBC_ERROR_IS_OK(wbc_err)) {
357 tevent_req_error(req, wbc_err);
361 tevent_req_done(req);
364 static wbcErr wb_int_trans_recv(struct tevent_req *req,
366 struct winbindd_response **presponse)
368 struct wb_int_trans_state *state = tevent_req_data(
369 req, struct wb_int_trans_state);
372 if (tevent_req_is_wbcerr(req, &wbc_err)) {
376 *presponse = talloc_move(mem_ctx, &state->wb_resp);
377 return WBC_ERR_SUCCESS;
380 static const char *winbindd_socket_dir(void)
382 #ifdef SOCKET_WRAPPER
385 env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
391 return WINBINDD_SOCKET_DIR;
394 struct wb_open_pipe_state {
395 struct wb_context *wb_ctx;
396 struct tevent_context *ev;
398 struct winbindd_request wb_req;
401 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq);
402 static void wb_open_pipe_ping_done(struct tevent_req *subreq);
403 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq);
404 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq);
406 static struct tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
407 struct tevent_context *ev,
408 struct wb_context *wb_ctx,
411 struct tevent_req *result, *subreq;
412 struct wb_open_pipe_state *state;
414 result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
415 if (result == NULL) {
418 state->wb_ctx = wb_ctx;
420 state->need_priv = need_priv;
422 if (wb_ctx->fd != -1) {
427 subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
428 if (subreq == NULL) {
431 tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
440 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
442 struct tevent_req *req = tevent_req_callback_data(
443 subreq, struct tevent_req);
444 struct wb_open_pipe_state *state = tevent_req_data(
445 req, struct wb_open_pipe_state);
448 wbc_err = wb_connect_recv(subreq);
450 if (!WBC_ERROR_IS_OK(wbc_err)) {
451 state->wb_ctx->is_priv = true;
452 tevent_req_error(req, wbc_err);
456 ZERO_STRUCT(state->wb_req);
457 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
459 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
461 if (tevent_req_nomem(subreq, req)) {
464 tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
467 static void wb_open_pipe_ping_done(struct tevent_req *subreq)
469 struct tevent_req *req = tevent_req_callback_data(
470 subreq, struct tevent_req);
471 struct wb_open_pipe_state *state = tevent_req_data(
472 req, struct wb_open_pipe_state);
473 struct winbindd_response *wb_resp;
476 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
478 if (!WBC_ERROR_IS_OK(wbc_err)) {
479 tevent_req_error(req, wbc_err);
483 if (!state->need_priv) {
484 tevent_req_done(req);
488 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
490 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
492 if (tevent_req_nomem(subreq, req)) {
495 tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
498 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
500 struct tevent_req *req = tevent_req_callback_data(
501 subreq, struct tevent_req);
502 struct wb_open_pipe_state *state = tevent_req_data(
503 req, struct wb_open_pipe_state);
504 struct winbindd_response *wb_resp = NULL;
507 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
509 if (!WBC_ERROR_IS_OK(wbc_err)) {
510 tevent_req_error(req, wbc_err);
514 close(state->wb_ctx->fd);
515 state->wb_ctx->fd = -1;
517 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
518 (char *)wb_resp->extra_data.data);
519 TALLOC_FREE(wb_resp);
520 if (tevent_req_nomem(subreq, req)) {
523 tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
526 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
528 struct tevent_req *req = tevent_req_callback_data(
529 subreq, struct tevent_req);
530 struct wb_open_pipe_state *state = tevent_req_data(
531 req, struct wb_open_pipe_state);
534 wbc_err = wb_connect_recv(subreq);
536 if (!WBC_ERROR_IS_OK(wbc_err)) {
537 tevent_req_error(req, wbc_err);
540 state->wb_ctx->is_priv = true;
541 tevent_req_done(req);
544 static wbcErr wb_open_pipe_recv(struct tevent_req *req)
546 return tevent_req_simple_recv_wbcerr(req);
549 struct wb_trans_state {
550 struct wb_trans_state *prev, *next;
551 struct wb_context *wb_ctx;
552 struct tevent_context *ev;
553 struct winbindd_request *wb_req;
554 struct winbindd_response *wb_resp;
559 static void wb_trans_connect_done(struct tevent_req *subreq);
560 static void wb_trans_done(struct tevent_req *subreq);
561 static void wb_trans_retry_wait_done(struct tevent_req *subreq);
563 struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
564 struct tevent_context *ev,
565 struct wb_context *wb_ctx, bool need_priv,
566 struct winbindd_request *wb_req)
568 struct tevent_req *req, *subreq;
569 struct wb_trans_state *state;
571 req = tevent_req_create(mem_ctx, &state, struct wb_trans_state);
575 state->wb_ctx = wb_ctx;
577 state->wb_req = wb_req;
578 state->num_retries = 10;
579 state->need_priv = need_priv;
581 if ((wb_ctx->fd == -1) || (need_priv && !wb_ctx->is_priv)) {
582 subreq = wb_open_pipe_send(state, ev, wb_ctx, need_priv);
583 if (subreq == NULL) {
586 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
590 subreq = wb_int_trans_send(state, ev, wb_ctx->queue, wb_ctx->fd,
592 if (subreq == NULL) {
595 tevent_req_set_callback(subreq, wb_trans_done, req);
602 static bool wb_trans_retry(struct tevent_req *req,
603 struct wb_trans_state *state,
606 struct tevent_req *subreq;
608 if (WBC_ERROR_IS_OK(wbc_err)) {
612 if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
614 * Winbind not around or we can't connect to the pipe. Fail
617 tevent_req_error(req, wbc_err);
621 state->num_retries -= 1;
622 if (state->num_retries == 0) {
623 tevent_req_error(req, wbc_err);
628 * The transfer as such failed, retry after one second
631 if (state->wb_ctx->fd != -1) {
632 close(state->wb_ctx->fd);
633 state->wb_ctx->fd = -1;
636 subreq = tevent_wakeup_send(state, state->ev,
637 timeval_current_ofs(1, 0));
638 if (tevent_req_nomem(subreq, req)) {
641 tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req);
645 static void wb_trans_retry_wait_done(struct tevent_req *subreq)
647 struct tevent_req *req = tevent_req_callback_data(
648 subreq, struct tevent_req);
649 struct wb_trans_state *state = tevent_req_data(
650 req, struct wb_trans_state);
653 ret = tevent_wakeup_recv(subreq);
656 tevent_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
660 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
662 if (tevent_req_nomem(subreq, req)) {
665 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
668 static void wb_trans_connect_done(struct tevent_req *subreq)
670 struct tevent_req *req = tevent_req_callback_data(
671 subreq, struct tevent_req);
672 struct wb_trans_state *state = tevent_req_data(
673 req, struct wb_trans_state);
676 wbc_err = wb_open_pipe_recv(subreq);
679 if (wb_trans_retry(req, state, wbc_err)) {
683 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
685 if (tevent_req_nomem(subreq, req)) {
688 tevent_req_set_callback(subreq, wb_trans_done, req);
691 static void wb_trans_done(struct tevent_req *subreq)
693 struct tevent_req *req = tevent_req_callback_data(
694 subreq, struct tevent_req);
695 struct wb_trans_state *state = tevent_req_data(
696 req, struct wb_trans_state);
699 wbc_err = wb_int_trans_recv(subreq, state, &state->wb_resp);
702 if (wb_trans_retry(req, state, wbc_err)) {
706 tevent_req_done(req);
709 wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
710 struct winbindd_response **presponse)
712 struct wb_trans_state *state = tevent_req_data(
713 req, struct wb_trans_state);
716 if (tevent_req_is_wbcerr(req, &wbc_err)) {
720 *presponse = talloc_move(mem_ctx, &state->wb_resp);
721 return WBC_ERR_SUCCESS;