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 "winbindd/winbindd.h"
22 #include "winbindd/winbindd_proto.h"
24 static int make_nonstd_fd(int fd)
42 for (i=0; i<num_fds; i++) {
51 /****************************************************************************
52 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
56 Set close on exec also.
57 ****************************************************************************/
59 static int make_safe_fd(int fd)
62 int new_fd = make_nonstd_fd(fd);
68 /* Socket should be nonblocking. */
71 #define FLAG_TO_SET O_NONBLOCK
74 #define FLAG_TO_SET O_NDELAY
76 #define FLAG_TO_SET FNDELAY
80 if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
85 if (fcntl(new_fd, F_SETFL, flags) == -1) {
91 /* Socket should be closed on exec() */
93 result = flags = fcntl(new_fd, F_GETFD, 0);
96 result = fcntl( new_fd, F_SETFD, flags );
106 int sys_errno = errno;
113 static bool winbind_closed_fd(int fd)
126 if ((select(fd+1, &r_fds, NULL, NULL, &tv) == -1)
127 || FD_ISSET(fd, &r_fds)) {
135 struct async_req_queue *queue;
140 struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx)
142 struct wb_context *result;
144 result = talloc(mem_ctx, struct wb_context);
145 if (result == NULL) {
148 result->queue = async_req_queue_init(result);
149 if (result->queue == NULL) {
157 static struct async_req *wb_connect_send(TALLOC_CTX *mem_ctx,
158 struct event_context *ev,
159 struct wb_context *wb_ctx,
162 struct async_req *req;
163 struct sockaddr_un sunaddr;
168 if (wb_ctx->fd != -1) {
173 /* Check permissions on unix socket directory */
175 if (lstat(dir, &st) == -1) {
176 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
180 if (!S_ISDIR(st.st_mode) ||
181 (st.st_uid != 0 && st.st_uid != geteuid())) {
182 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
186 /* Connect to socket */
188 path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
189 WINBINDD_SOCKET_NAME);
194 sunaddr.sun_family = AF_UNIX;
195 strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
198 /* If socket file doesn't exist, don't bother trying to connect
199 with retry. This is an attempt to make the system usable when
200 the winbindd daemon is not running. */
202 if ((lstat(sunaddr.sun_path, &st) == -1)
203 || !S_ISSOCK(st.st_mode)
204 || (st.st_uid != 0 && st.st_uid != geteuid())) {
205 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
209 wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
210 if (wb_ctx->fd == -1) {
211 status = map_nt_error_from_unix(errno);
215 req = async_connect_send(mem_ctx, ev, wb_ctx->fd,
216 (struct sockaddr *)&sunaddr,
221 if (!async_req_set_timeout(req, ev, timeval_set(30, 0))) {
229 status = NT_STATUS_NO_MEMORY;
231 req = async_req_new(mem_ctx);
235 if (async_post_status(req, ev, status)) {
242 static NTSTATUS wb_connect_recv(struct async_req *req)
246 return async_connect_recv(req, &dummy);
249 static struct winbindd_request *winbindd_request_copy(
251 const struct winbindd_request *req)
253 struct winbindd_request *result;
255 result = (struct winbindd_request *)TALLOC_MEMDUP(
256 mem_ctx, req, sizeof(struct winbindd_request));
257 if (result == NULL) {
261 if (result->extra_len == 0) {
265 result->extra_data.data = (char *)TALLOC_MEMDUP(
266 result, result->extra_data.data, result->extra_len);
267 if (result->extra_data.data == NULL) {
274 struct wb_int_trans_state {
275 struct event_context *ev;
277 struct winbindd_request *wb_req;
278 struct winbindd_response *wb_resp;
281 static void wb_int_trans_write_done(struct async_req *subreq);
282 static void wb_int_trans_read_done(struct async_req *subreq);
284 static struct async_req *wb_int_trans_send(TALLOC_CTX *mem_ctx,
285 struct event_context *ev, int fd,
286 struct winbindd_request *wb_req)
288 struct async_req *result;
289 struct async_req *subreq;
290 struct wb_int_trans_state *state;
292 if (!async_req_setup(mem_ctx, &result, &state,
293 struct wb_int_trans_state)) {
297 if (winbind_closed_fd(fd)) {
298 if (!async_post_status(result, ev,
299 NT_STATUS_PIPE_DISCONNECTED)) {
307 state->wb_req = wb_req;
309 state->wb_req->length = sizeof(struct winbindd_request);
310 state->wb_req->pid = getpid();
312 subreq = wb_req_write_send(state, state->ev, state->fd, state->wb_req);
313 if (subreq == NULL) {
316 subreq->async.fn = wb_int_trans_write_done;
317 subreq->async.priv = result;
326 static void wb_int_trans_write_done(struct async_req *subreq)
328 struct async_req *req = talloc_get_type_abort(
329 subreq->async.priv, struct async_req);
330 struct wb_int_trans_state *state = talloc_get_type_abort(
331 req->private_data, struct wb_int_trans_state);
334 status = wb_req_write_recv(subreq);
336 if (!NT_STATUS_IS_OK(status)) {
337 async_req_error(req, status);
341 subreq = wb_resp_read_send(state, state->ev, state->fd);
342 if (subreq == NULL) {
343 async_req_error(req, NT_STATUS_NO_MEMORY);
345 subreq->async.fn = wb_int_trans_read_done;
346 subreq->async.priv = req;
349 static void wb_int_trans_read_done(struct async_req *subreq)
351 struct async_req *req = talloc_get_type_abort(
352 subreq->async.priv, struct async_req);
353 struct wb_int_trans_state *state = talloc_get_type_abort(
354 req->private_data, struct wb_int_trans_state);
357 status = wb_resp_read_recv(subreq, state, &state->wb_resp);
359 if (!NT_STATUS_IS_OK(status)) {
360 async_req_error(req, status);
367 static NTSTATUS wb_int_trans_recv(struct async_req *req,
369 struct winbindd_response **presponse)
371 struct wb_int_trans_state *state = talloc_get_type_abort(
372 req->private_data, struct wb_int_trans_state);
375 if (async_req_is_error(req, &status)) {
379 *presponse = talloc_move(mem_ctx, &state->wb_resp);
383 static const char *winbindd_socket_dir(void)
385 #ifdef SOCKET_WRAPPER
388 env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
394 return WINBINDD_SOCKET_DIR;
397 struct wb_open_pipe_state {
398 struct wb_context *wb_ctx;
399 struct event_context *ev;
401 struct winbindd_request wb_req;
404 static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq);
405 static void wb_open_pipe_ping_done(struct async_req *subreq);
406 static void wb_open_pipe_getpriv_done(struct async_req *subreq);
407 static void wb_open_pipe_connect_priv_done(struct async_req *subreq);
409 static struct async_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
410 struct event_context *ev,
411 struct wb_context *wb_ctx,
414 struct async_req *result;
415 struct async_req *subreq;
416 struct wb_open_pipe_state *state;
418 if (!async_req_setup(mem_ctx, &result, &state,
419 struct wb_open_pipe_state)) {
422 state->wb_ctx = wb_ctx;
424 state->need_priv = need_priv;
426 if (wb_ctx->fd != -1) {
431 subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
432 if (subreq == NULL) {
436 subreq->async.fn = wb_open_pipe_connect_nonpriv_done;
437 subreq->async.priv = result;
445 static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq)
447 struct async_req *req = talloc_get_type_abort(
448 subreq->async.priv, struct async_req);
449 struct wb_open_pipe_state *state = talloc_get_type_abort(
450 req->private_data, struct wb_open_pipe_state);
453 status = wb_connect_recv(subreq);
455 if (!NT_STATUS_IS_OK(status)) {
456 state->wb_ctx->is_priv = true;
457 async_req_error(req, status);
461 ZERO_STRUCT(state->wb_req);
462 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
464 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
466 if (async_req_nomem(subreq, req)) {
470 subreq->async.fn = wb_open_pipe_ping_done;
471 subreq->async.priv = req;
474 static void wb_open_pipe_ping_done(struct async_req *subreq)
476 struct async_req *req = talloc_get_type_abort(
477 subreq->async.priv, struct async_req);
478 struct wb_open_pipe_state *state = talloc_get_type_abort(
479 req->private_data, struct wb_open_pipe_state);
480 struct winbindd_response *wb_resp;
483 status = wb_int_trans_recv(subreq, state, &wb_resp);
485 if (!NT_STATUS_IS_OK(status)) {
486 async_req_error(req, status);
490 if (!state->need_priv) {
495 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
497 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
499 if (async_req_nomem(subreq, req)) {
503 subreq->async.fn = wb_open_pipe_getpriv_done;
504 subreq->async.priv = req;
507 static void wb_open_pipe_getpriv_done(struct async_req *subreq)
509 struct async_req *req = talloc_get_type_abort(
510 subreq->async.priv, struct async_req);
511 struct wb_open_pipe_state *state = talloc_get_type_abort(
512 req->private_data, struct wb_open_pipe_state);
513 struct winbindd_response *wb_resp = NULL;
516 status = wb_int_trans_recv(subreq, state, &wb_resp);
518 if (!NT_STATUS_IS_OK(status)) {
519 async_req_error(req, status);
523 close(state->wb_ctx->fd);
524 state->wb_ctx->fd = -1;
526 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
527 (char *)wb_resp->extra_data.data);
528 TALLOC_FREE(wb_resp);
529 if (async_req_nomem(subreq, req)) {
533 subreq->async.fn = wb_open_pipe_connect_priv_done;
534 subreq->async.priv = req;
537 static void wb_open_pipe_connect_priv_done(struct async_req *subreq)
539 struct async_req *req = talloc_get_type_abort(
540 subreq->async.priv, struct async_req);
541 struct wb_open_pipe_state *state = talloc_get_type_abort(
542 req->private_data, struct wb_open_pipe_state);
545 status = wb_connect_recv(subreq);
547 if (!NT_STATUS_IS_OK(status)) {
548 async_req_error(req, status);
551 state->wb_ctx->is_priv = true;
555 static NTSTATUS wb_open_pipe_recv(struct async_req *req)
557 return async_req_simple_recv(req);
560 struct wb_trans_state {
561 struct wb_trans_state *prev, *next;
562 struct wb_context *wb_ctx;
563 struct event_context *ev;
564 struct winbindd_request *wb_req;
565 struct winbindd_response *wb_resp;
570 static void wb_trans_connect_done(struct async_req *subreq);
571 static void wb_trans_done(struct async_req *subreq);
572 static void wb_trans_retry_wait_done(struct async_req *subreq);
574 static void wb_trigger_trans(struct async_req *req)
576 struct wb_trans_state *state = talloc_get_type_abort(
577 req->private_data, struct wb_trans_state);
578 struct async_req *subreq;
580 if ((state->wb_ctx->fd == -1)
581 || (state->need_priv && !state->wb_ctx->is_priv)) {
583 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
585 if (async_req_nomem(subreq, req)) {
588 subreq->async.fn = wb_trans_connect_done;
589 subreq->async.priv = req;
593 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
595 if (async_req_nomem(subreq, req)) {
598 subreq->async.fn = wb_trans_done;
599 subreq->async.priv = req;
602 struct async_req *wb_trans_send(TALLOC_CTX *mem_ctx, struct event_context *ev,
603 struct wb_context *wb_ctx, bool need_priv,
604 const struct winbindd_request *wb_req)
606 struct async_req *result;
607 struct wb_trans_state *state;
609 if (!async_req_setup(mem_ctx, &result, &state,
610 struct wb_trans_state)) {
613 state->wb_ctx = wb_ctx;
615 state->wb_req = winbindd_request_copy(state, wb_req);
616 if (state->wb_req == NULL) {
619 state->num_retries = 10;
620 state->need_priv = need_priv;
622 if (!async_req_enqueue(wb_ctx->queue, ev, result, wb_trigger_trans)) {
632 static bool wb_trans_retry(struct async_req *req,
633 struct wb_trans_state *state,
636 struct async_req *subreq;
638 if (NT_STATUS_IS_OK(status)) {
642 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)
643 || NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
645 * Winbind not around or we can't connect to the pipe. Fail
648 async_req_error(req, status);
652 state->num_retries -= 1;
653 if (state->num_retries == 0) {
654 async_req_error(req, status);
659 * The transfer as such failed, retry after one second
662 if (state->wb_ctx->fd != -1) {
663 close(state->wb_ctx->fd);
664 state->wb_ctx->fd = -1;
667 subreq = async_wait_send(state, state->ev, timeval_set(1, 0));
668 if (async_req_nomem(subreq, req)) {
672 subreq->async.fn = wb_trans_retry_wait_done;
673 subreq->async.priv = req;
677 static void wb_trans_retry_wait_done(struct async_req *subreq)
679 struct async_req *req = talloc_get_type_abort(
680 subreq->async.priv, struct async_req);
681 struct wb_trans_state *state = talloc_get_type_abort(
682 req->private_data, struct wb_trans_state);
685 status = async_wait_recv(subreq);
687 if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
688 async_req_error(req, status);
692 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
694 if (async_req_nomem(subreq, req)) {
697 subreq->async.fn = wb_trans_connect_done;
698 subreq->async.priv = req;
701 static void wb_trans_connect_done(struct async_req *subreq)
703 struct async_req *req = talloc_get_type_abort(
704 subreq->async.priv, struct async_req);
705 struct wb_trans_state *state = talloc_get_type_abort(
706 req->private_data, struct wb_trans_state);
709 status = wb_open_pipe_recv(subreq);
712 if (wb_trans_retry(req, state, status)) {
716 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
718 if (async_req_nomem(subreq, req)) {
722 subreq->async.fn = wb_trans_done;
723 subreq->async.priv = req;
726 static void wb_trans_done(struct async_req *subreq)
728 struct async_req *req = talloc_get_type_abort(
729 subreq->async.priv, struct async_req);
730 struct wb_trans_state *state = talloc_get_type_abort(
731 req->private_data, struct wb_trans_state);
734 status = wb_int_trans_recv(subreq, state, &state->wb_resp);
737 if (wb_trans_retry(req, state, status)) {
744 NTSTATUS wb_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
745 struct winbindd_response **presponse)
747 struct wb_trans_state *state = talloc_get_type_abort(
748 req->private_data, struct wb_trans_state);
751 if (async_req_is_error(req, &status)) {
755 *presponse = talloc_move(mem_ctx, &state->wb_resp);