s3-krb5: Fix Coverity #722 (RESOURCE_LEAK).
[Samba/gbeck.git] / source3 / lib / wbclient.c
blob3cf992c7de9a8ec95208bcbbf5b84086c679b359
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 "wbc_async.h"
23 struct wb_context {
24 struct tevent_queue *queue;
25 int fd;
26 bool is_priv;
29 static int make_nonstd_fd(int fd)
31 int i;
32 int sys_errno = 0;
33 int fds[3];
34 int num_fds = 0;
36 if (fd == -1) {
37 return -1;
39 while (fd < 3) {
40 fds[num_fds++] = fd;
41 fd = dup(fd);
42 if (fd == -1) {
43 sys_errno = errno;
44 break;
47 for (i=0; i<num_fds; i++) {
48 close(fds[i]);
50 if (fd == -1) {
51 errno = sys_errno;
53 return fd;
56 /****************************************************************************
57 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
58 else
59 if SYSV use O_NDELAY
60 if BSD use FNDELAY
61 Set close on exec also.
62 ****************************************************************************/
64 static int make_safe_fd(int fd)
66 int result, flags;
67 int new_fd = make_nonstd_fd(fd);
69 if (new_fd == -1) {
70 goto fail;
73 /* Socket should be nonblocking. */
75 #ifdef O_NONBLOCK
76 #define FLAG_TO_SET O_NONBLOCK
77 #else
78 #ifdef SYSV
79 #define FLAG_TO_SET O_NDELAY
80 #else /* BSD */
81 #define FLAG_TO_SET FNDELAY
82 #endif
83 #endif
85 if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
86 goto fail;
89 flags |= FLAG_TO_SET;
90 if (fcntl(new_fd, F_SETFL, flags) == -1) {
91 goto fail;
94 #undef FLAG_TO_SET
96 /* Socket should be closed on exec() */
97 #ifdef FD_CLOEXEC
98 result = flags = fcntl(new_fd, F_GETFD, 0);
99 if (flags >= 0) {
100 flags |= FD_CLOEXEC;
101 result = fcntl( new_fd, F_SETFD, flags );
103 if (result < 0) {
104 goto fail;
106 #endif
107 return new_fd;
109 fail:
110 if (new_fd != -1) {
111 int sys_errno = errno;
112 close(new_fd);
113 errno = sys_errno;
115 return -1;
118 static bool winbind_closed_fd(int fd)
120 struct timeval tv;
121 fd_set r_fds;
123 if (fd == -1) {
124 return true;
127 FD_ZERO(&r_fds);
128 FD_SET(fd, &r_fds);
129 ZERO_STRUCT(tv);
131 if ((select(fd+1, &r_fds, NULL, NULL, &tv) == -1)
132 || FD_ISSET(fd, &r_fds)) {
133 return true;
136 return false;
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) {
145 return NULL;
147 result->queue = tevent_queue_create(result, "wb_trans");
148 if (result->queue == NULL) {
149 TALLOC_FREE(result);
150 return NULL;
152 result->fd = -1;
153 return result;
156 struct wb_connect_state {
157 int dummy;
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,
165 const char *dir)
167 struct tevent_req *result, *subreq;
168 struct wb_connect_state *state;
169 struct sockaddr_un sunaddr;
170 struct stat st;
171 char *path = NULL;
172 wbcErr wbc_err;
174 result = tevent_req_create(mem_ctx, &state, struct wb_connect_state);
175 if (result == NULL) {
176 return NULL;
179 if (wb_ctx->fd != -1) {
180 close(wb_ctx->fd);
181 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;
188 goto post_status;
191 if (!S_ISDIR(st.st_mode) ||
192 (st.st_uid != 0 && st.st_uid != geteuid())) {
193 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
194 goto post_status;
197 /* Connect to socket */
199 path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
200 WINBINDD_SOCKET_NAME);
201 if (path == NULL) {
202 goto nomem;
205 sunaddr.sun_family = AF_UNIX;
206 strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
207 TALLOC_FREE(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;
217 goto post_status;
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);
223 goto post_status;
226 subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
227 (struct sockaddr *)&sunaddr,
228 sizeof(sunaddr));
229 if (subreq == NULL) {
230 goto nomem;
232 tevent_req_set_callback(subreq, wbc_connect_connected, result);
234 if (!tevent_req_set_endtime(subreq, ev, timeval_current_ofs(30, 0))) {
235 goto nomem;
238 return result;
240 post_status:
241 tevent_req_error(result, wbc_err);
242 return tevent_req_post(result, ev);
243 nomem:
244 TALLOC_FREE(result);
245 return NULL;
248 static void wbc_connect_connected(struct tevent_req *subreq)
250 struct tevent_req *req = tevent_req_callback_data(
251 subreq, struct tevent_req);
252 int res, err;
254 res = async_connect_recv(subreq, &err);
255 TALLOC_FREE(subreq);
256 if (res == -1) {
257 tevent_req_error(req, map_wbc_err_from_errno(err));
258 return;
260 tevent_req_done(req);
263 static wbcErr wb_connect_recv(struct tevent_req *req)
265 return tevent_req_simple_recv_wbcerr(req);
268 struct wb_int_trans_state {
269 struct tevent_context *ev;
270 int fd;
271 struct winbindd_request *wb_req;
272 struct winbindd_response *wb_resp;
275 static void wb_int_trans_write_done(struct tevent_req *subreq);
276 static void wb_int_trans_read_done(struct tevent_req *subreq);
278 static struct tevent_req *wb_int_trans_send(TALLOC_CTX *mem_ctx,
279 struct tevent_context *ev,
280 struct tevent_queue *queue, int fd,
281 struct winbindd_request *wb_req)
283 struct tevent_req *result, *subreq;
284 struct wb_int_trans_state *state;
286 result = tevent_req_create(mem_ctx, &state,
287 struct wb_int_trans_state);
288 if (result == NULL) {
289 return NULL;
292 if (winbind_closed_fd(fd)) {
293 tevent_req_error(result, WBC_ERR_WINBIND_NOT_AVAILABLE);
294 return tevent_req_post(result, ev);
297 state->ev = ev;
298 state->fd = fd;
299 state->wb_req = wb_req;
300 state->wb_req->length = sizeof(struct winbindd_request);
301 state->wb_req->pid = getpid();
303 subreq = wb_req_write_send(state, state->ev, queue, state->fd,
304 state->wb_req);
305 if (subreq == NULL) {
306 goto fail;
308 tevent_req_set_callback(subreq, wb_int_trans_write_done, result);
310 return result;
312 fail:
313 TALLOC_FREE(result);
314 return NULL;
317 static void wb_int_trans_write_done(struct tevent_req *subreq)
319 struct tevent_req *req = tevent_req_callback_data(
320 subreq, struct tevent_req);
321 struct wb_int_trans_state *state = tevent_req_data(
322 req, struct wb_int_trans_state);
323 wbcErr wbc_err;
325 wbc_err = wb_req_write_recv(subreq);
326 TALLOC_FREE(subreq);
327 if (!WBC_ERROR_IS_OK(wbc_err)) {
328 tevent_req_error(req, wbc_err);
329 return;
332 subreq = wb_resp_read_send(state, state->ev, state->fd);
333 if (tevent_req_nomem(subreq, req)) {
334 return;
336 tevent_req_set_callback(subreq, wb_int_trans_read_done, req);
339 static void wb_int_trans_read_done(struct tevent_req *subreq)
341 struct tevent_req *req = tevent_req_callback_data(
342 subreq, struct tevent_req);
343 struct wb_int_trans_state *state = tevent_req_data(
344 req, struct wb_int_trans_state);
345 wbcErr wbc_err;
347 wbc_err = wb_resp_read_recv(subreq, state, &state->wb_resp);
348 TALLOC_FREE(subreq);
349 if (!WBC_ERROR_IS_OK(wbc_err)) {
350 tevent_req_error(req, wbc_err);
351 return;
354 tevent_req_done(req);
357 static wbcErr wb_int_trans_recv(struct tevent_req *req,
358 TALLOC_CTX *mem_ctx,
359 struct winbindd_response **presponse)
361 struct wb_int_trans_state *state = tevent_req_data(
362 req, struct wb_int_trans_state);
363 wbcErr wbc_err;
365 if (tevent_req_is_wbcerr(req, &wbc_err)) {
366 return wbc_err;
369 *presponse = talloc_move(mem_ctx, &state->wb_resp);
370 return WBC_ERR_SUCCESS;
373 static const char *winbindd_socket_dir(void)
375 #ifdef SOCKET_WRAPPER
376 const char *env_dir;
378 env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
379 if (env_dir) {
380 return env_dir;
382 #endif
384 return WINBINDD_SOCKET_DIR;
387 struct wb_open_pipe_state {
388 struct wb_context *wb_ctx;
389 struct tevent_context *ev;
390 bool need_priv;
391 struct winbindd_request wb_req;
394 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq);
395 static void wb_open_pipe_ping_done(struct tevent_req *subreq);
396 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq);
397 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq);
399 static struct tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
400 struct tevent_context *ev,
401 struct wb_context *wb_ctx,
402 bool need_priv)
404 struct tevent_req *result, *subreq;
405 struct wb_open_pipe_state *state;
407 result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
408 if (result == NULL) {
409 return NULL;
411 state->wb_ctx = wb_ctx;
412 state->ev = ev;
413 state->need_priv = need_priv;
415 if (wb_ctx->fd != -1) {
416 close(wb_ctx->fd);
417 wb_ctx->fd = -1;
420 subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
421 if (subreq == NULL) {
422 goto fail;
424 tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
425 result);
426 return result;
428 fail:
429 TALLOC_FREE(result);
430 return NULL;
433 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
435 struct tevent_req *req = tevent_req_callback_data(
436 subreq, struct tevent_req);
437 struct wb_open_pipe_state *state = tevent_req_data(
438 req, struct wb_open_pipe_state);
439 wbcErr wbc_err;
441 wbc_err = wb_connect_recv(subreq);
442 TALLOC_FREE(subreq);
443 if (!WBC_ERROR_IS_OK(wbc_err)) {
444 state->wb_ctx->is_priv = true;
445 tevent_req_error(req, wbc_err);
446 return;
449 ZERO_STRUCT(state->wb_req);
450 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
452 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
453 &state->wb_req);
454 if (tevent_req_nomem(subreq, req)) {
455 return;
457 tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
460 static void wb_open_pipe_ping_done(struct tevent_req *subreq)
462 struct tevent_req *req = tevent_req_callback_data(
463 subreq, struct tevent_req);
464 struct wb_open_pipe_state *state = tevent_req_data(
465 req, struct wb_open_pipe_state);
466 struct winbindd_response *wb_resp;
467 wbcErr wbc_err;
469 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
470 TALLOC_FREE(subreq);
471 if (!WBC_ERROR_IS_OK(wbc_err)) {
472 tevent_req_error(req, wbc_err);
473 return;
476 if (!state->need_priv) {
477 tevent_req_done(req);
478 return;
481 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
483 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
484 &state->wb_req);
485 if (tevent_req_nomem(subreq, req)) {
486 return;
488 tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
491 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
493 struct tevent_req *req = tevent_req_callback_data(
494 subreq, struct tevent_req);
495 struct wb_open_pipe_state *state = tevent_req_data(
496 req, struct wb_open_pipe_state);
497 struct winbindd_response *wb_resp = NULL;
498 wbcErr wbc_err;
500 wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
501 TALLOC_FREE(subreq);
502 if (!WBC_ERROR_IS_OK(wbc_err)) {
503 tevent_req_error(req, wbc_err);
504 return;
507 close(state->wb_ctx->fd);
508 state->wb_ctx->fd = -1;
510 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
511 (char *)wb_resp->extra_data.data);
512 TALLOC_FREE(wb_resp);
513 if (tevent_req_nomem(subreq, req)) {
514 return;
516 tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
519 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
521 struct tevent_req *req = tevent_req_callback_data(
522 subreq, struct tevent_req);
523 struct wb_open_pipe_state *state = tevent_req_data(
524 req, struct wb_open_pipe_state);
525 wbcErr wbc_err;
527 wbc_err = wb_connect_recv(subreq);
528 TALLOC_FREE(subreq);
529 if (!WBC_ERROR_IS_OK(wbc_err)) {
530 tevent_req_error(req, wbc_err);
531 return;
533 state->wb_ctx->is_priv = true;
534 tevent_req_done(req);
537 static wbcErr wb_open_pipe_recv(struct tevent_req *req)
539 return tevent_req_simple_recv_wbcerr(req);
542 struct wb_trans_state {
543 struct wb_trans_state *prev, *next;
544 struct wb_context *wb_ctx;
545 struct tevent_context *ev;
546 struct winbindd_request *wb_req;
547 struct winbindd_response *wb_resp;
548 int num_retries;
549 bool need_priv;
552 static void wb_trans_connect_done(struct tevent_req *subreq);
553 static void wb_trans_done(struct tevent_req *subreq);
554 static void wb_trans_retry_wait_done(struct tevent_req *subreq);
556 struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
557 struct tevent_context *ev,
558 struct wb_context *wb_ctx, bool need_priv,
559 struct winbindd_request *wb_req)
561 struct tevent_req *req, *subreq;
562 struct wb_trans_state *state;
564 req = tevent_req_create(mem_ctx, &state, struct wb_trans_state);
565 if (req == NULL) {
566 return NULL;
568 state->wb_ctx = wb_ctx;
569 state->ev = ev;
570 state->wb_req = wb_req;
571 state->num_retries = 10;
572 state->need_priv = need_priv;
574 if ((wb_ctx->fd == -1) || (need_priv && !wb_ctx->is_priv)) {
575 subreq = wb_open_pipe_send(state, ev, wb_ctx, need_priv);
576 if (subreq == NULL) {
577 goto fail;
579 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
580 return req;
583 subreq = wb_int_trans_send(state, ev, wb_ctx->queue, wb_ctx->fd,
584 wb_req);
585 if (subreq == NULL) {
586 goto fail;
588 tevent_req_set_callback(subreq, wb_trans_done, req);
589 return req;
590 fail:
591 TALLOC_FREE(req);
592 return NULL;
595 static bool wb_trans_retry(struct tevent_req *req,
596 struct wb_trans_state *state,
597 wbcErr wbc_err)
599 struct tevent_req *subreq;
601 if (WBC_ERROR_IS_OK(wbc_err)) {
602 return false;
605 if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
607 * Winbind not around or we can't connect to the pipe. Fail
608 * immediately.
610 tevent_req_error(req, wbc_err);
611 return true;
614 state->num_retries -= 1;
615 if (state->num_retries == 0) {
616 tevent_req_error(req, wbc_err);
617 return true;
621 * The transfer as such failed, retry after one second
624 if (state->wb_ctx->fd != -1) {
625 close(state->wb_ctx->fd);
626 state->wb_ctx->fd = -1;
629 subreq = tevent_wakeup_send(state, state->ev,
630 timeval_current_ofs(1, 0));
631 if (tevent_req_nomem(subreq, req)) {
632 return true;
634 tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req);
635 return true;
638 static void wb_trans_retry_wait_done(struct tevent_req *subreq)
640 struct tevent_req *req = tevent_req_callback_data(
641 subreq, struct tevent_req);
642 struct wb_trans_state *state = tevent_req_data(
643 req, struct wb_trans_state);
644 bool ret;
646 ret = tevent_wakeup_recv(subreq);
647 TALLOC_FREE(subreq);
648 if (!ret) {
649 tevent_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
650 return;
653 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
654 state->need_priv);
655 if (tevent_req_nomem(subreq, req)) {
656 return;
658 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
661 static void wb_trans_connect_done(struct tevent_req *subreq)
663 struct tevent_req *req = tevent_req_callback_data(
664 subreq, struct tevent_req);
665 struct wb_trans_state *state = tevent_req_data(
666 req, struct wb_trans_state);
667 wbcErr wbc_err;
669 wbc_err = wb_open_pipe_recv(subreq);
670 TALLOC_FREE(subreq);
672 if (wb_trans_retry(req, state, wbc_err)) {
673 return;
676 subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
677 state->wb_req);
678 if (tevent_req_nomem(subreq, req)) {
679 return;
681 tevent_req_set_callback(subreq, wb_trans_done, req);
684 static void wb_trans_done(struct tevent_req *subreq)
686 struct tevent_req *req = tevent_req_callback_data(
687 subreq, struct tevent_req);
688 struct wb_trans_state *state = tevent_req_data(
689 req, struct wb_trans_state);
690 wbcErr wbc_err;
692 wbc_err = wb_int_trans_recv(subreq, state, &state->wb_resp);
693 TALLOC_FREE(subreq);
695 if (wb_trans_retry(req, state, wbc_err)) {
696 return;
699 tevent_req_done(req);
702 wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
703 struct winbindd_response **presponse)
705 struct wb_trans_state *state = tevent_req_data(
706 req, struct wb_trans_state);
707 wbcErr wbc_err;
709 if (tevent_req_is_wbcerr(req, &wbc_err)) {
710 return wbc_err;
713 *presponse = talloc_move(mem_ctx, &state->wb_resp);
714 return WBC_ERR_SUCCESS;