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 wbcErr map_wbc_err_from_errno(int error)
28 return WBC_ERR_AUTH_ERROR;
30 return WBC_ERR_NO_MEMORY;
33 return WBC_ERR_UNKNOWN_FAILURE;
37 bool tevent_req_is_wbcerr(struct tevent_req *req, wbcErr *pwbc_err)
39 enum tevent_req_state state;
41 if (!tevent_req_is_error(req, &state, &error)) {
42 *pwbc_err = WBC_ERR_SUCCESS;
47 case TEVENT_REQ_USER_ERROR:
50 case TEVENT_REQ_TIMED_OUT:
51 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
53 case TEVENT_REQ_NO_MEMORY:
54 *pwbc_err = WBC_ERR_NO_MEMORY;
57 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
63 wbcErr tevent_req_simple_recv_wbcerr(struct tevent_req *req)
67 if (tevent_req_is_wbcerr(req, &wbc_err)) {
71 return WBC_ERR_SUCCESS;
75 struct tevent_queue *queue;
80 static int make_nonstd_fd(int fd)
98 for (i=0; i<num_fds; i++) {
107 /****************************************************************************
108 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
112 Set close on exec also.
113 ****************************************************************************/
115 static int make_safe_fd(int fd)
118 int new_fd = make_nonstd_fd(fd);
124 /* Socket should be nonblocking. */
127 #define FLAG_TO_SET O_NONBLOCK
130 #define FLAG_TO_SET O_NDELAY
132 #define FLAG_TO_SET FNDELAY
136 if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
140 flags |= FLAG_TO_SET;
141 if (fcntl(new_fd, F_SETFL, flags) == -1) {
147 /* Socket should be closed on exec() */
149 result = flags = fcntl(new_fd, F_GETFD, 0);
152 result = fcntl( new_fd, F_SETFD, flags );
162 int sys_errno = errno;
169 struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx)
171 struct wb_context *result;
173 result = talloc(mem_ctx, struct wb_context);
174 if (result == NULL) {
177 result->queue = tevent_queue_create(result, "wb_trans");
178 if (result->queue == NULL) {
186 struct wb_connect_state {
190 static void wbc_connect_connected(struct tevent_req *subreq);
192 static struct tevent_req *wb_connect_send(TALLOC_CTX *mem_ctx,
193 struct tevent_context *ev,
194 struct wb_context *wb_ctx,
197 struct tevent_req *result, *subreq;
198 struct wb_connect_state *state;
199 struct sockaddr_un sunaddr;
204 result = tevent_req_create(mem_ctx, &state, struct wb_connect_state);
205 if (result == NULL) {
209 if (wb_ctx->fd != -1) {
214 /* Check permissions on unix socket directory */
216 if (lstat(dir, &st) == -1) {
217 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
221 if (!S_ISDIR(st.st_mode) ||
222 (st.st_uid != 0 && st.st_uid != geteuid())) {
223 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
227 /* Connect to socket */
229 path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
230 WINBINDD_SOCKET_NAME);
235 sunaddr.sun_family = AF_UNIX;
236 strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
239 /* If socket file doesn't exist, don't bother trying to connect
240 with retry. This is an attempt to make the system usable when
241 the winbindd daemon is not running. */
243 if ((lstat(sunaddr.sun_path, &st) == -1)
244 || !S_ISSOCK(st.st_mode)
245 || (st.st_uid != 0 && st.st_uid != geteuid())) {
246 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
250 wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
251 if (wb_ctx->fd == -1) {
252 wbc_err = map_wbc_err_from_errno(errno);
256 subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
257 (struct sockaddr *)(void *)&sunaddr,
259 if (subreq == NULL) {
262 tevent_req_set_callback(subreq, wbc_connect_connected, result);
264 if (!tevent_req_set_endtime(subreq, ev, timeval_current_ofs(30, 0))) {
271 tevent_req_error(result, wbc_err);
272 return tevent_req_post(result, ev);
278 static void wbc_connect_connected(struct tevent_req *subreq)
280 struct tevent_req *req = tevent_req_callback_data(
281 subreq, struct tevent_req);
284 res = async_connect_recv(subreq, &err);
287 tevent_req_error(req, map_wbc_err_from_errno(err));
290 tevent_req_done(req);
293 static wbcErr wb_connect_recv(struct tevent_req *req)
295 return tevent_req_simple_recv_wbcerr(req);
298 static const char *winbindd_socket_dir(void)
300 #ifdef SOCKET_WRAPPER
303 env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
309 return WINBINDD_SOCKET_DIR;
312 struct wb_open_pipe_state {
313 struct wb_context *wb_ctx;
314 struct tevent_context *ev;
316 struct winbindd_request wb_req;
319 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq);
320 static void wb_open_pipe_ping_done(struct tevent_req *subreq);
321 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq);
322 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq);
324 static struct tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
325 struct tevent_context *ev,
326 struct wb_context *wb_ctx,
329 struct tevent_req *result, *subreq;
330 struct wb_open_pipe_state *state;
332 result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
333 if (result == NULL) {
336 state->wb_ctx = wb_ctx;
338 state->need_priv = need_priv;
340 if (wb_ctx->fd != -1) {
345 subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
346 if (subreq == NULL) {
349 tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
358 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
360 struct tevent_req *req = tevent_req_callback_data(
361 subreq, struct tevent_req);
362 struct wb_open_pipe_state *state = tevent_req_data(
363 req, struct wb_open_pipe_state);
366 wbc_err = wb_connect_recv(subreq);
368 if (!WBC_ERROR_IS_OK(wbc_err)) {
369 state->wb_ctx->is_priv = true;
370 tevent_req_error(req, wbc_err);
374 ZERO_STRUCT(state->wb_req);
375 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
376 state->wb_req.pid = getpid();
378 subreq = wb_simple_trans_send(state, state->ev, state->wb_ctx->queue,
379 state->wb_ctx->fd, &state->wb_req);
380 if (tevent_req_nomem(subreq, req)) {
383 tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
386 static void wb_open_pipe_ping_done(struct tevent_req *subreq)
388 struct tevent_req *req = tevent_req_callback_data(
389 subreq, struct tevent_req);
390 struct wb_open_pipe_state *state = tevent_req_data(
391 req, struct wb_open_pipe_state);
392 struct winbindd_response *wb_resp;
395 ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
398 tevent_req_error(req, map_wbc_err_from_errno(err));
402 if (!state->need_priv) {
403 tevent_req_done(req);
407 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
408 state->wb_req.pid = getpid();
410 subreq = wb_simple_trans_send(state, state->ev, state->wb_ctx->queue,
411 state->wb_ctx->fd, &state->wb_req);
412 if (tevent_req_nomem(subreq, req)) {
415 tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
418 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
420 struct tevent_req *req = tevent_req_callback_data(
421 subreq, struct tevent_req);
422 struct wb_open_pipe_state *state = tevent_req_data(
423 req, struct wb_open_pipe_state);
424 struct winbindd_response *wb_resp = NULL;
427 ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
430 tevent_req_error(req, map_wbc_err_from_errno(err));
434 close(state->wb_ctx->fd);
435 state->wb_ctx->fd = -1;
437 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
438 (char *)wb_resp->extra_data.data);
439 TALLOC_FREE(wb_resp);
440 if (tevent_req_nomem(subreq, req)) {
443 tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
446 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
448 struct tevent_req *req = tevent_req_callback_data(
449 subreq, struct tevent_req);
450 struct wb_open_pipe_state *state = tevent_req_data(
451 req, struct wb_open_pipe_state);
454 wbc_err = wb_connect_recv(subreq);
456 if (!WBC_ERROR_IS_OK(wbc_err)) {
457 tevent_req_error(req, wbc_err);
460 state->wb_ctx->is_priv = true;
461 tevent_req_done(req);
464 static wbcErr wb_open_pipe_recv(struct tevent_req *req)
466 return tevent_req_simple_recv_wbcerr(req);
469 struct wb_trans_state {
470 struct wb_trans_state *prev, *next;
471 struct wb_context *wb_ctx;
472 struct tevent_context *ev;
473 struct winbindd_request *wb_req;
474 struct winbindd_response *wb_resp;
479 static void wb_trans_connect_done(struct tevent_req *subreq);
480 static void wb_trans_done(struct tevent_req *subreq);
481 static void wb_trans_retry_wait_done(struct tevent_req *subreq);
483 struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
484 struct tevent_context *ev,
485 struct wb_context *wb_ctx, bool need_priv,
486 struct winbindd_request *wb_req)
488 struct tevent_req *req, *subreq;
489 struct wb_trans_state *state;
491 req = tevent_req_create(mem_ctx, &state, struct wb_trans_state);
495 state->wb_ctx = wb_ctx;
497 state->wb_req = wb_req;
498 state->num_retries = 10;
499 state->need_priv = need_priv;
501 if ((wb_ctx->fd == -1) || (need_priv && !wb_ctx->is_priv)) {
502 subreq = wb_open_pipe_send(state, ev, wb_ctx, need_priv);
503 if (subreq == NULL) {
506 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
510 state->wb_req->pid = getpid();
512 subreq = wb_simple_trans_send(state, ev, wb_ctx->queue, wb_ctx->fd,
514 if (subreq == NULL) {
517 tevent_req_set_callback(subreq, wb_trans_done, req);
524 static bool wb_trans_retry(struct tevent_req *req,
525 struct wb_trans_state *state,
528 struct tevent_req *subreq;
530 if (WBC_ERROR_IS_OK(wbc_err)) {
534 if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
536 * Winbind not around or we can't connect to the pipe. Fail
539 tevent_req_error(req, wbc_err);
543 state->num_retries -= 1;
544 if (state->num_retries == 0) {
545 tevent_req_error(req, wbc_err);
550 * The transfer as such failed, retry after one second
553 if (state->wb_ctx->fd != -1) {
554 close(state->wb_ctx->fd);
555 state->wb_ctx->fd = -1;
558 subreq = tevent_wakeup_send(state, state->ev,
559 timeval_current_ofs(1, 0));
560 if (tevent_req_nomem(subreq, req)) {
563 tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req);
567 static void wb_trans_retry_wait_done(struct tevent_req *subreq)
569 struct tevent_req *req = tevent_req_callback_data(
570 subreq, struct tevent_req);
571 struct wb_trans_state *state = tevent_req_data(
572 req, struct wb_trans_state);
575 ret = tevent_wakeup_recv(subreq);
578 tevent_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
582 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
584 if (tevent_req_nomem(subreq, req)) {
587 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
590 static void wb_trans_connect_done(struct tevent_req *subreq)
592 struct tevent_req *req = tevent_req_callback_data(
593 subreq, struct tevent_req);
594 struct wb_trans_state *state = tevent_req_data(
595 req, struct wb_trans_state);
598 wbc_err = wb_open_pipe_recv(subreq);
601 if (wb_trans_retry(req, state, wbc_err)) {
605 subreq = wb_simple_trans_send(state, state->ev, state->wb_ctx->queue,
606 state->wb_ctx->fd, state->wb_req);
607 if (tevent_req_nomem(subreq, req)) {
610 tevent_req_set_callback(subreq, wb_trans_done, req);
613 static void wb_trans_done(struct tevent_req *subreq)
615 struct tevent_req *req = tevent_req_callback_data(
616 subreq, struct tevent_req);
617 struct wb_trans_state *state = tevent_req_data(
618 req, struct wb_trans_state);
621 ret = wb_simple_trans_recv(subreq, state, &state->wb_resp, &err);
624 if (wb_trans_retry(req, state, map_wbc_err_from_errno(err))) {
628 tevent_req_done(req);
631 wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
632 struct winbindd_response **presponse)
634 struct wb_trans_state *state = tevent_req_data(
635 req, struct wb_trans_state);
638 if (tevent_req_is_wbcerr(req, &wbc_err)) {
642 *presponse = talloc_move(mem_ctx, &state->wb_resp);
643 return WBC_ERR_SUCCESS;