Document default of the printing config variable.
[Samba/nascimento.git] / source3 / lib / wbclient.c
blobea0bcb512e936036114edbc72b420015b1f04d1d
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 if (!async_req_setup(mem_ctx, &result, &state,
293 struct wb_int_trans_state)) {
294 return NULL;
297 if (winbind_closed_fd(fd)) {
298 if (!async_post_status(result, ev,
299 NT_STATUS_PIPE_DISCONNECTED)) {
300 goto fail;
302 return result;
305 state->ev = ev;
306 state->fd = fd;
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) {
314 goto fail;
316 subreq->async.fn = wb_int_trans_write_done;
317 subreq->async.priv = result;
319 return result;
321 fail:
322 TALLOC_FREE(result);
323 return NULL;
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);
332 NTSTATUS status;
334 status = wb_req_write_recv(subreq);
335 TALLOC_FREE(subreq);
336 if (!NT_STATUS_IS_OK(status)) {
337 async_req_error(req, status);
338 return;
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);
355 NTSTATUS status;
357 status = wb_resp_read_recv(subreq, state, &state->wb_resp);
358 TALLOC_FREE(subreq);
359 if (!NT_STATUS_IS_OK(status)) {
360 async_req_error(req, status);
361 return;
364 async_req_done(req);
367 static NTSTATUS wb_int_trans_recv(struct async_req *req,
368 TALLOC_CTX *mem_ctx,
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);
373 NTSTATUS status;
375 if (async_req_is_error(req, &status)) {
376 return status;
379 *presponse = talloc_move(mem_ctx, &state->wb_resp);
380 return NT_STATUS_OK;
383 static const char *winbindd_socket_dir(void)
385 #ifdef SOCKET_WRAPPER
386 const char *env_dir;
388 env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
389 if (env_dir) {
390 return env_dir;
392 #endif
394 return WINBINDD_SOCKET_DIR;
397 struct wb_open_pipe_state {
398 struct wb_context *wb_ctx;
399 struct event_context *ev;
400 bool need_priv;
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,
412 bool need_priv)
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)) {
420 return NULL;
422 state->wb_ctx = wb_ctx;
423 state->ev = ev;
424 state->need_priv = need_priv;
426 if (wb_ctx->fd != -1) {
427 close(wb_ctx->fd);
428 wb_ctx->fd = -1;
431 subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
432 if (subreq == NULL) {
433 goto fail;
436 subreq->async.fn = wb_open_pipe_connect_nonpriv_done;
437 subreq->async.priv = result;
438 return result;
440 fail:
441 TALLOC_FREE(result);
442 return NULL;
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);
451 NTSTATUS status;
453 status = wb_connect_recv(subreq);
454 TALLOC_FREE(subreq);
455 if (!NT_STATUS_IS_OK(status)) {
456 state->wb_ctx->is_priv = true;
457 async_req_error(req, status);
458 return;
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,
465 &state->wb_req);
466 if (async_req_nomem(subreq, req)) {
467 return;
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;
481 NTSTATUS status;
483 status = wb_int_trans_recv(subreq, state, &wb_resp);
484 TALLOC_FREE(subreq);
485 if (!NT_STATUS_IS_OK(status)) {
486 async_req_error(req, status);
487 return;
490 if (!state->need_priv) {
491 async_req_done(req);
492 return;
495 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
497 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
498 &state->wb_req);
499 if (async_req_nomem(subreq, req)) {
500 return;
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;
514 NTSTATUS status;
516 status = wb_int_trans_recv(subreq, state, &wb_resp);
517 TALLOC_FREE(subreq);
518 if (!NT_STATUS_IS_OK(status)) {
519 async_req_error(req, status);
520 return;
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)) {
530 return;
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);
543 NTSTATUS status;
545 status = wb_connect_recv(subreq);
546 TALLOC_FREE(subreq);
547 if (!NT_STATUS_IS_OK(status)) {
548 async_req_error(req, status);
549 return;
551 state->wb_ctx->is_priv = true;
552 async_req_done(req);
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;
566 int num_retries;
567 bool need_priv;
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,
584 state->need_priv);
585 if (async_req_nomem(subreq, req)) {
586 return;
588 subreq->async.fn = wb_trans_connect_done;
589 subreq->async.priv = req;
590 return;
593 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
594 state->wb_req);
595 if (async_req_nomem(subreq, req)) {
596 return;
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)) {
611 return NULL;
613 state->wb_ctx = wb_ctx;
614 state->ev = ev;
615 state->wb_req = winbindd_request_copy(state, wb_req);
616 if (state->wb_req == NULL) {
617 goto fail;
619 state->num_retries = 10;
620 state->need_priv = need_priv;
622 if (!async_req_enqueue(wb_ctx->queue, ev, result, wb_trigger_trans)) {
623 goto fail;
625 return result;
627 fail:
628 TALLOC_FREE(result);
629 return NULL;
632 static bool wb_trans_retry(struct async_req *req,
633 struct wb_trans_state *state,
634 NTSTATUS status)
636 struct async_req *subreq;
638 if (NT_STATUS_IS_OK(status)) {
639 return false;
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
646 * immediately.
648 async_req_error(req, status);
649 return true;
652 state->num_retries -= 1;
653 if (state->num_retries == 0) {
654 async_req_error(req, status);
655 return true;
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)) {
669 return true;
672 subreq->async.fn = wb_trans_retry_wait_done;
673 subreq->async.priv = req;
674 return true;
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);
683 NTSTATUS status;
685 status = async_wait_recv(subreq);
686 TALLOC_FREE(subreq);
687 if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
688 async_req_error(req, status);
689 return;
692 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
693 state->need_priv);
694 if (async_req_nomem(subreq, req)) {
695 return;
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);
707 NTSTATUS status;
709 status = wb_open_pipe_recv(subreq);
710 TALLOC_FREE(subreq);
712 if (wb_trans_retry(req, state, status)) {
713 return;
716 subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
717 state->wb_req);
718 if (async_req_nomem(subreq, req)) {
719 return;
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);
732 NTSTATUS status;
734 status = wb_int_trans_recv(subreq, state, &state->wb_resp);
735 TALLOC_FREE(subreq);
737 if (wb_trans_retry(req, state, status)) {
738 return;
741 async_req_done(req);
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);
749 NTSTATUS status;
751 if (async_req_is_error(req, &status)) {
752 return status;
755 *presponse = talloc_move(mem_ctx, &state->wb_resp);
756 return NT_STATUS_OK;