async libwbclient infrastructure
[Samba.git] / source3 / lib / wbclient.c
blobd58c934c0716a79abd5aee6cf2eec79780e9d134
1 /*
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/>.
20 #include "includes.h"
21 #include "winbindd/winbindd.h"
22 #include "winbindd/winbindd_proto.h"
24 static int make_nonstd_fd(int fd)
26 int i;
27 int sys_errno = 0;
28 int fds[3];
29 int num_fds = 0;
31 if (fd == -1) {
32 return -1;
34 while (fd < 3) {
35 fds[num_fds++] = fd;
36 fd = dup(fd);
37 if (fd == -1) {
38 sys_errno = errno;
39 break;
42 for (i=0; i<num_fds; i++) {
43 close(fds[i]);
45 if (fd == -1) {
46 errno = sys_errno;
48 return fd;
51 /****************************************************************************
52 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
53 else
54 if SYSV use O_NDELAY
55 if BSD use FNDELAY
56 Set close on exec also.
57 ****************************************************************************/
59 static int make_safe_fd(int fd)
61 int result, flags;
62 int new_fd = make_nonstd_fd(fd);
64 if (new_fd == -1) {
65 goto fail;
68 /* Socket should be nonblocking. */
70 #ifdef O_NONBLOCK
71 #define FLAG_TO_SET O_NONBLOCK
72 #else
73 #ifdef SYSV
74 #define FLAG_TO_SET O_NDELAY
75 #else /* BSD */
76 #define FLAG_TO_SET FNDELAY
77 #endif
78 #endif
80 if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
81 goto fail;
84 flags |= FLAG_TO_SET;
85 if (fcntl(new_fd, F_SETFL, flags) == -1) {
86 goto fail;
89 #undef FLAG_TO_SET
91 /* Socket should be closed on exec() */
92 #ifdef FD_CLOEXEC
93 result = flags = fcntl(new_fd, F_GETFD, 0);
94 if (flags >= 0) {
95 flags |= FD_CLOEXEC;
96 result = fcntl( new_fd, F_SETFD, flags );
98 if (result < 0) {
99 goto fail;
101 #endif
102 return new_fd;
104 fail:
105 if (new_fd != -1) {
106 int sys_errno = errno;
107 close(new_fd);
108 errno = sys_errno;
110 return -1;
113 static bool winbind_closed_fd(int fd)
115 struct timeval tv;
116 fd_set r_fds;
118 if (fd == -1) {
119 return true;
122 FD_ZERO(&r_fds);
123 FD_SET(fd, &r_fds);
124 ZERO_STRUCT(tv);
126 if ((select(fd+1, &r_fds, NULL, NULL, &tv) == -1)
127 || FD_ISSET(fd, &r_fds)) {
128 return true;
131 return false;
134 struct wb_context {
135 struct async_req_queue *queue;
136 int fd;
137 bool is_priv;
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) {
146 return NULL;
148 result->queue = async_req_queue_init(result);
149 if (result->queue == NULL) {
150 TALLOC_FREE(result);
151 return NULL;
153 result->fd = -1;
154 return result;
157 static struct async_req *wb_connect_send(TALLOC_CTX *mem_ctx,
158 struct event_context *ev,
159 struct wb_context *wb_ctx,
160 const char *dir)
162 struct async_req *req;
163 struct sockaddr_un sunaddr;
164 struct stat st;
165 char *path = NULL;
166 NTSTATUS status;
168 if (wb_ctx->fd != -1) {
169 close(wb_ctx->fd);
170 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;
177 goto post_status;
180 if (!S_ISDIR(st.st_mode) ||
181 (st.st_uid != 0 && st.st_uid != geteuid())) {
182 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
183 goto post_status;
186 /* Connect to socket */
188 path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
189 WINBINDD_SOCKET_NAME);
190 if (path == NULL) {
191 goto nomem;
194 sunaddr.sun_family = AF_UNIX;
195 strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
196 TALLOC_FREE(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;
206 goto post_status;
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);
212 goto post_status;
215 req = async_connect_send(mem_ctx, ev, wb_ctx->fd,
216 (struct sockaddr *)&sunaddr,
217 sizeof(sunaddr));
218 if (req == NULL) {
219 goto nomem;
221 if (!async_req_set_timeout(req, ev, timeval_set(30, 0))) {
222 TALLOC_FREE(req);
223 goto nomem;
226 return req;
228 nomem:
229 status = NT_STATUS_NO_MEMORY;
230 post_status:
231 req = async_req_new(mem_ctx);
232 if (req == NULL) {
233 return NULL;
235 if (async_post_status(req, ev, status)) {
236 return req;
238 TALLOC_FREE(req);
239 return NULL;
242 static NTSTATUS wb_connect_recv(struct async_req *req)
244 int dummy;
246 return async_connect_recv(req, &dummy);
249 static struct winbindd_request *winbindd_request_copy(
250 TALLOC_CTX *mem_ctx,
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) {
258 return NULL;
261 if (result->extra_len == 0) {
262 return result;
265 result->extra_data.data = (char *)TALLOC_MEMDUP(
266 result, result->extra_data.data, result->extra_len);
267 if (result->extra_data.data == NULL) {
268 TALLOC_FREE(result);
269 return NULL;
271 return result;
274 struct wb_int_trans_state {
275 struct event_context *ev;
276 int fd;
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 result = async_req_new(mem_ctx);
293 if (result == NULL) {
294 return NULL;
296 state = talloc(result, struct wb_int_trans_state);
297 if (state == NULL) {
298 goto fail;
300 result->private_data = state;
302 if (winbind_closed_fd(fd)) {
303 if (!async_post_status(result, ev,
304 NT_STATUS_PIPE_DISCONNECTED)) {
305 goto fail;
307 return result;
310 state->ev = ev;
311 state->fd = fd;
312 state->wb_req = wb_req;
314 state->wb_req->length = sizeof(struct winbindd_request);
315 state->wb_req->pid = getpid();
317 subreq = wb_req_write_send(state, state->ev, state->fd, state->wb_req);
318 if (subreq == NULL) {
319 goto fail;
321 subreq->async.fn = wb_int_trans_write_done;
322 subreq->async.priv = result;
324 return result;
326 fail:
327 TALLOC_FREE(result);
328 return NULL;
331 static void wb_int_trans_write_done(struct async_req *subreq)
333 struct async_req *req = talloc_get_type_abort(
334 subreq->async.priv, struct async_req);
335 struct wb_int_trans_state *state = talloc_get_type_abort(
336 req->private_data, struct wb_int_trans_state);
337 NTSTATUS status;
339 status = wb_req_write_recv(subreq);
340 TALLOC_FREE(subreq);
341 if (!NT_STATUS_IS_OK(status)) {
342 async_req_error(req, status);
343 return;
346 subreq = wb_resp_read_send(state, state->ev, state->fd);
347 if (subreq == NULL) {
348 async_req_error(req, NT_STATUS_NO_MEMORY);
350 subreq->async.fn = wb_int_trans_read_done;
351 subreq->async.priv = req;
354 static void wb_int_trans_read_done(struct async_req *subreq)
356 struct async_req *req = talloc_get_type_abort(
357 subreq->async.priv, struct async_req);
358 struct wb_int_trans_state *state = talloc_get_type_abort(
359 req->private_data, struct wb_int_trans_state);
360 NTSTATUS status;
362 status = wb_resp_read_recv(subreq, state, &state->wb_resp);
363 TALLOC_FREE(subreq);
364 if (!NT_STATUS_IS_OK(status)) {
365 async_req_error(req, status);
366 return;
369 async_req_done(req);
372 static NTSTATUS wb_int_trans_recv(struct async_req *req,
373 TALLOC_CTX *mem_ctx,
374 struct winbindd_response **presponse)
376 struct wb_int_trans_state *state = talloc_get_type_abort(
377 req->private_data, struct wb_int_trans_state);
378 NTSTATUS status;
380 if (async_req_is_error(req, &status)) {
381 return status;
384 *presponse = talloc_move(mem_ctx, &state->wb_resp);
385 return NT_STATUS_OK;
388 static const char *winbindd_socket_dir(void)
390 #ifdef SOCKET_WRAPPER
391 const char *env_dir;
393 env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
394 if (env_dir) {
395 return env_dir;
397 #endif
399 return WINBINDD_SOCKET_DIR;
402 struct wb_open_pipe_state {
403 struct wb_context *wb_ctx;
404 struct event_context *ev;
405 bool need_priv;
406 struct winbindd_request wb_req;
409 static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq);
410 static void wb_open_pipe_ping_done(struct async_req *subreq);
411 static void wb_open_pipe_getpriv_done(struct async_req *subreq);
412 static void wb_open_pipe_connect_priv_done(struct async_req *subreq);
414 static struct async_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
415 struct event_context *ev,
416 struct wb_context *wb_ctx,
417 bool need_priv)
419 struct async_req *result;
420 struct async_req *subreq;
421 struct wb_open_pipe_state *state;
423 result = async_req_new(mem_ctx);
424 if (result == NULL) {
425 return NULL;
427 state = talloc(result, struct wb_open_pipe_state);
428 if (state == NULL) {
429 goto fail;
431 result->private_data = state;
433 state->wb_ctx = wb_ctx;
434 state->ev = ev;
435 state->need_priv = need_priv;
437 if (wb_ctx->fd != -1) {
438 close(wb_ctx->fd);
439 wb_ctx->fd = -1;
442 subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
443 if (subreq == NULL) {
444 goto fail;
447 subreq->async.fn = wb_open_pipe_connect_nonpriv_done;
448 subreq->async.priv = result;
449 return result;
451 fail:
452 TALLOC_FREE(result);
453 return NULL;
456 static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq)
458 struct async_req *req = talloc_get_type_abort(
459 subreq->async.priv, struct async_req);
460 struct wb_open_pipe_state *state = talloc_get_type_abort(
461 req->private_data, struct wb_open_pipe_state);
462 NTSTATUS status;
464 status = wb_connect_recv(subreq);
465 TALLOC_FREE(subreq);
466 if (!NT_STATUS_IS_OK(status)) {
467 state->wb_ctx->is_priv = true;
468 async_req_error(req, status);
469 return;
472 ZERO_STRUCT(state->wb_req);
473 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
475 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
476 &state->wb_req);
477 if (async_req_nomem(subreq, req)) {
478 return;
481 subreq->async.fn = wb_open_pipe_ping_done;
482 subreq->async.priv = req;
485 static void wb_open_pipe_ping_done(struct async_req *subreq)
487 struct async_req *req = talloc_get_type_abort(
488 subreq->async.priv, struct async_req);
489 struct wb_open_pipe_state *state = talloc_get_type_abort(
490 req->private_data, struct wb_open_pipe_state);
491 struct winbindd_response *wb_resp;
492 NTSTATUS status;
494 status = wb_int_trans_recv(subreq, state, &wb_resp);
495 TALLOC_FREE(subreq);
496 if (!NT_STATUS_IS_OK(status)) {
497 async_req_error(req, status);
498 return;
501 if (!state->need_priv) {
502 async_req_done(req);
503 return;
506 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
508 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
509 &state->wb_req);
510 if (async_req_nomem(subreq, req)) {
511 return;
514 subreq->async.fn = wb_open_pipe_getpriv_done;
515 subreq->async.priv = req;
518 static void wb_open_pipe_getpriv_done(struct async_req *subreq)
520 struct async_req *req = talloc_get_type_abort(
521 subreq->async.priv, struct async_req);
522 struct wb_open_pipe_state *state = talloc_get_type_abort(
523 req->private_data, struct wb_open_pipe_state);
524 struct winbindd_response *wb_resp = NULL;
525 NTSTATUS status;
527 status = wb_int_trans_recv(subreq, state, &wb_resp);
528 TALLOC_FREE(subreq);
529 if (!NT_STATUS_IS_OK(status)) {
530 async_req_error(req, status);
531 return;
534 close(state->wb_ctx->fd);
535 state->wb_ctx->fd = -1;
537 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
538 (char *)wb_resp->extra_data.data);
539 TALLOC_FREE(wb_resp);
540 if (async_req_nomem(subreq, req)) {
541 return;
544 subreq->async.fn = wb_open_pipe_connect_priv_done;
545 subreq->async.priv = req;
548 static void wb_open_pipe_connect_priv_done(struct async_req *subreq)
550 struct async_req *req = talloc_get_type_abort(
551 subreq->async.priv, struct async_req);
552 struct wb_open_pipe_state *state = talloc_get_type_abort(
553 req->private_data, struct wb_open_pipe_state);
554 NTSTATUS status;
556 status = wb_connect_recv(subreq);
557 TALLOC_FREE(subreq);
558 if (!NT_STATUS_IS_OK(status)) {
559 async_req_error(req, status);
560 return;
562 state->wb_ctx->is_priv = true;
563 async_req_done(req);
566 static NTSTATUS wb_open_pipe_recv(struct async_req *req)
568 return async_req_simple_recv(req);
571 struct wb_trans_state {
572 struct wb_trans_state *prev, *next;
573 struct wb_context *wb_ctx;
574 struct event_context *ev;
575 struct winbindd_request *wb_req;
576 struct winbindd_response *wb_resp;
577 int num_retries;
578 bool need_priv;
581 static void wb_trans_connect_done(struct async_req *subreq);
582 static void wb_trans_done(struct async_req *subreq);
583 static void wb_trans_retry_wait_done(struct async_req *subreq);
585 static void wb_trigger_trans(struct async_req *req)
587 struct wb_trans_state *state = talloc_get_type_abort(
588 req->private_data, struct wb_trans_state);
589 struct async_req *subreq;
591 if ((state->wb_ctx->fd == -1)
592 || (state->need_priv && !state->wb_ctx->is_priv)) {
594 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
595 state->need_priv);
596 if (async_req_nomem(subreq, req)) {
597 return;
599 subreq->async.fn = wb_trans_connect_done;
600 subreq->async.priv = req;
601 return;
604 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
605 state->wb_req);
606 if (async_req_nomem(subreq, req)) {
607 return;
609 subreq->async.fn = wb_trans_done;
610 subreq->async.priv = req;
613 struct async_req *wb_trans_send(TALLOC_CTX *mem_ctx, struct event_context *ev,
614 struct wb_context *wb_ctx, bool need_priv,
615 const struct winbindd_request *wb_req)
617 struct async_req *result;
618 struct wb_trans_state *state;
620 result = async_req_new(mem_ctx);
621 if (result == NULL) {
622 return NULL;
624 state = talloc(result, struct wb_trans_state);
625 if (state == NULL) {
626 goto fail;
628 result->private_data = state;
630 state->wb_ctx = wb_ctx;
631 state->ev = ev;
632 state->wb_req = winbindd_request_copy(state, wb_req);
633 if (state->wb_req == NULL) {
634 goto fail;
636 state->num_retries = 10;
637 state->need_priv = need_priv;
639 if (!async_req_enqueue(wb_ctx->queue, ev, result, wb_trigger_trans)) {
640 goto fail;
642 return result;
644 fail:
645 TALLOC_FREE(result);
646 return NULL;
649 static bool wb_trans_retry(struct async_req *req,
650 struct wb_trans_state *state,
651 NTSTATUS status)
653 struct async_req *subreq;
655 if (NT_STATUS_IS_OK(status)) {
656 return false;
659 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)
660 || NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
662 * Winbind not around or we can't connect to the pipe. Fail
663 * immediately.
665 async_req_error(req, status);
666 return true;
669 state->num_retries -= 1;
670 if (state->num_retries == 0) {
671 async_req_error(req, status);
672 return true;
676 * The transfer as such failed, retry after one second
679 if (state->wb_ctx->fd != -1) {
680 close(state->wb_ctx->fd);
681 state->wb_ctx->fd = -1;
684 subreq = async_wait_send(state, state->ev, timeval_set(1, 0));
685 if (async_req_nomem(subreq, req)) {
686 return true;
689 subreq->async.fn = wb_trans_retry_wait_done;
690 subreq->async.priv = req;
691 return true;
694 static void wb_trans_retry_wait_done(struct async_req *subreq)
696 struct async_req *req = talloc_get_type_abort(
697 subreq->async.priv, struct async_req);
698 struct wb_trans_state *state = talloc_get_type_abort(
699 req->private_data, struct wb_trans_state);
700 NTSTATUS status;
702 status = async_wait_recv(subreq);
703 TALLOC_FREE(subreq);
704 if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
705 async_req_error(req, status);
706 return;
709 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
710 state->need_priv);
711 if (async_req_nomem(subreq, req)) {
712 return;
714 subreq->async.fn = wb_trans_connect_done;
715 subreq->async.priv = req;
718 static void wb_trans_connect_done(struct async_req *subreq)
720 struct async_req *req = talloc_get_type_abort(
721 subreq->async.priv, struct async_req);
722 struct wb_trans_state *state = talloc_get_type_abort(
723 req->private_data, struct wb_trans_state);
724 NTSTATUS status;
726 status = wb_open_pipe_recv(subreq);
727 TALLOC_FREE(subreq);
729 if (wb_trans_retry(req, state, status)) {
730 return;
733 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
734 state->wb_req);
735 if (async_req_nomem(subreq, req)) {
736 return;
739 subreq->async.fn = wb_trans_done;
740 subreq->async.priv = req;
743 static void wb_trans_done(struct async_req *subreq)
745 struct async_req *req = talloc_get_type_abort(
746 subreq->async.priv, struct async_req);
747 struct wb_trans_state *state = talloc_get_type_abort(
748 req->private_data, struct wb_trans_state);
749 NTSTATUS status;
751 status = wb_int_trans_recv(subreq, state, &state->wb_resp);
752 TALLOC_FREE(subreq);
754 if (wb_trans_retry(req, state, status)) {
755 return;
758 async_req_done(req);
761 NTSTATUS wb_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
762 struct winbindd_response **presponse)
764 struct wb_trans_state *state = talloc_get_type_abort(
765 req->private_data, struct wb_trans_state);
766 NTSTATUS status;
768 if (async_req_is_error(req, &status)) {
769 return status;
772 *presponse = talloc_move(mem_ctx, &state->wb_resp);
773 return NT_STATUS_OK;