2 Unix SMB/CIFS implementation.
3 Infrastructure for async winbind requests
4 Copyright (C) Volker Lendecke 2008
6 ** NOTE! The following LGPL license applies to the wbclient
7 ** library. This does NOT imply that all of Samba is released
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 3 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Library General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "system/filesys.h"
26 #include "system/network.h"
29 #include "lib/async_req/async_sock.h"
30 #include "nsswitch/winbind_struct_protocol.h"
31 #include "nsswitch/libwbclient/wbclient.h"
32 #include "wbc_async.h"
33 #include "lib/util/blocking.h"
35 wbcErr map_wbc_err_from_errno(int error)
40 return WBC_ERR_AUTH_ERROR;
42 return WBC_ERR_NO_MEMORY;
45 return WBC_ERR_UNKNOWN_FAILURE;
49 bool tevent_req_is_wbcerr(struct tevent_req *req, wbcErr *pwbc_err)
51 enum tevent_req_state state;
53 if (!tevent_req_is_error(req, &state, &error)) {
54 *pwbc_err = WBC_ERR_SUCCESS;
59 case TEVENT_REQ_USER_ERROR:
62 case TEVENT_REQ_TIMED_OUT:
63 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
65 case TEVENT_REQ_NO_MEMORY:
66 *pwbc_err = WBC_ERR_NO_MEMORY;
69 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
75 wbcErr tevent_req_simple_recv_wbcerr(struct tevent_req *req)
79 if (tevent_req_is_wbcerr(req, &wbc_err)) {
83 return WBC_ERR_SUCCESS;
86 struct wbc_debug_ops {
87 void (*debug)(void *context, enum wbcDebugLevel level,
88 const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
93 struct tevent_queue *queue;
97 struct wbc_debug_ops debug_ops;
100 static int make_nonstd_fd(int fd)
118 for (i=0; i<num_fds; i++) {
127 /****************************************************************************
128 Set a fd into blocking/nonblocking mode.
129 Set close on exec also.
130 ****************************************************************************/
132 static int make_safe_fd(int fd)
135 int new_fd = make_nonstd_fd(fd);
141 result = set_blocking(new_fd, false);
146 /* Socket should be closed on exec() */
148 result = flags = fcntl(new_fd, F_GETFD, 0);
151 result = fcntl( new_fd, F_SETFD, flags );
161 int sys_errno = errno;
168 /* Just put a prototype to avoid moving the whole function around */
169 static const char *winbindd_socket_dir(void);
171 struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx, const char* dir)
173 struct wb_context *result;
175 result = talloc_zero(mem_ctx, struct wb_context);
176 if (result == NULL) {
179 result->queue = tevent_queue_create(result, "wb_trans");
180 if (result->queue == NULL) {
185 result->is_priv = false;
188 result->dir = talloc_strdup(result, dir);
190 result->dir = winbindd_socket_dir();
192 if (result->dir == NULL) {
199 struct wb_connect_state {
203 static void wbc_connect_connected(struct tevent_req *subreq);
205 static struct tevent_req *wb_connect_send(TALLOC_CTX *mem_ctx,
206 struct tevent_context *ev,
207 struct wb_context *wb_ctx,
210 struct tevent_req *result, *subreq;
211 struct wb_connect_state *state;
212 struct sockaddr_un sunaddr;
217 result = tevent_req_create(mem_ctx, &state, struct wb_connect_state);
218 if (result == NULL) {
222 if (wb_ctx->fd != -1) {
227 /* Check permissions on unix socket directory */
229 if (lstat(dir, &st) == -1) {
230 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
234 if (!S_ISDIR(st.st_mode) ||
235 (st.st_uid != 0 && st.st_uid != geteuid())) {
236 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
240 /* Connect to socket */
242 path = talloc_asprintf(mem_ctx, "%s/%s", dir,
243 WINBINDD_SOCKET_NAME);
248 sunaddr.sun_family = AF_UNIX;
249 strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
252 /* If socket file doesn't exist, don't bother trying to connect
253 with retry. This is an attempt to make the system usable when
254 the winbindd daemon is not running. */
256 if ((lstat(sunaddr.sun_path, &st) == -1)
257 || !S_ISSOCK(st.st_mode)
258 || (st.st_uid != 0 && st.st_uid != geteuid())) {
259 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
263 wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
264 if (wb_ctx->fd == -1) {
265 wbc_err = map_wbc_err_from_errno(errno);
269 subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
270 (struct sockaddr *)(void *)&sunaddr,
271 sizeof(sunaddr), NULL, NULL, NULL);
272 if (subreq == NULL) {
275 tevent_req_set_callback(subreq, wbc_connect_connected, result);
279 tevent_req_error(result, wbc_err);
280 return tevent_req_post(result, ev);
286 static void wbc_connect_connected(struct tevent_req *subreq)
288 struct tevent_req *req = tevent_req_callback_data(
289 subreq, struct tevent_req);
292 res = async_connect_recv(subreq, &err);
295 tevent_req_error(req, map_wbc_err_from_errno(err));
298 tevent_req_done(req);
301 static wbcErr wb_connect_recv(struct tevent_req *req)
303 return tevent_req_simple_recv_wbcerr(req);
306 static const char *winbindd_socket_dir(void)
308 if (nss_wrapper_enabled()) {
311 env_dir = getenv("SELFTEST_WINBINDD_SOCKET_DIR");
312 if (env_dir != NULL) {
317 return WINBINDD_SOCKET_DIR;
320 struct wb_open_pipe_state {
321 struct wb_context *wb_ctx;
322 struct tevent_context *ev;
324 struct winbindd_request wb_req;
327 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq);
328 static void wb_open_pipe_ping_done(struct tevent_req *subreq);
329 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq);
330 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq);
332 static struct tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
333 struct tevent_context *ev,
334 struct wb_context *wb_ctx,
337 struct tevent_req *result, *subreq;
338 struct wb_open_pipe_state *state;
340 result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
341 if (result == NULL) {
344 state->wb_ctx = wb_ctx;
346 state->need_priv = need_priv;
348 if (wb_ctx->fd != -1) {
353 subreq = wb_connect_send(state, ev, wb_ctx, wb_ctx->dir);
354 if (subreq == NULL) {
357 tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
366 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
368 struct tevent_req *req = tevent_req_callback_data(
369 subreq, struct tevent_req);
370 struct wb_open_pipe_state *state = tevent_req_data(
371 req, struct wb_open_pipe_state);
374 wbc_err = wb_connect_recv(subreq);
376 if (!WBC_ERROR_IS_OK(wbc_err)) {
377 state->wb_ctx->is_priv = true;
378 tevent_req_error(req, wbc_err);
382 ZERO_STRUCT(state->wb_req);
383 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
384 state->wb_req.pid = getpid();
385 (void)snprintf(state->wb_req.client_name,
386 sizeof(state->wb_req.client_name),
390 subreq = wb_simple_trans_send(state, state->ev, NULL,
391 state->wb_ctx->fd, &state->wb_req);
392 if (tevent_req_nomem(subreq, req)) {
395 tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
398 static void wb_open_pipe_ping_done(struct tevent_req *subreq)
400 struct tevent_req *req = tevent_req_callback_data(
401 subreq, struct tevent_req);
402 struct wb_open_pipe_state *state = tevent_req_data(
403 req, struct wb_open_pipe_state);
404 struct winbindd_response *wb_resp;
407 ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
410 tevent_req_error(req, map_wbc_err_from_errno(err));
414 if (!state->need_priv) {
415 tevent_req_done(req);
419 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
420 state->wb_req.pid = getpid();
422 subreq = wb_simple_trans_send(state, state->ev, NULL,
423 state->wb_ctx->fd, &state->wb_req);
424 if (tevent_req_nomem(subreq, req)) {
427 tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
430 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
432 struct tevent_req *req = tevent_req_callback_data(
433 subreq, struct tevent_req);
434 struct wb_open_pipe_state *state = tevent_req_data(
435 req, struct wb_open_pipe_state);
436 struct winbindd_response *wb_resp = NULL;
439 ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
442 tevent_req_error(req, map_wbc_err_from_errno(err));
446 close(state->wb_ctx->fd);
447 state->wb_ctx->fd = -1;
449 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
450 (char *)wb_resp->extra_data.data);
451 TALLOC_FREE(wb_resp);
452 if (tevent_req_nomem(subreq, req)) {
455 tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
458 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
460 struct tevent_req *req = tevent_req_callback_data(
461 subreq, struct tevent_req);
462 struct wb_open_pipe_state *state = tevent_req_data(
463 req, struct wb_open_pipe_state);
466 wbc_err = wb_connect_recv(subreq);
468 if (!WBC_ERROR_IS_OK(wbc_err)) {
469 tevent_req_error(req, wbc_err);
472 state->wb_ctx->is_priv = true;
473 tevent_req_done(req);
476 static wbcErr wb_open_pipe_recv(struct tevent_req *req)
478 return tevent_req_simple_recv_wbcerr(req);
481 struct wb_trans_state {
482 struct wb_trans_state *prev, *next;
483 struct wb_context *wb_ctx;
484 struct tevent_context *ev;
485 struct winbindd_request *wb_req;
486 struct winbindd_response *wb_resp;
490 static bool closed_fd(int fd)
504 selret = select(fd+1, &r_fds, NULL, NULL, &tv);
511 return (FD_ISSET(fd, &r_fds));
514 static void wb_trans_trigger(struct tevent_req *req, void *private_data);
515 static void wb_trans_connect_done(struct tevent_req *subreq);
516 static void wb_trans_done(struct tevent_req *subreq);
517 static void wb_trans_retry_wait_done(struct tevent_req *subreq);
519 struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
520 struct tevent_context *ev,
521 struct wb_context *wb_ctx, bool need_priv,
522 struct winbindd_request *wb_req)
524 struct tevent_req *req;
525 struct wb_trans_state *state;
527 req = tevent_req_create(mem_ctx, &state, struct wb_trans_state);
531 state->wb_ctx = wb_ctx;
533 state->wb_req = wb_req;
534 state->need_priv = need_priv;
536 if (!tevent_queue_add(wb_ctx->queue, ev, req, wb_trans_trigger,
539 return tevent_req_post(req, ev);
544 static void wb_trans_trigger(struct tevent_req *req, void *private_data)
546 struct wb_trans_state *state = tevent_req_data(
547 req, struct wb_trans_state);
548 struct tevent_req *subreq;
550 if ((state->wb_ctx->fd != -1) && closed_fd(state->wb_ctx->fd)) {
551 close(state->wb_ctx->fd);
552 state->wb_ctx->fd = -1;
555 if ((state->wb_ctx->fd == -1)
556 || (state->need_priv && !state->wb_ctx->is_priv)) {
557 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
559 if (tevent_req_nomem(subreq, req)) {
562 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
566 state->wb_req->pid = getpid();
568 subreq = wb_simple_trans_send(state, state->ev, NULL,
569 state->wb_ctx->fd, state->wb_req);
570 if (tevent_req_nomem(subreq, req)) {
573 tevent_req_set_callback(subreq, wb_trans_done, req);
576 static bool wb_trans_retry(struct tevent_req *req,
577 struct wb_trans_state *state,
580 struct tevent_req *subreq;
582 if (WBC_ERROR_IS_OK(wbc_err)) {
586 if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
588 * Winbind not around or we can't connect to the pipe. Fail
591 tevent_req_error(req, wbc_err);
596 * The transfer as such failed, retry after one second
599 if (state->wb_ctx->fd != -1) {
600 close(state->wb_ctx->fd);
601 state->wb_ctx->fd = -1;
604 subreq = tevent_wakeup_send(state, state->ev,
605 tevent_timeval_current_ofs(1, 0));
606 if (tevent_req_nomem(subreq, req)) {
609 tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req);
613 static void wb_trans_retry_wait_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 = tevent_wakeup_recv(subreq);
624 tevent_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
628 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
630 if (tevent_req_nomem(subreq, req)) {
633 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
636 static void wb_trans_connect_done(struct tevent_req *subreq)
638 struct tevent_req *req = tevent_req_callback_data(
639 subreq, struct tevent_req);
640 struct wb_trans_state *state = tevent_req_data(
641 req, struct wb_trans_state);
644 wbc_err = wb_open_pipe_recv(subreq);
647 if (wb_trans_retry(req, state, wbc_err)) {
651 subreq = wb_simple_trans_send(state, state->ev, NULL,
652 state->wb_ctx->fd, state->wb_req);
653 if (tevent_req_nomem(subreq, req)) {
656 tevent_req_set_callback(subreq, wb_trans_done, req);
659 static void wb_trans_done(struct tevent_req *subreq)
661 struct tevent_req *req = tevent_req_callback_data(
662 subreq, struct tevent_req);
663 struct wb_trans_state *state = tevent_req_data(
664 req, struct wb_trans_state);
667 ret = wb_simple_trans_recv(subreq, state, &state->wb_resp, &err);
670 && wb_trans_retry(req, state, map_wbc_err_from_errno(err))) {
674 tevent_req_done(req);
677 wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
678 struct winbindd_response **presponse)
680 struct wb_trans_state *state = tevent_req_data(
681 req, struct wb_trans_state);
684 if (tevent_req_is_wbcerr(req, &wbc_err)) {
688 *presponse = talloc_move(mem_ctx, &state->wb_resp);
689 return WBC_ERR_SUCCESS;
692 /********************************************************************
693 * Debug wrapper functions, modeled (with lot's of code copied as is)
694 * after the tevent debug wrapper functions
695 ********************************************************************/
698 this allows the user to choose their own debug function
700 int wbcSetDebug(struct wb_context *wb_ctx,
701 void (*debug)(void *context,
702 enum wbcDebugLevel level,
704 va_list ap) PRINTF_ATTRIBUTE(3,0),
707 wb_ctx->debug_ops.debug = debug;
708 wb_ctx->debug_ops.context = context;
713 debug function for wbcSetDebugStderr
715 static void wbcDebugStderr(void *private_data,
716 enum wbcDebugLevel level,
718 va_list ap) PRINTF_ATTRIBUTE(3,0);
719 static void wbcDebugStderr(void *private_data,
720 enum wbcDebugLevel level,
721 const char *fmt, va_list ap)
723 if (level <= WBC_DEBUG_WARNING) {
724 vfprintf(stderr, fmt, ap);
729 convenience function to setup debug messages on stderr
730 messages of level WBC_DEBUG_WARNING and higher are printed
732 int wbcSetDebugStderr(struct wb_context *wb_ctx)
734 return wbcSetDebug(wb_ctx, wbcDebugStderr, wb_ctx);
740 * The default debug action is to ignore debugging messages.
741 * This is the most appropriate action for a library.
742 * Applications using the library must decide where to
743 * redirect debugging messages
745 void wbcDebug(struct wb_context *wb_ctx, enum wbcDebugLevel level,
746 const char *fmt, ...)
752 if (wb_ctx->debug_ops.debug == NULL) {
756 wb_ctx->debug_ops.debug(wb_ctx->debug_ops.context, level, fmt, ap);