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 tevent_req_set_callback(subreq, wbc_connect_connected, result);
229 if (!tevent_req_set_endtime(subreq, ev, timeval_current_ofs(30, 0))) {
236 wbc_err = WBC_ERR_NO_MEMORY;
238 if (async_post_error(result, ev, wbc_err)) {
245 static void wbc_connect_connected(struct tevent_req *subreq)
247 struct async_req *req =
248 tevent_req_callback_data(subreq, struct async_req);
251 res = async_connect_recv(subreq, &err);
254 async_req_error(req, map_wbc_err_from_errno(err));
260 static wbcErr wb_connect_recv(struct async_req *req)
262 return async_req_simple_recv_wbcerr(req);
265 static struct winbindd_request *winbindd_request_copy(
267 const struct winbindd_request *req)
269 struct winbindd_request *result;
271 result = (struct winbindd_request *)TALLOC_MEMDUP(
272 mem_ctx, req, sizeof(struct winbindd_request));
273 if (result == NULL) {
277 if (result->extra_len == 0) {
281 result->extra_data.data = (char *)TALLOC_MEMDUP(
282 result, result->extra_data.data, result->extra_len);
283 if (result->extra_data.data == NULL) {
290 struct wb_int_trans_state {
291 struct tevent_context *ev;
293 struct winbindd_request *wb_req;
294 struct winbindd_response *wb_resp;
297 static void wb_int_trans_write_done(struct tevent_req *subreq);
298 static void wb_int_trans_read_done(struct tevent_req *subreq);
300 static struct async_req *wb_int_trans_send(TALLOC_CTX *mem_ctx,
301 struct tevent_context *ev,
302 struct tevent_queue *queue, int fd,
303 struct winbindd_request *wb_req)
305 struct async_req *result;
306 struct tevent_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, queue, state->fd,
331 if (subreq == NULL) {
334 tevent_req_set_callback(subreq, wb_int_trans_write_done, result);
343 static void wb_int_trans_write_done(struct tevent_req *subreq)
345 struct async_req *req = tevent_req_callback_data(
346 subreq, 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 (async_req_nomem(subreq, req)) {
362 tevent_req_set_callback(subreq, wb_int_trans_read_done, req);
365 static void wb_int_trans_read_done(struct tevent_req *subreq)
367 struct async_req *req = tevent_req_callback_data(
368 subreq, struct async_req);
369 struct wb_int_trans_state *state = talloc_get_type_abort(
370 req->private_data, struct wb_int_trans_state);
373 wbc_err = wb_resp_read_recv(subreq, state, &state->wb_resp);
375 if (!WBC_ERROR_IS_OK(wbc_err)) {
376 async_req_error(req, wbc_err);
383 static wbcErr wb_int_trans_recv(struct async_req *req,
385 struct winbindd_response **presponse)
387 struct wb_int_trans_state *state = talloc_get_type_abort(
388 req->private_data, struct wb_int_trans_state);
391 if (async_req_is_wbcerr(req, &wbc_err)) {
395 *presponse = talloc_move(mem_ctx, &state->wb_resp);
396 return WBC_ERR_SUCCESS;
399 static const char *winbindd_socket_dir(void)
401 #ifdef SOCKET_WRAPPER
404 env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
410 return WINBINDD_SOCKET_DIR;
413 struct wb_open_pipe_state {
414 struct wb_context *wb_ctx;
415 struct tevent_context *ev;
417 struct winbindd_request wb_req;
420 static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq);
421 static void wb_open_pipe_ping_done(struct async_req *subreq);
422 static void wb_open_pipe_getpriv_done(struct async_req *subreq);
423 static void wb_open_pipe_connect_priv_done(struct async_req *subreq);
425 static struct async_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
426 struct tevent_context *ev,
427 struct wb_context *wb_ctx,
430 struct async_req *result;
431 struct async_req *subreq;
432 struct wb_open_pipe_state *state;
434 if (!async_req_setup(mem_ctx, &result, &state,
435 struct wb_open_pipe_state)) {
438 state->wb_ctx = wb_ctx;
440 state->need_priv = need_priv;
442 if (wb_ctx->fd != -1) {
447 subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
448 if (subreq == NULL) {
452 subreq->async.fn = wb_open_pipe_connect_nonpriv_done;
453 subreq->async.priv = result;
461 static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq)
463 struct async_req *req = talloc_get_type_abort(
464 subreq->async.priv, struct async_req);
465 struct wb_open_pipe_state *state = talloc_get_type_abort(
466 req->private_data, struct wb_open_pipe_state);
469 wbc_err = wb_connect_recv(subreq);
471 if (!WBC_ERROR_IS_OK(wbc_err)) {
472 state->wb_ctx->is_priv = true;
473 async_req_error(req, wbc_err);
477 ZERO_STRUCT(state->wb_req);
478 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
480 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
482 if (async_req_nomem(subreq, req)) {
486 subreq->async.fn = wb_open_pipe_ping_done;
487 subreq->async.priv = req;
490 static void wb_open_pipe_ping_done(struct async_req *subreq)
492 struct async_req *req = talloc_get_type_abort(
493 subreq->async.priv, struct async_req);
494 struct wb_open_pipe_state *state = talloc_get_type_abort(
495 req->private_data, struct wb_open_pipe_state);
496 struct winbindd_response *wb_resp;
499 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
501 if (!WBC_ERROR_IS_OK(wbc_err)) {
502 async_req_error(req, wbc_err);
506 if (!state->need_priv) {
511 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
513 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
515 if (async_req_nomem(subreq, req)) {
519 subreq->async.fn = wb_open_pipe_getpriv_done;
520 subreq->async.priv = req;
523 static void wb_open_pipe_getpriv_done(struct async_req *subreq)
525 struct async_req *req = talloc_get_type_abort(
526 subreq->async.priv, struct async_req);
527 struct wb_open_pipe_state *state = talloc_get_type_abort(
528 req->private_data, struct wb_open_pipe_state);
529 struct winbindd_response *wb_resp = NULL;
532 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
534 if (!WBC_ERROR_IS_OK(wbc_err)) {
535 async_req_error(req, wbc_err);
539 close(state->wb_ctx->fd);
540 state->wb_ctx->fd = -1;
542 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
543 (char *)wb_resp->extra_data.data);
544 TALLOC_FREE(wb_resp);
545 if (async_req_nomem(subreq, req)) {
549 subreq->async.fn = wb_open_pipe_connect_priv_done;
550 subreq->async.priv = req;
553 static void wb_open_pipe_connect_priv_done(struct async_req *subreq)
555 struct async_req *req = talloc_get_type_abort(
556 subreq->async.priv, struct async_req);
557 struct wb_open_pipe_state *state = talloc_get_type_abort(
558 req->private_data, struct wb_open_pipe_state);
561 wbc_err = wb_connect_recv(subreq);
563 if (!WBC_ERROR_IS_OK(wbc_err)) {
564 async_req_error(req, wbc_err);
567 state->wb_ctx->is_priv = true;
571 static wbcErr wb_open_pipe_recv(struct async_req *req)
573 return async_req_simple_recv_wbcerr(req);
576 struct wb_trans_state {
577 struct wb_trans_state *prev, *next;
578 struct wb_context *wb_ctx;
579 struct tevent_context *ev;
580 struct winbindd_request *wb_req;
581 struct winbindd_response *wb_resp;
586 static void wb_trans_connect_done(struct async_req *subreq);
587 static void wb_trans_done(struct async_req *subreq);
588 static void wb_trans_retry_wait_done(struct async_req *subreq);
590 static void wb_trigger_trans(struct async_req *req)
592 struct wb_trans_state *state = talloc_get_type_abort(
593 req->private_data, struct wb_trans_state);
594 struct async_req *subreq;
596 if ((state->wb_ctx->fd == -1)
597 || (state->need_priv && !state->wb_ctx->is_priv)) {
599 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
601 if (async_req_nomem(subreq, req)) {
604 subreq->async.fn = wb_trans_connect_done;
605 subreq->async.priv = req;
609 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
611 if (async_req_nomem(subreq, req)) {
614 subreq->async.fn = wb_trans_done;
615 subreq->async.priv = req;
618 struct async_req *wb_trans_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
619 struct wb_context *wb_ctx, bool need_priv,
620 const struct winbindd_request *wb_req)
622 struct async_req *result;
623 struct wb_trans_state *state;
625 if (!async_req_setup(mem_ctx, &result, &state,
626 struct wb_trans_state)) {
629 state->wb_ctx = wb_ctx;
631 state->wb_req = winbindd_request_copy(state, wb_req);
632 if (state->wb_req == NULL) {
635 state->num_retries = 10;
636 state->need_priv = need_priv;
638 if (!async_req_enqueue(wb_ctx->queue, ev, result, wb_trigger_trans)) {
648 static bool wb_trans_retry(struct async_req *req,
649 struct wb_trans_state *state,
652 struct async_req *subreq;
654 if (WBC_ERROR_IS_OK(wbc_err)) {
658 if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
660 * Winbind not around or we can't connect to the pipe. Fail
663 async_req_error(req, wbc_err);
667 state->num_retries -= 1;
668 if (state->num_retries == 0) {
669 async_req_error(req, wbc_err);
674 * The transfer as such failed, retry after one second
677 if (state->wb_ctx->fd != -1) {
678 close(state->wb_ctx->fd);
679 state->wb_ctx->fd = -1;
682 subreq = async_wait_send(state, state->ev, timeval_set(1, 0));
683 if (async_req_nomem(subreq, req)) {
687 subreq->async.fn = wb_trans_retry_wait_done;
688 subreq->async.priv = req;
692 static void wb_trans_retry_wait_done(struct async_req *subreq)
694 struct async_req *req = talloc_get_type_abort(
695 subreq->async.priv, struct async_req);
696 struct wb_trans_state *state = talloc_get_type_abort(
697 req->private_data, struct wb_trans_state);
700 ret = async_wait_recv(subreq);
703 async_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
707 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
709 if (async_req_nomem(subreq, req)) {
712 subreq->async.fn = wb_trans_connect_done;
713 subreq->async.priv = req;
716 static void wb_trans_connect_done(struct async_req *subreq)
718 struct async_req *req = talloc_get_type_abort(
719 subreq->async.priv, struct async_req);
720 struct wb_trans_state *state = talloc_get_type_abort(
721 req->private_data, struct wb_trans_state);
724 wbc_err = wb_open_pipe_recv(subreq);
727 if (wb_trans_retry(req, state, wbc_err)) {
731 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
733 if (async_req_nomem(subreq, req)) {
737 subreq->async.fn = wb_trans_done;
738 subreq->async.priv = req;
741 static void wb_trans_done(struct async_req *subreq)
743 struct async_req *req = talloc_get_type_abort(
744 subreq->async.priv, struct async_req);
745 struct wb_trans_state *state = talloc_get_type_abort(
746 req->private_data, struct wb_trans_state);
749 wbc_err = wb_int_trans_recv(subreq, state, &state->wb_resp);
752 if (wb_trans_retry(req, state, wbc_err)) {
759 wbcErr wb_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
760 struct winbindd_response **presponse)
762 struct wb_trans_state *state = talloc_get_type_abort(
763 req->private_data, struct wb_trans_state);
766 if (async_req_is_wbcerr(req, &wbc_err)) {
770 *presponse = talloc_move(mem_ctx, &state->wb_resp);
771 return WBC_ERR_SUCCESS;